Compare commits

...

113 Commits

Author SHA1 Message Date
Patrick Goldinger
dfec1f3804 Release v0.3.11-beta04 2021-05-04 20:37:39 +02:00
Patrick Goldinger
1fffe7f6e5 Fix ؤ Arabic Letter Waw with Hamza Above not written correctly (#438) 2021-05-04 19:48:54 +02:00
Patrick Goldinger
862a6cc82a Fix font size multiplier and also scale drawables (#540) 2021-05-04 18:56:21 +02:00
Patrick Goldinger
068caaf09b Add schwa symbol (ə) in Italian extended popups (#693) 2021-05-04 18:04:20 +02:00
Patrick Goldinger
93fb6d6016 Fix English (US) store description being cut off in F-Droid (#709) 2021-05-04 17:55:25 +02:00
Patrick Goldinger
28f0657bd7 Improve and fix KeyboardIconSet (#778) 2021-05-04 17:46:06 +02:00
Patrick Goldinger
8c53c2a057 Fix Hungarian layout not containing special keys 2021-05-04 17:34:32 +02:00
Patrick Goldinger
6251fb2ef6 Fix bottom row keys not shifted in Dvorak layout (#805) 2021-05-04 17:07:50 +02:00
Patrick Goldinger
cba2b873b8 Add devtool overlay for heap memory usage 2021-05-04 16:55:14 +02:00
Patrick Goldinger
d7ee61f316 Merge pull request #718 from X-yl/emoji-key-mem
Recycle EmojiKeyViews to reduce memory usage
2021-05-04 15:17:00 +02:00
X-yl
cf309f43a4 Recycle EmojiKeyViews for 15%-20% memory savings
Instead of creating an EmojiKeyView for every emoji, you can use a
RecyclerView to only create the ones which are visible on screen, and
then reuse them later.
2021-05-04 09:48:31 +04:00
Patrick Goldinger
93acee778e Release v0.3.11-beta03 2021-05-03 20:52:30 +02:00
Patrick Goldinger
c7f2f31c99 Fix gestures (except space+shift) 2021-05-03 20:11:08 +02:00
Patrick Goldinger
ebb8837d8a Fix Double NaN crashes (#774, #790) 2021-05-03 15:47:55 +02:00
Patrick Goldinger
f04f185034 Fix adaptive theme memory management (#763) 2021-05-02 12:17:35 +02:00
Patrick Goldinger
20de007d3b Add "Copied to system clipboard" toast to crash dialog (#724) 2021-05-01 12:20:56 +02:00
Patrick Goldinger
df01f6fe57 Fix theme manager buttons not wrapping (#777) 2021-05-01 11:51:15 +02:00
Patrick Goldinger
f9e6d7b09c Fix keyboard preview visual bugs (#776) 2021-05-01 11:51:15 +02:00
Patrick Goldinger
f8000d999f Release v0.3.11-beta02 2021-04-30 20:37:58 +02:00
Patrick Goldinger
0392d1a7f1 Update translations from Crowdin 2021-04-30 19:59:45 +02:00
another-sapiens
0be1f4beb9 Spanish Readme (#750)
* Create .keep

* Spanish Readme

* Delete .keep

* Create .keep

* Spanish Readme (for Beta)

* Delete .keep
2021-04-30 19:06:13 +02:00
Patrick Goldinger
87bba56a80 Merge pull request #769 from olLenz/improve-ime-activation
Improve ime activation
2021-04-30 18:59:30 +02:00
Patrick Goldinger
e5154533ae Merge pull request #734 from florisboard/layout-engine-rework
Layout engine rework
2021-04-30 18:51:16 +02:00
Patrick Goldinger
fd00c2fa4c Move abstract definitions to own namespace and document them 2021-04-30 18:36:34 +02:00
Patrick Goldinger
3de07c30c6 Re-implement key hints 2021-04-30 16:07:29 +02:00
Patrick Goldinger
0a84c78740 Fix number row setting not correctly syncing (#730) 2021-04-30 02:44:30 +02:00
Patrick Goldinger
7f3f7a015b Fix minor issues 2021-04-30 02:15:49 +02:00
Patrick Goldinger
2899bd9234 Add support for multi-codepoint characters 2021-04-29 20:21:30 +02:00
Patrick Goldinger
90162b2eb5 Basic fix for emojis 2021-04-29 02:19:45 +02:00
Patrick Goldinger
a028abda65 Re-implement basic gestures 2021-04-29 01:47:34 +02:00
Patrick Goldinger
b7ed99ab7f Fix basic popup touch logic for TextKeyboardView 2021-04-28 18:49:29 +02:00
Patrick Goldinger
19bb15d006 Remove accidental URL paste of badge in README 2021-04-28 17:47:38 +02:00
Patrick Goldinger
4a005c6a3a Add Matrix chat badge in README (#587) 2021-04-28 17:41:11 +02:00
Patrick Goldinger
be6f7bcfc8 Merge pull request #764 from X-yl/master
Fix crash when copying from AOSP calculator
2021-04-28 05:03:45 +02:00
Patrick Goldinger
84b827d652 Fix currency slot popup bug 2021-04-28 04:34:17 +02:00
Patrick Goldinger
dd12de7b88 Adapt all layout files for new syntax 2021-04-28 04:27:53 +02:00
Patrick Goldinger
8641892b46 Re-implement functional extended popups for 'en' 2021-04-28 03:08:39 +02:00
Patrick Goldinger
ff109714f9 Document TextKeyboardIconSet and TextKeyboardCache 2021-04-28 02:23:39 +02:00
Patrick Goldinger
589f709fa2 Improve and fix TextKeyboardCache 2021-04-28 01:55:05 +02:00
Patrick Goldinger
920b2363d4 Re-add basic support for extended popups 2021-04-28 01:09:08 +02:00
Patrick Goldinger
2aa245ca1a Further improvements in performance 2021-04-27 02:14:04 +02:00
Patrick Goldinger
8305b9ac27 Fix some performance / memory issues 2021-04-26 20:52:04 +02:00
x-yl
b774a66b0c Fix crash when copying from AOSP calculator 2021-04-26 21:33:18 +04:00
Oliver Lenz
4d35245fe8 Check for a change of the input method via a broadcast receiver instead of using unreliable timeouts 2021-04-25 23:19:12 +02:00
Oliver Lenz
3f24913762 Move functions for checking if ime is enabled and selected into their own util class to reduce the size of the florisboard class 2021-04-25 23:16:53 +02:00
Patrick Goldinger
a0fd62ad45 Adapt more layouts to new syntax / Add locale label to space 2021-04-25 21:04:11 +02:00
Patrick Goldinger
efd5e62703 Improve evaluation process / Add enabled/visible states 2021-04-25 19:38:45 +02:00
Patrick Goldinger
c481a59e24 Fix layouting issues with positioning and text size 2021-04-24 20:15:42 +02:00
Patrick Goldinger
9f5eba3275 Re-add basic popups / Fix multiple popup bug (#513, #515) 2021-04-23 20:19:17 +02:00
Patrick Goldinger
3f0d3131aa Re-implement row margin 2021-04-22 19:27:02 +02:00
Patrick Goldinger
9d360f80c3 Auto-adjust visible height for enter and space when borderless (#676) 2021-04-22 19:08:04 +02:00
Patrick Goldinger
1d1a070b2b Adapt character modifier layouts to new layout syntax 2021-04-22 15:09:44 +02:00
Patrick Goldinger
ebe0e43641 Adapt other classes to new layout and asset logic 2021-04-22 15:04:14 +02:00
Patrick Goldinger
b99142d009 Fix media context issues 2021-04-22 15:03:09 +02:00
Patrick Goldinger
8077c440be Update SubtypeManager to new asset manager 2021-04-22 15:02:49 +02:00
Patrick Goldinger
64024101ba Introduce new layout logic 2021-04-22 15:01:37 +02:00
Patrick Goldinger
37a9445a64 Switch from Moshi to kotlinx.serialization 2021-04-22 14:59:38 +02:00
Patrick Goldinger
33630d6893 Improve debug/crash utility 2021-04-22 12:14:22 +02:00
Patrick Goldinger
242c21ce59 Merge pull request #728 from Glitchy-Tozier/patch-2
Improve crash_report.md
2021-04-21 19:40:14 +02:00
Patrick Goldinger
859d6dcde3 Merge pull request #727 from Glitchy-Tozier/patch-1
Improve bug_report.md
2021-04-21 19:39:20 +02:00
Glitchy-Tozier
9df9b8d752 Update crash_report.md 2021-04-21 16:02:14 +02:00
Glitchy-Tozier
a64d61c9b5 Improve Bug-Template
Make everything use the same number of placeholder-periods (3).
2021-04-21 15:58:42 +02:00
Patrick Goldinger
128faba755 Fix debug summarize tools not utilizing StringBuilder 2021-04-19 03:11:51 +02:00
Patrick Goldinger
95c23dc344 Improve core and clipboard code / Clean up code 2021-04-19 03:11:51 +02:00
Patrick Goldinger
c863d87724 Fix NPE in FileStorage.cloneURI (#699) 2021-04-19 03:11:51 +02:00
Patrick Goldinger
56878d06ca Incorporate feedback to new bug/crash report templates 2021-04-18 15:05:01 +02:00
Patrick Goldinger
c21b1806b6 Rename "Bug report (generated)" to "Crash report" 2021-04-18 15:05:01 +02:00
Patrick Goldinger
34d9495e66 Adapt bug report templates on GitHub to new format 2021-04-18 15:05:01 +02:00
Patrick Goldinger
32e822fa10 Adapt some core files to new Flog utility 2021-04-18 15:05:01 +02:00
Patrick Goldinger
5f9184f09d Improve CrashUtility class 2021-04-18 15:05:01 +02:00
Patrick Goldinger
4b4d0895ae Add Flog debug utility class 2021-04-18 15:05:01 +02:00
Patrick Goldinger
4c97a86de8 Merge pull request #669 from florisboard/flashing-fix
Possible fix for flashing keyboard
2021-04-15 19:43:09 +02:00
Patrick Goldinger
6f23d6544c Possible fix for flashing keyboard (#589) 2021-04-15 05:12:15 +02:00
Patrick Goldinger
304e1a0d46 Improve bug_report.md by making the instructions more clear 2021-04-15 04:18:03 +02:00
Patrick Goldinger
1e4b9ad86a Merge pull request #633 from mikelloc/add-catalan-language-support
Adding Catalan language support
2021-04-14 00:18:39 +02:00
Patrick Goldinger
1ba0f7c7c0 Fix merge conflicts for catalan layout 2021-04-13 23:54:27 +02:00
Patrick Goldinger
0eb35c5edc Merge pull request #655 from X-yl/translate-clipboard
Make clipboard strings translatable
2021-04-13 23:40:43 +02:00
X-yl
a6970eba81 Make clipboard strings translatable 2021-04-14 00:21:27 +04:00
Patrick Goldinger
fac7dde5f3 Release v0.3.11-beta01 2021-04-13 15:48:40 +02:00
Patrick Goldinger
e9ef11f2fb Update translations from Crowdin 2021-04-13 15:38:59 +02:00
Patrick Goldinger
068768fc05 Improve haptic feedback experience (#138, #324, #610) 2021-04-13 15:30:36 +02:00
Patrick Goldinger
a00e9e1a68 Fix TextInputManager glide post effect status (#249) 2021-04-13 00:48:46 +02:00
Patrick Goldinger
22a3b07fe5 Merge pull request #634 from majso/patch-2
Update of SK layout
2021-04-13 00:18:00 +02:00
Patrick Goldinger
27959b7d29 Fix Persian half-space for URI (#249) 2021-04-13 00:09:02 +02:00
majso
b8e49674bb Update of SK layout
- adding ä character
2021-04-12 20:46:14 +02:00
MiKi
5a1d277dc6 Adding Catalan language support in two layouts QWERTY one and with dedicated accent keys. Also suport middle dot l·l and ç 2021-04-12 20:26:47 +02:00
Patrick Goldinger
2534601116 Improve French popups (#615) 2021-04-12 20:10:43 +02:00
Patrick Goldinger
5426a0e5fb Add Undo, Redo and Cycle keyboard mode gestures (#215) 2021-04-12 19:55:21 +02:00
Patrick Goldinger
41e91410e7 Fix space bar up gesture (#627, #545) 2021-04-12 19:01:10 +02:00
Patrick Goldinger
3be3be8e9d Merge pull request #625 from X-yl/gesture-typing
Some gesture typing enhancements
2021-04-12 18:24:06 +02:00
Patrick Goldinger
1e7deebd23 Merge pull request #600 from Glitchy-Tozier/patch-1
Make version more generic so that it doesn't seem out of date.
2021-04-12 18:14:08 +02:00
Patrick Goldinger
a3aef0de31 Merge pull request #622 from stefan-misik/central-european-layouts
Add Central European layouts (SK and CZ (CS))
2021-04-12 18:10:06 +02:00
Patrick Goldinger
8933a03c3b Merge pull request #585 from 33kk/master
Move hard sign to extended popup in Russian layout, add Ukrainian layout
2021-04-12 18:03:29 +02:00
x-yl
a8df9892c2 Change github workflows back 2021-04-12 18:15:40 +04:00
x-yl
6414f94bd0 Space for numbers also 2021-04-12 17:41:01 +04:00
x-yl
7fb4c9ba00 Exceptions to space rules for some symbol 2021-04-12 17:34:42 +04:00
x-yl
a7fe421661 Cache word data
Temp fix while dictionaries aren't working yet.
2021-04-12 17:32:34 +04:00
x-yl
4cecbc4d5e make glide trail length customizable 2021-04-12 17:32:34 +04:00
x-yl
829fcbf468 Don't initialize gesture detector in theme preview 2021-04-12 17:32:34 +04:00
x-yl
00d0f778d2 Set correct time for glide typing suggestions 2021-04-12 17:32:34 +04:00
X-yl
22c35b7e99 Use historical X/Y as well 2021-04-12 17:32:34 +04:00
x-yl
87fa236d1e Return only one gesture if there are no loops 2021-04-12 17:32:33 +04:00
x-yl
158f2d33ae make distance threshold dynamic 2021-04-12 17:32:33 +04:00
Štefan Mišík
e55be5ca03 Add Central European layouts (SK and CZ (CS))
Add Slovak and Czech layouts.
2021-04-12 11:37:49 +02:00
Patrick Goldinger
b3413535f7 Fix popup position for rtl mode (#536) 2021-04-11 12:43:09 +02:00
Patrick Goldinger
21cabe1d92 Display clipboard row in numeric/phone input fields (#603) 2021-04-11 12:16:24 +02:00
Glitchy-Tozier
c6a30b1905 Update bug_report.md 2021-04-10 19:05:13 +02:00
Patrick Goldinger
9fa702fde7 Merge pull request #609 from florisboard/adaptive-theme-glide-trail
Improve adaptive theme for capslock and glide trail
2021-04-10 17:47:58 +02:00
Patrick Goldinger
9bd2b80949 Improve adaptive theme for capslock and glide trail (#591) 2021-04-10 17:43:09 +02:00
Patrick Goldinger
0e91003a68 Merge pull request #608 from florisboard/fix-glide-logic
Fix glide logic in TextInputManager
2021-04-10 17:23:39 +02:00
Patrick Goldinger
e42f69cc2e Fix glide logic in TextInputManager (#581, #595, #604) 2021-04-10 16:28:15 +02:00
Glitchy-Tozier
f6045a5c4f Make version more generic so that it doesn't seem out of date.
I'm torn between a big `x` and a small one.
2021-04-09 17:34:00 +02:00
Marko
d3240c9db5 Move hard sign to extended popup in Russian layout, add Ukrainian 2021-04-07 16:22:24 +03:00
217 changed files with 8815 additions and 5872 deletions

View File

@@ -8,30 +8,27 @@ assignees: ''
---
<!--
- Describe the bug in a short but concise way.
- If you have a screenshot or screen recording of the bug, link them at
the end of this issue.
- Please search existing bug reports to avoid creating duplicates.
- Thank you for your help in making FlorisBoard better!
Thank you for your help in making FlorisBoard better!
Guide to a good bug-report:
Please search existing bug/crash reports reports to avoid creating duplicates.
• Give your bug report a good name (no generics like "Error" or "Crash"), so others can easily identify the topic of your issue.
• Describe the bug in a short but concise way.
• If you have a screenshot or screen recording of the bug, link them at the end of this issue.
• Also make sure to fill out the environment information. This info is valuable when trying to fix your described bug.
-->
#### Short description
Describe the bug in a short but concise way.
#### Steps to reproduce
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
1. Go to ''
2. Click on ''
3. Scroll down to ''
4. See error
#### Environment information
- FlorisBoard Version: <!-- e.g. 0.3.6 -->
- FlorisBoard Version: <!-- e.g. 0.X.X -->
- Install Source: <!-- Google PlayStore/F-Droid/GitHub/? -->
- Device: <!-- e.g. OnePlus 7T -->
- Android version, ROM: <!-- e.g. 10, Stock -->
<!-- (remove this line if you paste a log)
```
If applicable, paste the captured debug log here.
```
(remove this line if you paste a log) -->
- Android: <!-- e.g. 10, Stock -->

28
.github/ISSUE_TEMPLATE/crash_report.md vendored Normal file
View File

@@ -0,0 +1,28 @@
---
name: Crash report
about: Create a report with a generated crash log attached to help FlorisBoard improve
title: ''
labels: bug
assignees: ''
---
<!--
Thank you for your help in making FlorisBoard better!
Guide to a good crash-report:
• Please search existing bug/crash reports to avoid creating duplicates.
• Give your crash report a good name (no generics like "Error" or "Crash"), so others can easily identify the topic of your issue.
• Describe what you were doing what could've led to the crash and whether the crash is random or reproducible.
-->
#### Short description
Describe what you were doing that could've led to the crash.
#### Steps to reproduce
1. Go to '…'
2. Click on '…'
3. Scroll down to '…'
4. See crash
<!-- Paste the generated crash log below -->

View File

@@ -1,7 +1,7 @@
<img align="left" width="80" height="80"
src="fastlane/metadata/android/en-US/images/icon.png" alt="App icon">
# FlorisBoard [![Crowdin](https://badges.crowdin.net/florisboard/localized.svg)](https://crowdin.florisboard.patrickgold.dev) ![FlorisBoard CI](https://github.com/florisboard/florisboard/workflows/FlorisBoard%20CI/badge.svg?event=push)
# FlorisBoard [![Crowdin](https://badges.crowdin.net/florisboard/localized.svg)](https://crowdin.florisboard.patrickgold.dev) [![Matrix badge](https://img.shields.io/badge/chat-%23florisboard%3amatrix.org-blue)](https://matrix.to/#/#florisboard:matrix.org) ![FlorisBoard CI](https://github.com/florisboard/florisboard/workflows/FlorisBoard%20CI/badge.svg?event=push)
**FlorisBoard** is a free and open-source keyboard for Android 6.0+
devices. It aims at being modern, user-friendly and customizable while

View File

@@ -1,8 +1,9 @@
plugins {
id("com.android.application") version "4.1.2"
kotlin("android") version "1.4.30"
kotlin("kapt") version "1.4.30"
id("com.android.application") version "4.1.3"
kotlin("android") version "1.5.0-RC"
kotlin("kapt") version "1.5.0-RC"
kotlin("plugin.serialization") version "1.5.0-RC"
}
android {
@@ -16,15 +17,15 @@ android {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
freeCompilerArgs = listOf("-Xallow-result-return-type") // enables use of kotlin.Result
freeCompilerArgs = listOf("-Xallow-result-return-type", "-Xopt-in=kotlin.RequiresOptIn")
}
defaultConfig {
applicationId = "dev.patrickgold.florisboard"
minSdkVersion(23)
targetSdkVersion(30)
versionCode(35)
versionName("0.3.10")
versionCode(39)
versionName("0.3.11")
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -46,7 +47,7 @@ android {
create("beta") // Needed because by default the "beta" BuildType does not exist
named("beta").configure {
applicationIdSuffix = ".beta"
versionNameSuffix = "-beta06"
versionNameSuffix = "-beta04"
proguardFiles.add(getDefaultProguardFile("proguard-android-optimize.txt"))
resValue("mipmap", "floris_app_icon", "@mipmap/ic_app_icon_beta")
@@ -84,10 +85,9 @@ dependencies {
implementation("androidx.constraintlayout", "constraintlayout", "2.0.4")
implementation("androidx.lifecycle", "lifecycle-service", "2.2.0")
implementation("com.google.android", "flexbox", "2.0.1") // requires jcenter as of version 2.0.1
implementation("com.squareup.moshi", "moshi-kotlin", "1.11.0")
implementation("com.squareup.moshi", "moshi-adapters", "1.11.0")
implementation("com.google.android.material", "material", "1.3.0")
implementation("org.jetbrains.kotlinx", "kotlinx-coroutines-android", "1.4.2")
implementation("org.jetbrains.kotlinx", "kotlinx-serialization-json", "1.1.0")
implementation("com.jaredrummler", "colorpicker", "1.1.0")
implementation("com.jakewharton.timber", "timber", "4.7.1")
implementation("com.nambimobile.widgets", "expandable-fab", "1.0.2")

View File

@@ -489,6 +489,14 @@
"characters": "jcuken_russian"
}
},
{
"id": 1351,
"languageTag": "uk",
"currencySet": "ukrainian_hryvnia",
"preferred": {
"characters": "jcuken_ukrainian"
}
},
{
"id": 1401,
"languageTag": "el",
@@ -572,6 +580,14 @@
"characters": "kurdish_kurmanci"
}
},
{
"id": 2501,
"languageTag": "ca",
"currencySet": "euro",
"preferred": {
"characters": "catalan"
}
},
{
"id": 2601,
"languageTag": "IPA-IPA",
@@ -581,6 +597,22 @@
"symbols": "ipa",
"symbols2": "ipa"
}
},
{
"id": 2701,
"languageTag": "sk",
"currencySet": "euro",
"preferred": {
"characters": "qwertz"
}
},
{
"id": 2801,
"languageTag": "cs",
"currencySet": "euro",
"preferred": {
"characters": "qwertz"
}
}
]
}

View File

@@ -35,7 +35,7 @@
[
{ "code": 1584, "label": "ذ" },
{ "code": 1569, "label": "ء" },
{ "code": 65157, "label": "" },
{ "code": 1572, "label": "ؤ" },
{ "code": 1585, "label": "ر" },
{ "code": 1609, "label": "ى" },
{ "code": 1577, "label": "ة" },

View File

@@ -6,36 +6,36 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 97, "label": "a" },
{ "code": 122, "label": "z" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 121, "label": "y" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" }
],
[
{ "code": 113, "label": "q" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" },
{ "code": 109, "label": "m" }
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 109, "label": "m" }
],
[
{ "code": 119, "label": "w" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "code": 39, "label": "'", "popup": {
"relevant": [
{ "code": 8218, "label": "" },

View File

@@ -6,38 +6,38 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 98, "label": "b" },
{ "code": 233, "label": "é" },
{ "code": 112, "label": "p" },
{ "code": 111, "label": "o" },
{ "code": 232, "label": "è" },
{ "code": 118, "label": "v" },
{ "code": 100, "label": "d" },
{ "code": 108, "label": "l" },
{ "code": 106, "label": "j" },
{ "code": 122, "label": "z" },
{ "code": 119, "label": "w" }
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 232, "label": "è" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 119, "label": "w" }
],
[
{ "code": 97, "label": "a" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 101, "label": "e" },
{ "code": 99, "label": "c" },
{ "code": 116, "label": "t" },
{ "code": 115, "label": "s" },
{ "code": 114, "label": "r" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" },
{ "code": 231, "label": "ç" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" },
{ "$": "auto_text_key", "code": 231, "label": "ç" }
],
[
{ "code": 234, "label": "ê" },
{ "code": 224, "label": "à" },
{ "code": 121, "label": "y" },
{ "code": 120, "label": "x" },
{ "code": 107, "label": "k" },
{ "code": 113, "label": "q", "popup": {
{ "$": "auto_text_key", "code": 234, "label": "ê" },
{ "$": "auto_text_key", "code": 224, "label": "à" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 113, "label": "q", "popup": {
"relevant": [
{ "code": 8218, "label": "" },
{ "code": 8216, "label": "" },
@@ -46,9 +46,9 @@
{ "code": 8250, "label": "" }
]
} },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 102, "label": "f" }
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 102, "label": "f" }
]
]
}

View File

@@ -6,42 +6,42 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 1099, "label": "ы" },
{ "code": 1091, "label": "у" },
{ "code": 1077, "label": "е" },
{ "code": 1080, "label": "и" },
{ "code": 1096, "label": "ш" },
{ "code": 1097, "label": "щ" },
{ "code": 1082, "label": "к" },
{ "code": 1089, "label": "с" },
{ "code": 1076, "label": "д" },
{ "code": 1079, "label": "з" },
{ "code": 1094, "label": "ц" }
{ "$": "auto_text_key", "code": 1099, "label": "ы" },
{ "$": "auto_text_key", "code": 1091, "label": "у" },
{ "$": "auto_text_key", "code": 1077, "label": "е" },
{ "$": "auto_text_key", "code": 1080, "label": "и" },
{ "$": "auto_text_key", "code": 1096, "label": "ш" },
{ "$": "auto_text_key", "code": 1097, "label": "щ" },
{ "$": "auto_text_key", "code": 1082, "label": "к" },
{ "$": "auto_text_key", "code": 1089, "label": "с" },
{ "$": "auto_text_key", "code": 1076, "label": "д" },
{ "$": "auto_text_key", "code": 1079, "label": "з" },
{ "$": "auto_text_key", "code": 1094, "label": "ц" }
],
[
{ "code": 1100, "label": "ь" },
{ "code": 1103, "label": "я" },
{ "code": 1072, "label": "а" },
{ "code": 1086, "label": "о" },
{ "code": 1078, "label": "ж" },
{ "code": 1075, "label": "г" },
{ "code": 1090, "label": "т" },
{ "code": 1085, "label": "н" },
{ "code": 1074, "label": "в" },
{ "code": 1084, "label": "м" },
{ "code": 1095, "label": "ч" }
{ "$": "auto_text_key", "code": 1100, "label": "ь" },
{ "$": "auto_text_key", "code": 1103, "label": "я" },
{ "$": "auto_text_key", "code": 1072, "label": "а" },
{ "$": "auto_text_key", "code": 1086, "label": "о" },
{ "$": "auto_text_key", "code": 1078, "label": "ж" },
{ "$": "auto_text_key", "code": 1075, "label": "г" },
{ "$": "auto_text_key", "code": 1090, "label": "т" },
{ "$": "auto_text_key", "code": 1085, "label": "н" },
{ "$": "auto_text_key", "code": 1074, "label": "в" },
{ "$": "auto_text_key", "code": 1084, "label": "м" },
{ "$": "auto_text_key", "code": 1095, "label": "ч" }
],
[
{ "code": 1102, "label": "ю" },
{ "code": 1081, "label": "й" },
{ "code": 1098, "label": "ъ" },
{ "code": 1101, "label": "э" },
{ "code": 1092, "label": "ф" },
{ "code": 1093, "label": "х" },
{ "code": 1087, "label": "п" },
{ "code": 1088, "label": "р" },
{ "code": 1083, "label": "л" },
{ "code": 1073, "label": "б" }
{ "$": "auto_text_key", "code": 1102, "label": "ю" },
{ "$": "auto_text_key", "code": 1081, "label": "й" },
{ "$": "auto_text_key", "code": 1098, "label": "ъ" },
{ "$": "auto_text_key", "code": 1101, "label": "э" },
{ "$": "auto_text_key", "code": 1092, "label": "ф" },
{ "$": "auto_text_key", "code": 1093, "label": "х" },
{ "$": "auto_text_key", "code": 1087, "label": "п" },
{ "$": "auto_text_key", "code": 1088, "label": "р" },
{ "$": "auto_text_key", "code": 1083, "label": "л" },
{ "$": "auto_text_key", "code": 1073, "label": "б" }
]
]
}

View File

@@ -6,40 +6,40 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 1103, "label": "я" },
{ "code": 1074, "label": "в" },
{ "code": 1077, "label": "е" },
{ "code": 1088, "label": "р" },
{ "code": 1090, "label": "т" },
{ "code": 1098, "label": "ъ" },
{ "code": 1091, "label": "у" },
{ "code": 1080, "label": "и" },
{ "code": 1086, "label": "о" },
{ "code": 1087, "label": "п" },
{ "code": 1095, "label": "ч" }
{ "$": "auto_text_key", "code": 1103, "label": "я" },
{ "$": "auto_text_key", "code": 1074, "label": "в" },
{ "$": "auto_text_key", "code": 1077, "label": "е" },
{ "$": "auto_text_key", "code": 1088, "label": "р" },
{ "$": "auto_text_key", "code": 1090, "label": "т" },
{ "$": "auto_text_key", "code": 1098, "label": "ъ" },
{ "$": "auto_text_key", "code": 1091, "label": "у" },
{ "$": "auto_text_key", "code": 1080, "label": "и" },
{ "$": "auto_text_key", "code": 1086, "label": "о" },
{ "$": "auto_text_key", "code": 1087, "label": "п" },
{ "$": "auto_text_key", "code": 1095, "label": "ч" }
],
[
{ "code": 1072, "label": "а" },
{ "code": 1089, "label": "с" },
{ "code": 1076, "label": "д" },
{ "code": 1092, "label": "ф" },
{ "code": 1075, "label": "г" },
{ "code": 1093, "label": "х" },
{ "code": 1081, "label": "й" },
{ "code": 1082, "label": "к" },
{ "code": 1083, "label": "л" },
{ "code": 1096, "label": "ш" },
{ "code": 1097, "label": "щ" }
{ "$": "auto_text_key", "code": 1072, "label": "а" },
{ "$": "auto_text_key", "code": 1089, "label": "с" },
{ "$": "auto_text_key", "code": 1076, "label": "д" },
{ "$": "auto_text_key", "code": 1092, "label": "ф" },
{ "$": "auto_text_key", "code": 1075, "label": "г" },
{ "$": "auto_text_key", "code": 1093, "label": "х" },
{ "$": "auto_text_key", "code": 1081, "label": "й" },
{ "$": "auto_text_key", "code": 1082, "label": "к" },
{ "$": "auto_text_key", "code": 1083, "label": "л" },
{ "$": "auto_text_key", "code": 1096, "label": "ш" },
{ "$": "auto_text_key", "code": 1097, "label": "щ" }
],
[
{ "code": 1079, "label": "з" },
{ "code": 1100, "label": "ь" },
{ "code": 1094, "label": "ц" },
{ "code": 1078, "label": "ж" },
{ "code": 1073, "label": "б" },
{ "code": 1085, "label": "н" },
{ "code": 1084, "label": "м" },
{ "code": 1102, "label": "ю" }
{ "$": "auto_text_key", "code": 1079, "label": "з" },
{ "$": "auto_text_key", "code": 1100, "label": "ь" },
{ "$": "auto_text_key", "code": 1094, "label": "ц" },
{ "$": "auto_text_key", "code": 1078, "label": "ж" },
{ "$": "auto_text_key", "code": 1073, "label": "б" },
{ "$": "auto_text_key", "code": 1085, "label": "н" },
{ "$": "auto_text_key", "code": 1084, "label": "м" },
{ "$": "auto_text_key", "code": 1102, "label": "ю" }
]
]
}

View File

@@ -6,39 +6,39 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 121, "label": "y" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" },
{ "code": 232, "label": "è" }
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 232, "label": "è" }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" },
{ "code": 233, "label": "é" },
{ "code": 224, "label": "à" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 224, "label": "à" }
],
[
{ "code": 122, "label": "z" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" }
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" }
]
]
}

View File

@@ -0,0 +1,42 @@
{
"type": "characters",
"name": "catalan",
"label": "Catalan (QWERTY)",
"authors": [ "mikelloc" ],
"direction": "ltr",
"arrangement": [
[
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" }
],
[
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 231, "label": "ç" }
],
[
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" }
]
]
}

View File

@@ -0,0 +1,44 @@
{
"type": "characters",
"name": "catalan_accents",
"label": "Catalan (Accents)",
"authors": [ "mikelloc" ],
"direction": "ltr",
"arrangement": [
[
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 768, "label": "◌̀" }
],
[
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 231, "label": "ç" },
{ "$": "auto_text_key", "code": 769, "label": "◌́" }
],
[
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" }
]
]
}

View File

@@ -6,41 +6,48 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 102, "label": "f" },
{ "code": 112, "label": "p" },
{ "code": 103, "label": "g" },
{ "code": 106, "label": "j" },
{ "code": 108, "label": "l" },
{ "code": 117, "label": "u" },
{ "code": 121, "label": "y" },
{ "code": 59, "label": ";", "popup": {
"relevant": [
{ "code": 58, "label": ":" }
]
}, "shift": { "code": 58, "label": ":" } }
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "case_selector",
"lower": { "code": 59, "label": ";", "popup": {
"relevant": [
{ "code": 58, "label": ":" }
]
} },
"upper": { "code": 58, "label": ":", "popup": {
"relevant": [
{ "code": 59, "label": ";" }
]
} }
}
],
[
{ "code": 97, "label": "a" },
{ "code": 114, "label": "r" },
{ "code": 115, "label": "s" },
{ "code": 116, "label": "t" },
{ "code": 100, "label": "d" },
{ "code": 104, "label": "h" },
{ "code": 110, "label": "n" },
{ "code": 101, "label": "e" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" }
],
[
{ "code": 122, "label": "z" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 107, "label": "k" },
{ "code": 109, "label": "m" }
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 109, "label": "m" }
]
]
}

View File

@@ -6,39 +6,39 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 121, "label": "y" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" },
{ "code": 229, "label": "å" }
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 229, "label": "å" }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" },
{ "code": 230, "label": "æ" },
{ "code": 248, "label": "ø" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 248, "label": "ø" }
],
[
{ "code": 122, "label": "z" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" }
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" }
]
]
}

View File

@@ -7,59 +7,78 @@
"modifier": "dvorak",
"arrangement": [
[
{ "code": 64, "label": "@", "groupId": 101, "variation": "email_address" },
{ "code": 39, "label": "'", "groupId": 101, "variation": "normal", "popup": {
"relevant": [
{ "code": 33, "label": "!" },
{ "code": 34, "label": "\"" }
]
}, "shift": { "code": 34, "label": "\"" } },
{ "code": 39, "label": "'", "groupId": 101, "variation": "password", "popup": {
"relevant": [
{ "code": 33, "label": "!" },
{ "code": 34, "label": "\"" }
]
}, "shift": { "code": 34, "label": "\"" } },
{ "code": 47, "label": "/", "groupId": 101, "variation": "uri" },
{ "code": 44, "label": ",", "popup": {
"relevant": [
{ "code": 60, "label": "<" },
{ "code": 63, "label": "?" }
]
}, "shift": { "code": 60, "label": "<" } },
{ "code": 46, "label": ".", "popup": {
"relevant": [
{ "code": 62, "label": ">" }
]
}, "shift": { "code": 62, "label": ">" } },
{ "code": 112, "label": "p" },
{ "code": 121, "label": "y" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 99, "label": "c" },
{ "code": 114, "label": "r" },
{ "code": 108, "label": "l" }
{ "$": "variation_selector",
"default": { "$": "case_selector",
"lower": { "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": {
"relevant": [
{ "code": 44, "label": "," },
{ "code": 63, "label": "?" }
]
} }
},
{ "$": "case_selector",
"lower": { "code": 46, "label": ".", "popup": {
"relevant": [
{ "code": 62, "label": ">" }
]
} },
"upper": { "code": 62, "label": ">", "popup": {
"relevant": [
{ "code": 46, "label": "." }
]
} }
},
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 108, "label": "l" }
],
[
{ "code": 97, "label": "a" },
{ "code": 111, "label": "o" },
{ "code": 101, "label": "e" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 100, "label": "d" },
{ "code": 104, "label": "h" },
{ "code": 116, "label": "t" },
{ "code": 110, "label": "n" },
{ "code": 115, "label": "s" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 115, "label": "s" }
],
[
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 120, "label": "x" },
{ "code": 98, "label": "b" },
{ "code": 109, "label": "m" },
{ "code": 119, "label": "w" },
{ "code": 118, "label": "v" }
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 109, "label": "m" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 118, "label": "v" }
]
]
}

View File

@@ -6,45 +6,45 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 349, "label": "ŝ", "popup": {
"main": { "code": 113, "label": "q" }
{ "$": "auto_text_key", "code": 349, "label": "ŝ", "popup": {
"main": { "$": "auto_text_key", "code": 113, "label": "q" }
} },
{ "code": 285, "label": "ĝ", "popup": {
"main": { "code": 119, "label": "w" }
{ "$": "auto_text_key", "code": 285, "label": "ĝ", "popup": {
"main": { "$": "auto_text_key", "code": 119, "label": "w" }
} },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 365, "label": "ŭ", "popup": {
"main": { "code": 121, "label": "y" }
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 365, "label": "ŭ", "popup": {
"main": { "$": "auto_text_key", "code": 121, "label": "y" }
} },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" }
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" },
{ "code": 309, "label": "ĵ" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 309, "label": "ĵ" }
],
[
{ "code": 122, "label": "z" },
{ "code": 265, "label": "ĉ", "popup": {
"main": { "code": 120, "label": "x" }
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 265, "label": "ĉ", "popup": {
"main": { "$": "auto_text_key", "code": 120, "label": "x" }
} },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" }
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" }
]
]
}

View File

@@ -6,46 +6,46 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 349, "label": "ŝ", "popup": {
"main": { "code": 113, "label": "q" }
{ "$": "auto_text_key", "code": 349, "label": "ŝ", "popup": {
"main": { "$": "auto_text_key", "code": 113, "label": "q" }
} },
{ "code": 285, "label": "ĝ", "popup": {
"main": { "code": 119, "label": "w" }
{ "$": "auto_text_key", "code": 285, "label": "ĝ", "popup": {
"main": { "$": "auto_text_key", "code": 119, "label": "w" }
} },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 365, "label": "ŭ", "popup": {
"main": { "code": 121, "label": "y" }
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 365, "label": "ŭ", "popup": {
"main": { "$": "auto_text_key", "code": 121, "label": "y" }
} },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" }
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" },
{ "code": 309, "label": "ĵ" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 309, "label": "ĵ" }
],
[
{ "code": 122, "label": "z" },
{ "code": 265, "label": "ĉ", "popup": {
"main": { "code": 120, "label": "x" }
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 265, "label": "ĉ", "popup": {
"main": { "$": "auto_text_key", "code": 120, "label": "x" }
} },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" },
{ "code": 293, "label": "ĥ" }
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" },
{ "$": "auto_text_key", "code": 293, "label": "ĥ" }
]
]
}

View File

@@ -6,7 +6,7 @@
"all": {
"и": {
"relevant": [
{ "code": 1117, "label": "ѝ" }
{ "$": "auto_text_key", "code": 1117, "label": "ѝ" }
]
},
"~right": {

View File

@@ -0,0 +1,115 @@
{
"type": "characters/extended_popups",
"name": "ca",
"authors": [ "mikelloc" ],
"mapping": {
"all": {
"a": {
"main": { "$": "auto_text_key", "code": 224, "label": "à" },
"relevant": [
{ "$": "auto_text_key", "code": 229, "label": "å" },
{ "$": "auto_text_key", "code": 261, "label": "ą" },
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 257, "label": "ā" },
{ "$": "auto_text_key", "code": 170, "label": "ª" },
{ "$": "auto_text_key", "code": 225, "label": "á" },
{ "$": "auto_text_key", "code": 228, "label": "ä" },
{ "$": "auto_text_key", "code": 226, "label": "â" },
{ "$": "auto_text_key", "code": 227, "label": "ã" }
]
},
"c": {
"main": { "$": "auto_text_key", "code": 231, "label": "ç" },
"relevant": [
{ "$": "auto_text_key", "code": 269, "label": "č" },
{ "$": "auto_text_key", "code": 263, "label": "ć" }
]
},
"e": {
"main": { "$": "auto_text_key", "code": 233, "label": "é" },
"relevant": [
{ "$": "auto_text_key", "code": 275, "label": "ē" },
{ "$": "auto_text_key", "code": 281, "label": "ę" },
{ "$": "auto_text_key", "code": 279, "label": "ė" },
{ "$": "auto_text_key", "code": 235, "label": "ë" },
{ "$": "auto_text_key", "code": 232, "label": "è" },
{ "$": "auto_text_key", "code": 234, "label": "ê" }
]
},
"i": {
"main": { "$": "auto_text_key", "code": 237, "label": "í" },
"relevant": [
{ "$": "auto_text_key", "code": 299, "label": "ī" },
{ "$": "auto_text_key", "code": 238, "label": "î" },
{ "$": "auto_text_key", "code": 303, "label": "į" },
{ "$": "auto_text_key", "code": 236, "label": "ì" },
{ "$": "auto_text_key", "code": 239, "label": "ï" }
]
},
"l": {
"relevant": [
{ "$": "auto_text_key", "code": 320, "label": "ŀ" },
{ "$": "auto_text_key", "code": 183, "label": "·" }
]
},
"o": {
"main": { "$": "auto_text_key", "code": 242, "label": "ò" },
"relevant": [
{ "$": "auto_text_key", "code": 186, "label": "º" },
{ "$": "auto_text_key", "code": 333, "label": "ō" },
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 339, "label": "œ" },
{ "$": "auto_text_key", "code": 245, "label": "õ" },
{ "$": "auto_text_key", "code": 244, "label": "ô" },
{ "$": "auto_text_key", "code": 246, "label": "ö" },
{ "$": "auto_text_key", "code": 243, "label": "ó" }
]
},
"s": {
"relevant": [
{ "$": "auto_text_key", "code": 223, "label": "ß" }
]
},
"u": {
"main": { "$": "auto_text_key", "code": 250, "label": "ú" },
"relevant": [
{ "$": "auto_text_key", "code": 363, "label": "ū" },
{ "$": "auto_text_key", "code": 249, "label": "ù" },
{ "$": "auto_text_key", "code": 252, "label": "ü" },
{ "$": "auto_text_key", "code": 251, "label": "û" }
]
},
"~right": {
"main": { "code": 44, "label": "," },
"relevant": [
{ "code": 38, "label": "&" },
{ "code": 37, "label": "%" },
{ "code": 43, "label": "+" },
{ "code": 34, "label": "\"" },
{ "code": 45, "label": "-" },
{ "code": 58, "label": ":" },
{ "code": 39, "label": "'" },
{ "code": 64, "label": "@" },
{ "code": 59, "label": ";" },
{ "code": 47, "label": "/" },
{ "code": 40, "label": "(" },
{ "code": 41, "label": ")" },
{ "code": 35, "label": "#" },
{ "code": 33, "label": "!" },
{ "code": 63, "label": "?" }
]
}
},
"uri": {
"~right": {
"main": { "code": -255, "label": ".com" },
"relevant": [
{ "code": -255, "label": ".org" },
{ "code": -255, "label": ".cat" },
{ "code": -255, "label": ".es" },
{ "code": -255, "label": ".net" }
]
}
}
}
}

View File

@@ -0,0 +1,115 @@
{
"type": "characters/extended_popups",
"name": "cs",
"authors": [ "stefan-misik" ],
"mapping": {
"all": {
"e": {
"relevant": [
{ "$": "auto_text_key", "code": 283, "label": "ě" },
{ "$": "auto_text_key", "code": 233, "label": "é" }
]
},
"r": {
"relevant": [
{ "$": "auto_text_key", "code": 345, "label": "ř" },
{ "$": "auto_text_key", "code": 341, "label": "ŕ" }
]
},
"t": {
"relevant": [
{ "$": "auto_text_key", "code": 357, "label": "ť" }
]
},
"y": {
"relevant": [
{ "$": "auto_text_key", "code": 253, "label": "ý" }
]
},
"u": {
"relevant": [
{ "$": "auto_text_key", "code": 367, "label": "ů" },
{ "$": "auto_text_key", "code": 250, "label": "ú" }
]
},
"i": {
"relevant": [
{ "$": "auto_text_key", "code": 237, "label": "í" }
]
},
"o": {
"relevant": [
{ "$": "auto_text_key", "code": 243, "label": "ó" },
{ "$": "auto_text_key", "code": 244, "label": "ô" }
]
},
"a": {
"relevant": [
{ "$": "auto_text_key", "code": 225, "label": "á" }
]
},
"s": {
"relevant": [
{ "$": "auto_text_key", "code": 353, "label": "š" }
]
},
"d": {
"relevant": [
{ "$": "auto_text_key", "code": 271, "label": "ď" }
]
},
"l": {
"relevant": [
{ "$": "auto_text_key", "code": 318, "label": "ľ" },
{ "$": "auto_text_key", "code": 314, "label": "ĺ" }
]
},
"z": {
"relevant": [
{ "$": "auto_text_key", "code": 382, "label": "ž" }
]
},
"c": {
"relevant": [
{ "$": "auto_text_key", "code": 269, "label": "č" }
]
},
"n": {
"relevant": [
{ "$": "auto_text_key", "code": 328, "label": "ň" }
]
},
"~right": {
"main": { "code": 44, "label": "," },
"relevant": [
{ "code": 38, "label": "&" },
{ "code": 37, "label": "%" },
{ "code": 43, "label": "+" },
{ "code": 34, "label": "\"" },
{ "code": 45, "label": "-" },
{ "code": 58, "label": ":" },
{ "code": 39, "label": "'" },
{ "code": 64, "label": "@" },
{ "code": 59, "label": ";" },
{ "code": 47, "label": "/" },
{ "code": 40, "label": "(" },
{ "code": 41, "label": ")" },
{ "code": 35, "label": "#" },
{ "code": 33, "label": "!" },
{ "code": 63, "label": "?" }
]
}
},
"uri": {
"~right": {
"main": { "code": -255, "label": ".com" },
"relevant": [
{ "code": -255, "label": ".cz" },
{ "code": -255, "label": ".eu" },
{ "code": -255, "label": ".org" },
{ "code": -255, "label": ".net" }
]
}
}
}
}

View File

@@ -6,95 +6,95 @@
"all": {
"a": {
"relevant": [
{ "code": 224, "label": "à" },
{ "code": 226, "label": "â" },
{ "code": 227, "label": "ã" },
{ "code": 257, "label": "ā" },
{ "code": 229, "label": "å" },
{ "code": 230, "label": "æ" },
{ "code": 225, "label": "á" },
{ "code": 228, "label": "ä" }
{ "$": "auto_text_key", "code": 224, "label": "à" },
{ "$": "auto_text_key", "code": 226, "label": "â" },
{ "$": "auto_text_key", "code": 227, "label": "ã" },
{ "$": "auto_text_key", "code": 257, "label": "ā" },
{ "$": "auto_text_key", "code": 229, "label": "å" },
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 225, "label": "á" },
{ "$": "auto_text_key", "code": 228, "label": "ä" }
]
},
"d": {
"relevant": [
{ "code": 240, "label": "ð" }
{ "$": "auto_text_key", "code": 240, "label": "ð" }
]
},
"e": {
"relevant": [
{ "code": 233, "label": "é" },
{ "code": 275, "label": "ē" },
{ "code": 281, "label": "ę" },
{ "code": 279, "label": "ė" },
{ "code": 232, "label": "è" },
{ "code": 235, "label": "ë" },
{ "code": 234, "label": "ê" }
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 275, "label": "ē" },
{ "$": "auto_text_key", "code": 281, "label": "ę" },
{ "$": "auto_text_key", "code": 279, "label": "ė" },
{ "$": "auto_text_key", "code": 232, "label": "è" },
{ "$": "auto_text_key", "code": 235, "label": "ë" },
{ "$": "auto_text_key", "code": 234, "label": "ê" }
]
},
"i": {
"relevant": [
{ "code": 237, "label": "í" },
{ "code": 299, "label": "ī" },
{ "code": 236, "label": "ì" },
{ "code": 303, "label": "į" },
{ "code": 238, "label": "î" },
{ "code": 239, "label": "ï" }
{ "$": "auto_text_key", "code": 237, "label": "í" },
{ "$": "auto_text_key", "code": 299, "label": "ī" },
{ "$": "auto_text_key", "code": 236, "label": "ì" },
{ "$": "auto_text_key", "code": 303, "label": "į" },
{ "$": "auto_text_key", "code": 238, "label": "î" },
{ "$": "auto_text_key", "code": 239, "label": "ï" }
]
},
"l": {
"relevant": [
{ "code": 322, "label": "ł" }
{ "$": "auto_text_key", "code": 322, "label": "ł" }
]
},
"n": {
"relevant": [
{ "code": 241, "label": "ñ" },
{ "code": 324, "label": "ń" }
{ "$": "auto_text_key", "code": 241, "label": "ñ" },
{ "$": "auto_text_key", "code": 324, "label": "ń" }
]
},
"o": {
"relevant": [
{ "code": 248, "label": "ø" },
{ "code": 333, "label": "ō" },
{ "code": 339, "label": "œ" },
{ "code": 242, "label": "ò" },
{ "code": 245, "label": "õ" },
{ "code": 244, "label": "ô" },
{ "code": 243, "label": "ó" },
{ "code": 246, "label": "ö" }
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 333, "label": "ō" },
{ "$": "auto_text_key", "code": 339, "label": "œ" },
{ "$": "auto_text_key", "code": 242, "label": "ò" },
{ "$": "auto_text_key", "code": 245, "label": "õ" },
{ "$": "auto_text_key", "code": 244, "label": "ô" },
{ "$": "auto_text_key", "code": 243, "label": "ó" },
{ "$": "auto_text_key", "code": 246, "label": "ö" }
]
},
"s": {
"relevant": [
{ "code": 223, "label": "ß" },
{ "code": 347, "label": "ś" },
{ "code": 353, "label": "š" }
{ "$": "auto_text_key", "code": 223, "label": "ß" },
{ "$": "auto_text_key", "code": 347, "label": "ś" },
{ "$": "auto_text_key", "code": 353, "label": "š" }
]
},
"u": {
"relevant": [
{ "code": 363, "label": "ū" },
{ "code": 251, "label": "û" },
{ "code": 250, "label": "ú" },
{ "code": 252, "label": "ü" },
{ "code": 249, "label": "ù" }
{ "$": "auto_text_key", "code": 363, "label": "ū" },
{ "$": "auto_text_key", "code": 251, "label": "û" },
{ "$": "auto_text_key", "code": 250, "label": "ú" },
{ "$": "auto_text_key", "code": 252, "label": "ü" },
{ "$": "auto_text_key", "code": 249, "label": "ù" }
]
},
"y": {
"relevant": [
{ "code": 253, "label": "ý" },
{ "code": 255, "label": "ÿ" }
{ "$": "auto_text_key", "code": 253, "label": "ý" },
{ "$": "auto_text_key", "code": 255, "label": "ÿ" }
]
},
"æ": {
"relevant": [
{ "code": 228, "label": "ä" }
{ "$": "auto_text_key", "code": 228, "label": "ä" }
]
},
"ø": {
"relevant": [
{ "code": 246, "label": "ö" }
{ "$": "auto_text_key", "code": 246, "label": "ö" }
]
},
"~right": {

View File

@@ -5,72 +5,72 @@
"mapping": {
"all": {
"a": {
"main": { "code": 228, "label": "ä" },
"main": { "$": "auto_text_key", "code": 228, "label": "ä" },
"relevant": [
{ "code": 230, "label": "æ" },
{ "code": 227, "label": "ã" },
{ "code": 229, "label": "å" },
{ "code": 257, "label": "ā" },
{ "code": 226, "label": "â" },
{ "code": 224, "label": "à" },
{ "code": 225, "label": "á" }
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 227, "label": "ã" },
{ "$": "auto_text_key", "code": 229, "label": "å" },
{ "$": "auto_text_key", "code": 257, "label": "ā" },
{ "$": "auto_text_key", "code": 226, "label": "â" },
{ "$": "auto_text_key", "code": 224, "label": "à" },
{ "$": "auto_text_key", "code": 225, "label": "á" }
]
},
"c": {
"relevant": [
{ "code": 231, "label": "ç" }
{ "$": "auto_text_key", "code": 231, "label": "ç" }
]
},
"e": {
"relevant": [
{ "code": 233, "label": "é" },
{ "code": 275, "label": "ē" },
{ "code": 234, "label": "ê" },
{ "code": 232, "label": "è" },
{ "code": 235, "label": "ë" }
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 275, "label": "ē" },
{ "$": "auto_text_key", "code": 234, "label": "ê" },
{ "$": "auto_text_key", "code": 232, "label": "è" },
{ "$": "auto_text_key", "code": 235, "label": "ë" }
]
},
"i": {
"relevant": [
{ "code": 237, "label": "í" },
{ "code": 236, "label": "ì" },
{ "code": 239, "label": "ï" },
{ "code": 238, "label": "î" },
{ "code": 299, "label": "ī" }
{ "$": "auto_text_key", "code": 237, "label": "í" },
{ "$": "auto_text_key", "code": 236, "label": "ì" },
{ "$": "auto_text_key", "code": 239, "label": "ï" },
{ "$": "auto_text_key", "code": 238, "label": "î" },
{ "$": "auto_text_key", "code": 299, "label": "ī" }
]
},
"n": {
"relevant": [
{ "code": 241, "label": "ñ" },
{ "code": 324, "label": "ń" }
{ "$": "auto_text_key", "code": 241, "label": "ñ" },
{ "$": "auto_text_key", "code": 324, "label": "ń" }
]
},
"o": {
"main": { "code": 246, "label": "ö" },
"main": { "$": "auto_text_key", "code": 246, "label": "ö" },
"relevant": [
{ "code": 333, "label": "ō" },
{ "code": 248, "label": "ø" },
{ "code": 245, "label": "õ" },
{ "code": 339, "label": "œ" },
{ "code": 243, "label": "ó" },
{ "code": 242, "label": "ò" },
{ "code": 244, "label": "ô" }
{ "$": "auto_text_key", "code": 333, "label": "ō" },
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 245, "label": "õ" },
{ "$": "auto_text_key", "code": 339, "label": "œ" },
{ "$": "auto_text_key", "code": 243, "label": "ó" },
{ "$": "auto_text_key", "code": 242, "label": "ò" },
{ "$": "auto_text_key", "code": 244, "label": "ô" }
]
},
"s": {
"main": { "code": 223, "label": "ß" },
"main": { "$": "auto_text_key", "code": 223, "label": "ß" },
"relevant": [
{ "code": 353, "label": "š" },
{ "code": 347, "label": "ś" }
{ "$": "auto_text_key", "code": 353, "label": "š" },
{ "$": "auto_text_key", "code": 347, "label": "ś" }
]
},
"u": {
"main": { "code": 252, "label": "ü" },
"main": { "$": "auto_text_key", "code": 252, "label": "ü" },
"relevant": [
{ "code": 363, "label": "ū" },
{ "code": 249, "label": "ù" },
{ "code": 251, "label": "û" },
{ "code": 250, "label": "ú" }
{ "$": "auto_text_key", "code": 363, "label": "ū" },
{ "$": "auto_text_key", "code": 249, "label": "ù" },
{ "$": "auto_text_key", "code": 251, "label": "û" },
{ "$": "auto_text_key", "code": 250, "label": "ú" }
]
},
"~right": {

View File

@@ -6,41 +6,41 @@
"all": {
"α": {
"relevant": [
{ "code": 940, "label": "ά" }
{ "$": "auto_text_key", "code": 940, "label": "ά" }
]
},
"ε": {
"relevant": [
{ "code": 941, "label": "έ" }
{ "$": "auto_text_key", "code": 941, "label": "έ" }
]
},
"η": {
"relevant": [
{ "code": 942, "label": "ή" }
{ "$": "auto_text_key", "code": 942, "label": "ή" }
]
},
"ι": {
"relevant": [
{ "code": 912, "label": "ΐ" },
{ "code": 970, "label": "ϊ" },
{ "code": 943, "label": "ί" }
{ "$": "auto_text_key", "code": 912, "label": "ΐ" },
{ "$": "auto_text_key", "code": 970, "label": "ϊ" },
{ "$": "auto_text_key", "code": 943, "label": "ί" }
]
},
"ο": {
"relevant": [
{ "code": 972, "label": "ό" }
{ "$": "auto_text_key", "code": 972, "label": "ό" }
]
},
"υ": {
"relevant": [
{ "code": 944, "label": "ΰ" },
{ "code": 971, "label": "ϋ" },
{ "code": 973, "label": "ύ" }
{ "$": "auto_text_key", "code": 944, "label": "ΰ" },
{ "$": "auto_text_key", "code": 971, "label": "ϋ" },
{ "$": "auto_text_key", "code": 973, "label": "ύ" }
]
},
"ω": {
"relevant": [
{ "code": 974, "label": "ώ" }
{ "$": "auto_text_key", "code": 974, "label": "ώ" }
]
},
"~right": {

View File

@@ -6,69 +6,69 @@
"all": {
"a": {
"relevant": [
{ "code": 230, "label": "æ" },
{ "code": 227, "label": "ã" },
{ "code": 229, "label": "å" },
{ "code": 257, "label": "ā" },
{ "code": 224, "label": "à" },
{ "code": 225, "label": "á" },
{ "code": 226, "label": "â" },
{ "code": 228, "label": "ä" }
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 227, "label": "ã" },
{ "$": "auto_text_key", "code": 229, "label": "å" },
{ "$": "auto_text_key", "code": 257, "label": "ā" },
{ "$": "auto_text_key", "code": 224, "label": "à" },
{ "$": "auto_text_key", "code": 225, "label": "á" },
{ "$": "auto_text_key", "code": 226, "label": "â" },
{ "$": "auto_text_key", "code": 228, "label": "ä" }
]
},
"c": {
"relevant": [
{ "code": 231, "label": "ç" }
{ "$": "auto_text_key", "code": 231, "label": "ç" }
]
},
"e": {
"relevant": [
{ "code": 275, "label": "ē" },
{ "code": 234, "label": "ê" },
{ "code": 233, "label": "é" },
{ "code": 232, "label": "è" },
{ "code": 235, "label": "ë" }
{ "$": "auto_text_key", "code": 275, "label": "ē" },
{ "$": "auto_text_key", "code": 234, "label": "ê" },
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 232, "label": "è" },
{ "$": "auto_text_key", "code": 235, "label": "ë" }
]
},
"i": {
"relevant": [
{ "code": 236, "label": "ì" },
{ "code": 239, "label": "ï" },
{ "code": 237, "label": "í" },
{ "code": 238, "label": "î" },
{ "code": 299, "label": "ī" }
{ "$": "auto_text_key", "code": 236, "label": "ì" },
{ "$": "auto_text_key", "code": 239, "label": "ï" },
{ "$": "auto_text_key", "code": 237, "label": "í" },
{ "$": "auto_text_key", "code": 238, "label": "î" },
{ "$": "auto_text_key", "code": 299, "label": "ī" }
]
},
"n": {
"relevant": [
{ "code": 241, "label": "ñ" },
{ "code": 324, "label": "ń" }
{ "$": "auto_text_key", "code": 241, "label": "ñ" },
{ "$": "auto_text_key", "code": 324, "label": "ń" }
]
},
"o": {
"relevant": [
{ "code": 245, "label": "õ" },
{ "code": 333, "label": "ō" },
{ "code": 339, "label": "œ" },
{ "code": 248, "label": "ø" },
{ "code": 242, "label": "ò" },
{ "code": 246, "label": "ö" },
{ "code": 243, "label": "ó" },
{ "code": 244, "label": "ô" }
{ "$": "auto_text_key", "code": 245, "label": "õ" },
{ "$": "auto_text_key", "code": 333, "label": "ō" },
{ "$": "auto_text_key", "code": 339, "label": "œ" },
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 242, "label": "ò" },
{ "$": "auto_text_key", "code": 246, "label": "ö" },
{ "$": "auto_text_key", "code": 243, "label": "ó" },
{ "$": "auto_text_key", "code": 244, "label": "ô" }
]
},
"s": {
"relevant": [
{ "code": 223, "label": "ß" }
{ "$": "auto_text_key", "code": 223, "label": "ß" }
]
},
"u": {
"relevant": [
{ "code": 250, "label": "ú" },
{ "code": 363, "label": "ū" },
{ "code": 252, "label": "ü" },
{ "code": 251, "label": "û" },
{ "code": 249, "label": "ù" }
{ "$": "auto_text_key", "code": 250, "label": "ú" },
{ "$": "auto_text_key", "code": 363, "label": "ū" },
{ "$": "auto_text_key", "code": 252, "label": "ü" },
{ "$": "auto_text_key", "code": 251, "label": "û" },
{ "$": "auto_text_key", "code": 249, "label": "ù" }
]
},
"~right": {

View File

@@ -6,52 +6,52 @@
"all": {
"c": {
"relevant": [
{ "code": 265, "label": "ĉ" }
{ "$": "auto_text_key", "code": 265, "label": "ĉ" }
]
},
"g": {
"relevant": [
{ "code": 285, "label": "ĝ" }
{ "$": "auto_text_key", "code": 285, "label": "ĝ" }
]
},
"h": {
"relevant": [
{ "code": 293, "label": "ĥ" }
{ "$": "auto_text_key", "code": 293, "label": "ĥ" }
]
},
"j": {
"relevant": [
{ "code": 309, "label": "ĵ" }
{ "$": "auto_text_key", "code": 309, "label": "ĵ" }
]
},
"s": {
"relevant": [
{ "code": 349, "label": "ŝ" }
{ "$": "auto_text_key", "code": 349, "label": "ŝ" }
]
},
"u": {
"relevant": [
{ "code": 365, "label": "ŭ" }
{ "$": "auto_text_key", "code": 365, "label": "ŭ" }
]
},
"q": {
"relevant": [
{ "code": 349, "label": "ŝ" }
{ "$": "auto_text_key", "code": 349, "label": "ŝ" }
]
},
"w": {
"relevant": [
{ "code": 285, "label": "ĝ" }
{ "$": "auto_text_key", "code": 285, "label": "ĝ" }
]
},
"x": {
"relevant": [
{ "code": 265, "label": "ĉ" }
{ "$": "auto_text_key", "code": 265, "label": "ĉ" }
]
},
"y": {
"relevant": [
{ "code": 365, "label": "ŭ" }
{ "$": "auto_text_key", "code": 365, "label": "ŭ" }
]
},
"~right": {

View File

@@ -5,78 +5,78 @@
"mapping": {
"all": {
"a": {
"main": { "code": 225, "label": "á" },
"main": { "$": "auto_text_key", "code": 225, "label": "á" },
"relevant": [
{ "code": 229, "label": "å" },
{ "code": 261, "label": "ą" },
{ "code": 230, "label": "æ" },
{ "code": 257, "label": "ā" },
{ "code": 170, "label": "ª" },
{ "code": 224, "label": "à" },
{ "code": 228, "label": "ä" },
{ "code": 226, "label": "â" },
{ "code": 227, "label": "ã" }
{ "$": "auto_text_key", "code": 229, "label": "å" },
{ "$": "auto_text_key", "code": 261, "label": "ą" },
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 257, "label": "ā" },
{ "$": "auto_text_key", "code": 170, "label": "ª" },
{ "$": "auto_text_key", "code": 224, "label": "à" },
{ "$": "auto_text_key", "code": 228, "label": "ä" },
{ "$": "auto_text_key", "code": 226, "label": "â" },
{ "$": "auto_text_key", "code": 227, "label": "ã" }
]
},
"c": {
"relevant": [
{ "code": 269, "label": "č" },
{ "code": 231, "label": "ç" },
{ "code": 263, "label": "ć" }
{ "$": "auto_text_key", "code": 269, "label": "č" },
{ "$": "auto_text_key", "code": 231, "label": "ç" },
{ "$": "auto_text_key", "code": 263, "label": "ć" }
]
},
"e": {
"main": { "code": 233, "label": "é" },
"main": { "$": "auto_text_key", "code": 233, "label": "é" },
"relevant": [
{ "code": 275, "label": "ē" },
{ "code": 281, "label": "ę" },
{ "code": 279, "label": "ė" },
{ "code": 235, "label": "ë" },
{ "code": 232, "label": "è" },
{ "code": 234, "label": "ê" }
{ "$": "auto_text_key", "code": 275, "label": "ē" },
{ "$": "auto_text_key", "code": 281, "label": "ę" },
{ "$": "auto_text_key", "code": 279, "label": "ė" },
{ "$": "auto_text_key", "code": 235, "label": "ë" },
{ "$": "auto_text_key", "code": 232, "label": "è" },
{ "$": "auto_text_key", "code": 234, "label": "ê" }
]
},
"i": {
"main": { "code": 237, "label": "í" },
"main": { "$": "auto_text_key", "code": 237, "label": "í" },
"relevant": [
{ "code": 299, "label": "ī" },
{ "code": 238, "label": "î" },
{ "code": 303, "label": "į" },
{ "code": 236, "label": "ì" },
{ "code": 239, "label": "ï" }
{ "$": "auto_text_key", "code": 299, "label": "ī" },
{ "$": "auto_text_key", "code": 238, "label": "î" },
{ "$": "auto_text_key", "code": 303, "label": "į" },
{ "$": "auto_text_key", "code": 236, "label": "ì" },
{ "$": "auto_text_key", "code": 239, "label": "ï" }
]
},
"n": {
"relevant": [
{ "code": 241, "label": "ñ" },
{ "code": 324, "label": "ń" }
{ "$": "auto_text_key", "code": 241, "label": "ñ" },
{ "$": "auto_text_key", "code": 324, "label": "ń" }
]
},
"o": {
"main": { "code": 243, "label": "ó" },
"main": { "$": "auto_text_key", "code": 243, "label": "ó" },
"relevant": [
{ "code": 186, "label": "º" },
{ "code": 333, "label": "ō" },
{ "code": 248, "label": "ø" },
{ "code": 339, "label": "œ" },
{ "code": 245, "label": "õ" },
{ "code": 244, "label": "ô" },
{ "code": 246, "label": "ö" },
{ "code": 242, "label": "ò" }
{ "$": "auto_text_key", "code": 186, "label": "º" },
{ "$": "auto_text_key", "code": 333, "label": "ō" },
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 339, "label": "œ" },
{ "$": "auto_text_key", "code": 245, "label": "õ" },
{ "$": "auto_text_key", "code": 244, "label": "ô" },
{ "$": "auto_text_key", "code": 246, "label": "ö" },
{ "$": "auto_text_key", "code": 242, "label": "ò" }
]
},
"s": {
"relevant": [
{ "code": 223, "label": "ß" }
{ "$": "auto_text_key", "code": 223, "label": "ß" }
]
},
"u": {
"main": { "code": 250, "label": "ú" },
"main": { "$": "auto_text_key", "code": 250, "label": "ú" },
"relevant": [
{ "code": 363, "label": "ū" },
{ "code": 249, "label": "ù" },
{ "code": 252, "label": "ü" },
{ "code": 251, "label": "û" }
{ "$": "auto_text_key", "code": 363, "label": "ū" },
{ "$": "auto_text_key", "code": 249, "label": "ù" },
{ "$": "auto_text_key", "code": 252, "label": "ü" },
{ "$": "auto_text_key", "code": 251, "label": "û" }
]
},
"~right": {

View File

@@ -6,80 +6,80 @@
"all": {
"a": {
"relevant": [
{ "code": 228, "label": "ä" },
{ "code": 225, "label": "á" },
{ "code": 226, "label": "â" },
{ "code": 227, "label": "ã" },
{ "code": 257, "label": "ā" },
{ "code": 229, "label": "å" },
{ "code": 230, "label": "æ" },
{ "code": 224, "label": "à" }
{ "$": "auto_text_key", "code": 228, "label": "ä" },
{ "$": "auto_text_key", "code": 225, "label": "á" },
{ "$": "auto_text_key", "code": 226, "label": "â" },
{ "$": "auto_text_key", "code": 227, "label": "ã" },
{ "$": "auto_text_key", "code": 257, "label": "ā" },
{ "$": "auto_text_key", "code": 229, "label": "å" },
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 224, "label": "à" }
]
},
"e": {
"relevant": [
{ "code": 233, "label": "é" },
{ "code": 275, "label": "ē" },
{ "code": 281, "label": "ę" },
{ "code": 279, "label": "ė" },
{ "code": 232, "label": "è" },
{ "code": 235, "label": "ë" },
{ "code": 234, "label": "ê" }
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 275, "label": "ē" },
{ "$": "auto_text_key", "code": 281, "label": "ę" },
{ "$": "auto_text_key", "code": 279, "label": "ė" },
{ "$": "auto_text_key", "code": 232, "label": "è" },
{ "$": "auto_text_key", "code": 235, "label": "ë" },
{ "$": "auto_text_key", "code": 234, "label": "ê" }
]
},
"i": {
"relevant": [
{ "code": 237, "label": "í" },
{ "code": 299, "label": "ī" },
{ "code": 236, "label": "ì" },
{ "code": 303, "label": "į" },
{ "code": 238, "label": "î" },
{ "code": 239, "label": "ï" }
{ "$": "auto_text_key", "code": 237, "label": "í" },
{ "$": "auto_text_key", "code": 299, "label": "ī" },
{ "$": "auto_text_key", "code": 236, "label": "ì" },
{ "$": "auto_text_key", "code": 303, "label": "į" },
{ "$": "auto_text_key", "code": 238, "label": "î" },
{ "$": "auto_text_key", "code": 239, "label": "ï" }
]
},
"o": {
"relevant": [
{ "code": 246, "label": "ö" },
{ "code": 333, "label": "ō" },
{ "code": 339, "label": "œ" },
{ "code": 243, "label": "ó" },
{ "code": 245, "label": "õ" },
{ "code": 242, "label": "ò" },
{ "code": 244, "label": "ô" },
{ "code": 248, "label": "ø" }
{ "$": "auto_text_key", "code": 246, "label": "ö" },
{ "$": "auto_text_key", "code": 333, "label": "ō" },
{ "$": "auto_text_key", "code": 339, "label": "œ" },
{ "$": "auto_text_key", "code": 243, "label": "ó" },
{ "$": "auto_text_key", "code": 245, "label": "õ" },
{ "$": "auto_text_key", "code": 242, "label": "ò" },
{ "$": "auto_text_key", "code": 244, "label": "ô" },
{ "$": "auto_text_key", "code": 248, "label": "ø" }
]
},
"s": {
"relevant": [
{ "code": 353, "label": "š" },
{ "code": 223, "label": "ß" },
{ "code": 347, "label": "ś" }
{ "$": "auto_text_key", "code": 353, "label": "š" },
{ "$": "auto_text_key", "code": 223, "label": "ß" },
{ "$": "auto_text_key", "code": 347, "label": "ś" }
]
},
"u": {
"relevant": [
{ "code": 252, "label": "ü" },
{ "code": 363, "label": "ū" },
{ "code": 251, "label": "û" },
{ "code": 250, "label": "ú" },
{ "code": 249, "label": "ù" }
{ "$": "auto_text_key", "code": 252, "label": "ü" },
{ "$": "auto_text_key", "code": 363, "label": "ū" },
{ "$": "auto_text_key", "code": 251, "label": "û" },
{ "$": "auto_text_key", "code": 250, "label": "ú" },
{ "$": "auto_text_key", "code": 249, "label": "ù" }
]
},
"z": {
"relevant": [
{ "code": 382, "label": "ž" },
{ "code": 380, "label": "ż" },
{ "code": 378, "label": "ź" }
{ "$": "auto_text_key", "code": 382, "label": "ž" },
{ "$": "auto_text_key", "code": 380, "label": "ż" },
{ "$": "auto_text_key", "code": 378, "label": "ź" }
]
},
"ä": {
"relevant": [
{ "code": 230, "label": "æ" }
{ "$": "auto_text_key", "code": 230, "label": "æ" }
]
},
"ö": {
"relevant": [
{ "code": 248, "label": "ø" }
{ "$": "auto_text_key", "code": 248, "label": "ø" }
]
},
"~right": {

View File

@@ -5,101 +5,101 @@
"mapping": {
"all": {
"a": {
"main": { "code": 225, "label": "á" },
"main": { "$": "auto_text_key", "code": 225, "label": "á" },
"relevant": [
{ "code": 224, "label": "à" },
{ "code": 226, "label": "â" },
{ "code": 227, "label": "ã" },
{ "code": 257, "label": "ā" },
{ "code": 229, "label": "å" },
{ "code": 230, "label": "æ" },
{ "code": 228, "label": "ä" }
{ "$": "auto_text_key", "code": 224, "label": "à" },
{ "$": "auto_text_key", "code": 226, "label": "â" },
{ "$": "auto_text_key", "code": 227, "label": "ã" },
{ "$": "auto_text_key", "code": 257, "label": "ā" },
{ "$": "auto_text_key", "code": 229, "label": "å" },
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 228, "label": "ä" }
]
},
"e": {
"relevant": [
{ "code": 233, "label": "é" },
{ "code": 275, "label": "ē" },
{ "code": 281, "label": "ę" },
{ "code": 279, "label": "ė" },
{ "code": 232, "label": "è" },
{ "code": 235, "label": "ë" },
{ "code": 234, "label": "ê" }
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 275, "label": "ē" },
{ "$": "auto_text_key", "code": 281, "label": "ę" },
{ "$": "auto_text_key", "code": 279, "label": "ė" },
{ "$": "auto_text_key", "code": 232, "label": "è" },
{ "$": "auto_text_key", "code": 235, "label": "ë" },
{ "$": "auto_text_key", "code": 234, "label": "ê" }
]
},
"i": {
"main": { "code": 237, "label": "í" },
"main": { "$": "auto_text_key", "code": 237, "label": "í" },
"relevant": [
{ "code": 299, "label": "ī" },
{ "code": 236, "label": "ì" },
{ "code": 303, "label": "į" },
{ "code": 238, "label": "î" },
{ "code": 239, "label": "ï" }
{ "$": "auto_text_key", "code": 299, "label": "ī" },
{ "$": "auto_text_key", "code": 236, "label": "ì" },
{ "$": "auto_text_key", "code": 303, "label": "į" },
{ "$": "auto_text_key", "code": 238, "label": "î" },
{ "$": "auto_text_key", "code": 239, "label": "ï" }
]
},
"l": {
"relevant": [
{ "code": 322, "label": "ł" }
{ "$": "auto_text_key", "code": 322, "label": "ł" }
]
},
"n": {
"relevant": [
{ "code": 241, "label": "ñ" },
{ "code": 324, "label": "ń" }
{ "$": "auto_text_key", "code": 241, "label": "ñ" },
{ "$": "auto_text_key", "code": 324, "label": "ń" }
]
},
"o": {
"main": { "code": 243, "label": "ó" },
"main": { "$": "auto_text_key", "code": 243, "label": "ó" },
"relevant": [
{ "code": 248, "label": "ø" },
{ "code": 333, "label": "ō" },
{ "code": 339, "label": "œ" },
{ "code": 242, "label": "ò" },
{ "code": 245, "label": "õ" },
{ "code": 244, "label": "ô" },
{ "code": 246, "label": "ö" }
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 333, "label": "ō" },
{ "$": "auto_text_key", "code": 339, "label": "œ" },
{ "$": "auto_text_key", "code": 242, "label": "ò" },
{ "$": "auto_text_key", "code": 245, "label": "õ" },
{ "$": "auto_text_key", "code": 244, "label": "ô" },
{ "$": "auto_text_key", "code": 246, "label": "ö" }
]
},
"t": {
"relevant": [
{ "code": 254, "label": "þ" }
{ "$": "auto_text_key", "code": 254, "label": "þ" }
]
},
"s": {
"relevant": [
{ "code": 223, "label": "ß" },
{ "code": 347, "label": "ś" },
{ "code": 353, "label": "š" }
{ "$": "auto_text_key", "code": 223, "label": "ß" },
{ "$": "auto_text_key", "code": 347, "label": "ś" },
{ "$": "auto_text_key", "code": 353, "label": "š" }
]
},
"u": {
"main": { "code": 250, "label": "ú" },
"main": { "$": "auto_text_key", "code": 250, "label": "ú" },
"relevant": [
{ "code": 363, "label": "ū" },
{ "code": 251, "label": "û" },
{ "code": 252, "label": "ü" },
{ "code": 249, "label": "ù" }
{ "$": "auto_text_key", "code": 363, "label": "ū" },
{ "$": "auto_text_key", "code": 251, "label": "û" },
{ "$": "auto_text_key", "code": 252, "label": "ü" },
{ "$": "auto_text_key", "code": 249, "label": "ù" }
]
},
"y": {
"main": { "code": 253, "label": "ý" },
"main": { "$": "auto_text_key", "code": 253, "label": "ý" },
"relevant": [
{ "code": 255, "label": "ÿ" }
{ "$": "auto_text_key", "code": 255, "label": "ÿ" }
]
},
"æ": {
"relevant": [
{ "code": 228, "label": "ä" }
{ "$": "auto_text_key", "code": 228, "label": "ä" }
]
},
"ð": {
"relevant": [
{ "code": 254, "label": "þ" }
{ "$": "auto_text_key", "code": 254, "label": "þ" }
]
},
"ø": {
"relevant": [
{ "code": 246, "label": "ö" }
{ "$": "auto_text_key", "code": 246, "label": "ö" }
]
},
"~right": {

View File

@@ -5,84 +5,84 @@
"mapping": {
"all": {
"a": {
"main": { "$": "auto_text_key", "code": 224, "label": "à" },
"relevant": [
{ "code": 224, "label": "à" },
{ "code": 227, "label": "ã" },
{ "code": 229, "label": "å" },
{ "code": 257, "label": "ā" },
{ "code": 170, "label": "ª" },
{ "code": 226, "label": "â" },
{ "code": 230, "label": "æ" },
{ "code": 225, "label": "á" },
{ "code": 228, "label": "ä" }
{ "$": "auto_text_key", "code": 228, "label": "ä" },
{ "$": "auto_text_key", "code": 227, "label": "ã" },
{ "$": "auto_text_key", "code": 229, "label": "å" },
{ "$": "auto_text_key", "code": 257, "label": "ā" },
{ "$": "auto_text_key", "code": 170, "label": "ª" },
{ "$": "auto_text_key", "code": 226, "label": "â" },
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 225, "label": "á" }
]
},
"c": {
"main": { "$": "auto_text_key", "code": 231, "label": "ç" },
"relevant": [
{ "code": 231, "label": "ç" },
{ "code": 269, "label": "č" },
{ "code": 263, "label": "ć" }
{ "$": "auto_text_key", "code": 269, "label": "č" },
{ "$": "auto_text_key", "code": 263, "label": "ć" }
]
},
"e": {
"main": { "$": "auto_text_key", "code": 233, "label": "é" },
"relevant": [
{ "code": 233, "label": "é" },
{ "code": 275, "label": "ē" },
{ "code": 281, "label": "ę" },
{ "code": 279, "label": "ė" },
{ "code": 234, "label": "ê" },
{ "code": 232, "label": "è" },
{ "code": 235, "label": "ë" }
{ "$": "auto_text_key", "code": 279, "label": "ė" },
{ "$": "auto_text_key", "code": 281, "label": "ę" },
{ "$": "auto_text_key", "code": 275, "label": "ē" },
{ "$": "auto_text_key", "code": 234, "label": "ê" },
{ "$": "auto_text_key", "code": 232, "label": "è" },
{ "$": "auto_text_key", "code": 235, "label": "ë" }
]
},
"i": {
"main": { "$": "auto_text_key", "code": 238, "label": "î" },
"relevant": [
{ "code": 238, "label": "î" },
{ "code": 299, "label": "ī" },
{ "code": 237, "label": "í" },
{ "code": 303, "label": "į" },
{ "code": 236, "label": "ì" },
{ "code": 239, "label": "ï" }
{ "$": "auto_text_key", "code": 299, "label": "ī" },
{ "$": "auto_text_key", "code": 237, "label": "í" },
{ "$": "auto_text_key", "code": 303, "label": "į" },
{ "$": "auto_text_key", "code": 236, "label": "ì" },
{ "$": "auto_text_key", "code": 239, "label": "ï" }
]
},
"n": {
"relevant": [
{ "code": 241, "label": "ñ" },
{ "code": 324, "label": "ń" }
{ "$": "auto_text_key", "code": 241, "label": "ñ" },
{ "$": "auto_text_key", "code": 324, "label": "ń" }
]
},
"o": {
"main": { "$": "auto_text_key", "code": 244, "label": "ô" },
"relevant": [
{ "code": 244, "label": "ô" },
{ "code": 186, "label": "º" },
{ "code": 333, "label": "ō" },
{ "code": 245, "label": "õ" },
{ "code": 248, "label": "ø" },
{ "code": 243, "label": "ó" },
{ "code": 242, "label": "ò" },
{ "code": 246, "label": "ö" },
{ "code": 339, "label": "œ" }
{ "$": "auto_text_key", "code": 186, "label": "º" },
{ "$": "auto_text_key", "code": 333, "label": "ō" },
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 245, "label": "õ" },
{ "$": "auto_text_key", "code": 243, "label": "ó" },
{ "$": "auto_text_key", "code": 242, "label": "ò" },
{ "$": "auto_text_key", "code": 246, "label": "ö" },
{ "$": "auto_text_key", "code": 339, "label": "œ" }
]
},
"s": {
"relevant": [
{ "code": 223, "label": "ß" },
{ "code": 353, "label": "š" },
{ "code": 347, "label": "ś" }
{ "$": "auto_text_key", "code": 223, "label": "ß" },
{ "$": "auto_text_key", "code": 353, "label": "š" },
{ "$": "auto_text_key", "code": 347, "label": "ś" }
]
},
"u": {
"main": { "$": "auto_text_key", "code": 249, "label": "ù" },
"relevant": [
{ "code": 249, "label": "ù" },
{ "code": 363, "label": "ū" },
{ "code": 252, "label": "ü" },
{ "code": 251, "label": "û" },
{ "code": 250, "label": "ú" }
{ "$": "auto_text_key", "code": 363, "label": "ū" },
{ "$": "auto_text_key", "code": 252, "label": "ü" },
{ "$": "auto_text_key", "code": 250, "label": "ú" },
{ "$": "auto_text_key", "code": 251, "label": "û" }
]
},
"y": {
"relevant": [
{ "code": 255, "label": "ÿ" }
{ "$": "auto_text_key", "code": 255, "label": "ÿ" }
]
},
"~right": {

View File

@@ -6,23 +6,23 @@
"all": {
"c": {
"relevant": [
{ "code": 269, "label": "č" },
{ "code": 263, "label": "ć" }
{ "$": "auto_text_key", "code": 269, "label": "č" },
{ "$": "auto_text_key", "code": 263, "label": "ć" }
]
},
"d": {
"relevant": [
{ "code": 273, "label": "đ" }
{ "$": "auto_text_key", "code": 273, "label": "đ" }
]
},
"s": {
"relevant": [
{ "code": 353, "label": "š" }
{ "$": "auto_text_key", "code": 353, "label": "š" }
]
},
"z": {
"relevant": [
{ "code": 382, "label": "ž" }
{ "$": "auto_text_key", "code": 382, "label": "ž" }
]
},
"~right": {

View File

@@ -6,31 +6,31 @@
"all": {
"a": {
"relevant": [
{ "code": 225, "label": "á" }
{ "$": "auto_text_key", "code": 225, "label": "á" }
]
},
"e": {
"relevant": [
{ "code": 233, "label": "é" }
{ "$": "auto_text_key", "code": 233, "label": "é" }
]
},
"i": {
"relevant": [
{ "code": 237, "label": "í" }
{ "$": "auto_text_key", "code": 237, "label": "í" }
]
},
"o": {
"relevant": [
{ "code": 243, "label": "ó" },
{ "code": 246, "label": "ö" },
{ "code": 337, "label": "ő" }
{ "$": "auto_text_key", "code": 243, "label": "ó" },
{ "$": "auto_text_key", "code": 246, "label": "ö" },
{ "$": "auto_text_key", "code": 337, "label": "ő" }
]
},
"u": {
"relevant": [
{ "code": 250, "label": "ú" },
{ "code": 252, "label": "ü" },
{ "code": 369, "label": "ű" }
{ "$": "auto_text_key", "code": 250, "label": "ú" },
{ "$": "auto_text_key", "code": 252, "label": "ü" },
{ "$": "auto_text_key", "code": 369, "label": "ű" }
]
},
"~right": {

View File

@@ -6,72 +6,72 @@
"all": {
"a": {
"relevant": [
{ "code": 225, "label": "á" },
{ "code": 224, "label": "à" },
{ "code": 226, "label": "â" },
{ "code": 227, "label": "ã" },
{ "code": 257, "label": "ā" },
{ "code": 228, "label": "ä" },
{ "code": 230, "label": "æ" },
{ "code": 229, "label": "å" }
{ "$": "auto_text_key", "code": 225, "label": "á" },
{ "$": "auto_text_key", "code": 224, "label": "à" },
{ "$": "auto_text_key", "code": 226, "label": "â" },
{ "$": "auto_text_key", "code": 227, "label": "ã" },
{ "$": "auto_text_key", "code": 257, "label": "ā" },
{ "$": "auto_text_key", "code": 228, "label": "ä" },
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 229, "label": "å" }
]
},
"d": {
"relevant": [
{ "code": 240, "label": "ð" }
{ "$": "auto_text_key", "code": 240, "label": "ð" }
]
},
"e": {
"relevant": [
{ "code": 233, "label": "é" },
{ "code": 275, "label": "ē" },
{ "code": 281, "label": "ę" },
{ "code": 279, "label": "ė" },
{ "code": 232, "label": "è" },
{ "code": 235, "label": "ë" },
{ "code": 234, "label": "ê" }
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 275, "label": "ē" },
{ "$": "auto_text_key", "code": 281, "label": "ę" },
{ "$": "auto_text_key", "code": 279, "label": "ė" },
{ "$": "auto_text_key", "code": 232, "label": "è" },
{ "$": "auto_text_key", "code": 235, "label": "ë" },
{ "$": "auto_text_key", "code": 234, "label": "ê" }
]
},
"i": {
"relevant": [
{ "code": 237, "label": "í" },
{ "code": 299, "label": "ī" },
{ "code": 236, "label": "ì" },
{ "code": 303, "label": "į" },
{ "code": 238, "label": "î" },
{ "code": 239, "label": "ï" }
{ "$": "auto_text_key", "code": 237, "label": "í" },
{ "$": "auto_text_key", "code": 299, "label": "ī" },
{ "$": "auto_text_key", "code": 236, "label": "ì" },
{ "$": "auto_text_key", "code": 303, "label": "į" },
{ "$": "auto_text_key", "code": 238, "label": "î" },
{ "$": "auto_text_key", "code": 239, "label": "ï" }
]
},
"o": {
"relevant": [
{ "code": 243, "label": "ó" },
{ "code": 333, "label": "ō" },
{ "code": 248, "label": "ø" },
{ "code": 245, "label": "õ" },
{ "code": 339, "label": "œ" },
{ "code": 242, "label": "ò" },
{ "code": 244, "label": "ô" },
{ "code": 246, "label": "ö" }
{ "$": "auto_text_key", "code": 243, "label": "ó" },
{ "$": "auto_text_key", "code": 333, "label": "ō" },
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 245, "label": "õ" },
{ "$": "auto_text_key", "code": 339, "label": "œ" },
{ "$": "auto_text_key", "code": 242, "label": "ò" },
{ "$": "auto_text_key", "code": 244, "label": "ô" },
{ "$": "auto_text_key", "code": 246, "label": "ö" }
]
},
"t": {
"relevant": [
{ "code": 254, "label": "þ" }
{ "$": "auto_text_key", "code": 254, "label": "þ" }
]
},
"u": {
"relevant": [
{ "code": 250, "label": "ú" },
{ "code": 363, "label": "ū" },
{ "code": 251, "label": "û" },
{ "code": 252, "label": "ü" },
{ "code": 249, "label": "ù" }
{ "$": "auto_text_key", "code": 250, "label": "ú" },
{ "$": "auto_text_key", "code": 363, "label": "ū" },
{ "$": "auto_text_key", "code": 251, "label": "û" },
{ "$": "auto_text_key", "code": 252, "label": "ü" },
{ "$": "auto_text_key", "code": 249, "label": "ù" }
]
},
"y": {
"relevant": [
{ "code": 253, "label": "ý" },
{ "code": 255, "label": "ÿ" }
{ "$": "auto_text_key", "code": 253, "label": "ý" },
{ "$": "auto_text_key", "code": 255, "label": "ÿ" }
]
},
"~right": {

View File

@@ -5,65 +5,66 @@
"mapping": {
"all": {
"a": {
"main": { "code": 224, "label": "à" },
"main": { "$": "auto_text_key", "code": 224, "label": "à" },
"relevant": [
{ "code": 227, "label": "ã" },
{ "code": 229, "label": "å" },
{ "code": 257, "label": "ā" },
{ "code": 170, "label": "ª" },
{ "code": 225, "label": "á" },
{ "code": 226, "label": "â" },
{ "code": 228, "label": "ä" },
{ "code": 230, "label": "æ" }
{ "$": "auto_text_key", "code": 227, "label": "ã" },
{ "$": "auto_text_key", "code": 229, "label": "å" },
{ "$": "auto_text_key", "code": 257, "label": "ā" },
{ "$": "auto_text_key", "code": 170, "label": "ª" },
{ "$": "auto_text_key", "code": 225, "label": "á" },
{ "$": "auto_text_key", "code": 226, "label": "â" },
{ "$": "auto_text_key", "code": 228, "label": "ä" },
{ "$": "auto_text_key", "code": 230, "label": "æ" }
]
},
"e": {
"main": { "code": 232, "label": "è" },
"main": { "$": "auto_text_key", "code": 232, "label": "è" },
"relevant": [
{ "code": 275, "label": "ē" },
{ "code": 281, "label": "ę" },
{ "code": 279, "label": "ė" },
{ "code": 234, "label": "ê" },
{ "code": 233, "label": "é" },
{ "code": 235, "label": "ë" }
{ "$": "auto_text_key", "code": 279, "label": "ė" },
{ "$": "auto_text_key", "code": 601, "label": "ə" },
{ "$": "auto_text_key", "code": 281, "label": "ę" },
{ "$": "auto_text_key", "code": 275, "label": "ē" },
{ "$": "auto_text_key", "code": 234, "label": "ê" },
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 235, "label": "ë" }
]
},
"i": {
"main": { "code": 236, "label": "ì" },
"main": { "$": "auto_text_key", "code": 236, "label": "ì" },
"relevant": [
{ "code": 299, "label": "ī" },
{ "code": 239, "label": "ï" },
{ "code": 303, "label": "į" },
{ "code": 238, "label": "î" },
{ "code": 237, "label": "í" }
{ "$": "auto_text_key", "code": 299, "label": "ī" },
{ "$": "auto_text_key", "code": 239, "label": "ï" },
{ "$": "auto_text_key", "code": 303, "label": "į" },
{ "$": "auto_text_key", "code": 238, "label": "î" },
{ "$": "auto_text_key", "code": 237, "label": "í" }
]
},
"n": {
"relevant": [
{ "code": 241, "label": "ñ" },
{ "code": 324, "label": "ń" }
{ "$": "auto_text_key", "code": 241, "label": "ñ" },
{ "$": "auto_text_key", "code": 324, "label": "ń" }
]
},
"o": {
"main": { "code": 242, "label": "ò" },
"main": { "$": "auto_text_key", "code": 242, "label": "ò" },
"relevant": [
{ "code": 186, "label": "º" },
{ "code": 333, "label": "ō" },
{ "code": 339, "label": "œ" },
{ "code": 248, "label": "ø" },
{ "code": 245, "label": "õ" },
{ "code": 246, "label": "ö" },
{ "code": 244, "label": "ô" },
{ "code": 243, "label": "ó" }
{ "$": "auto_text_key", "code": 186, "label": "º" },
{ "$": "auto_text_key", "code": 333, "label": "ō" },
{ "$": "auto_text_key", "code": 339, "label": "œ" },
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 245, "label": "õ" },
{ "$": "auto_text_key", "code": 246, "label": "ö" },
{ "$": "auto_text_key", "code": 244, "label": "ô" },
{ "$": "auto_text_key", "code": 243, "label": "ó" }
]
},
"u": {
"main": { "code": 249, "label": "ù" },
"main": { "$": "auto_text_key", "code": 249, "label": "ù" },
"relevant": [
{ "code": 363, "label": "ū" },
{ "code": 251, "label": "û" },
{ "code": 250, "label": "ú" },
{ "code": 252, "label": "ü" }
{ "$": "auto_text_key", "code": 363, "label": "ū" },
{ "$": "auto_text_key", "code": 251, "label": "û" },
{ "$": "auto_text_key", "code": 250, "label": "ú" },
{ "$": "auto_text_key", "code": 252, "label": "ü" }
]
},
"~right": {

View File

@@ -6,94 +6,91 @@
"all": {
"a": {
"relevant": [
{ "code": 229, "label": "å" },
{ "code": 225, "label": "á" },
{ "code": 226, "label": "â" },
{ "code": 227, "label": "ã" },
{ "code": 257, "label": "ā" },
{ "code": 230, "label": "æ" },
{ "code": 228, "label": "ä" },
{ "code": 224, "label": "à" }
{ "$": "auto_text_key", "code": 229, "label": "å" },
{ "$": "auto_text_key", "code": 225, "label": "á" },
{ "$": "auto_text_key", "code": 226, "label": "â" },
{ "$": "auto_text_key", "code": 227, "label": "ã" },
{ "$": "auto_text_key", "code": 257, "label": "ā" },
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 228, "label": "ä" },
{ "$": "auto_text_key", "code": 224, "label": "à" }
]
},
"c": {
"main": { "code": 231, "label": "ç" },
"main": { "$": "auto_text_key", "code": 231, "label": "ç" },
"relevant": [
{ "code": 269, "label": "č" },
{ "code": 265, "label": "ĉ" },
{ "code": 263, "label": "ć" }
{ "$": "auto_text_key", "code": 269, "label": "č" },
{ "$": "auto_text_key", "code": 265, "label": "ĉ" },
{ "$": "auto_text_key", "code": 263, "label": "ć" }
]
},
"e": {
"relevant": [
{ "code": 233, "label": "é" },
{ "code": 275, "label": "ē" },
{ "code": 281, "label": "ę" },
{ "code": 279, "label": "ė" },
{ "code": 234, "label": "ê" },
{ "code": 232, "label": "è" },
{ "code": 235, "label": "ë" }
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 275, "label": "ē" },
{ "$": "auto_text_key", "code": 281, "label": "ę" },
{ "$": "auto_text_key", "code": 279, "label": "ė" },
{ "$": "auto_text_key", "code": 234, "label": "ê" },
{ "$": "auto_text_key", "code": 232, "label": "è" },
{ "$": "auto_text_key", "code": 235, "label": "ë" }
]
},
"r": {
"main": { "code": 345, "label": "ř" }
"main": { "$": "auto_text_key", "code": 345, "label": "ř" }
},
"g": {
"main": { "code": 285, "label": "ĝ" }
"main": { "$": "auto_text_key", "code": 285, "label": "ĝ" }
},
"h": {
"main": { "code": 293, "label": "ĥ" }
}
},
"main": { "$": "auto_text_key", "code": 293, "label": "ĥ" }
},
"j": {
"main": { "code": 309, "label": "ĵ" }
"main": { "$": "auto_text_key", "code": 309, "label": "ĵ" }
},
"n": {
"relevant": [
{ "code": 328, "label": "ň" },
{ "code": 241, "label": "ñ" }
{ "$": "auto_text_key", "code": 328, "label": "ň" },
{ "$": "auto_text_key", "code": 241, "label": "ñ" }
]
},
"o": {
"main": { "code": 246, "label": "ö" },
"main": { "$": "auto_text_key", "code": 246, "label": "ö" },
"relevant": [
{ "code": 333, "label": "ō" },
{ "code": 248, "label": "ø" },
{ "code": 243, "label": "ó" },
{ "code": 245, "label": "õ" },
{ "code": 242, "label": "ò" },
{ "code": 339, "label": "œ" },
{ "code": 244, "label": "ô" }
{ "$": "auto_text_key", "code": 333, "label": "ō" },
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 243, "label": "ó" },
{ "$": "auto_text_key", "code": 245, "label": "õ" },
{ "$": "auto_text_key", "code": 242, "label": "ò" },
{ "$": "auto_text_key", "code": 339, "label": "œ" },
{ "$": "auto_text_key", "code": 244, "label": "ô" }
]
},
"s": {
"main": { "code": 219, "label": "ș" },
"main": { "$": "auto_text_key", "code": 219, "label": "ș" },
"relevant": [
{ "code": 347, "label": "ś" },
{ "code": 349, "label": "ŝ" },
{ "code": 353, "label": "š" }
{ "$": "auto_text_key", "code": 347, "label": "ś" },
{ "$": "auto_text_key", "code": 349, "label": "ŝ" },
{ "$": "auto_text_key", "code": 353, "label": "š" }
]
},
"u": {
"main": { "code": 251, "label": "û" },
"main": { "$": "auto_text_key", "code": 251, "label": "û" },
"relevant": [
{ "code": 363, "label": "ū" },
{ "code": 249, "label": "ù" },
{ "code": 250, "label": "ú" },
{ "code": 252, "label": "ü" }
{ "$": "auto_text_key", "code": 363, "label": "ū" },
{ "$": "auto_text_key", "code": 249, "label": "ù" },
{ "$": "auto_text_key", "code": 250, "label": "ú" },
{ "$": "auto_text_key", "code": 252, "label": "ü" }
]
},
"y": {
"relevant": [
{ "code": 253, "label": "ý" }
{ "$": "auto_text_key", "code": 253, "label": "ý" }
]
},
"z": {
"relevant": [
{ "code": 382, "label": "ž" }
{ "$": "auto_text_key", "code": 382, "label": "ž" }
]
},
"~right": {
@@ -130,3 +127,4 @@
}
}
}
}

View File

@@ -6,68 +6,68 @@
"all": {
"a": {
"relevant": [
{ "code": 257, "label": "ā" }
{ "$": "auto_text_key", "code": 257, "label": "ā" }
]
},
"c": {
"relevant": [
{ "code": 269, "label": "č" }
{ "$": "auto_text_key", "code": 269, "label": "č" }
]
},
"e": {
"relevant": [
{ "code": 275, "label": "ē" },
{ "code": 8364, "label": "€" }
{ "$": "auto_text_key", "code": 275, "label": "ē" },
{ "$": "auto_text_key", "code": 8364, "label": "€" }
]
},
"g": {
"relevant": [
{ "code": 291, "label": "ģ" }
{ "$": "auto_text_key", "code": 291, "label": "ģ" }
]
},
"i": {
"relevant": [
{ "code": 299, "label": "ī" }
{ "$": "auto_text_key", "code": 299, "label": "ī" }
]
},
"k": {
"relevant": [
{ "code": 311, "label": "ķ" }
{ "$": "auto_text_key", "code": 311, "label": "ķ" }
]
},
"l": {
"relevant": [
{ "code": 316, "label": "ļ" }
{ "$": "auto_text_key", "code": 316, "label": "ļ" }
]
},
"n": {
"relevant": [
{ "code": 326, "label": "ņ" }
{ "$": "auto_text_key", "code": 326, "label": "ņ" }
]
},
"o": {
"relevant": [
{ "code": 333, "label": "ō" }
{ "$": "auto_text_key", "code": 333, "label": "ō" }
]
},
"r": {
"relevant": [
{ "code": 343, "label": "ŗ" }
{ "$": "auto_text_key", "code": 343, "label": "ŗ" }
]
},
"s": {
"relevant": [
{ "code": 353, "label": "š" }
{ "$": "auto_text_key", "code": 353, "label": "š" }
]
},
"u": {
"relevant": [
{ "code": 363, "label": "ū" }
{ "$": "auto_text_key", "code": 363, "label": "ū" }
]
},
"z": {
"relevant": [
{ "code": 382, "label": "ž" }
{ "$": "auto_text_key", "code": 382, "label": "ž" }
]
},
"~right": {

View File

@@ -6,61 +6,61 @@
"all": {
"a": {
"relevant": [
{ "code": 229, "label": "å" },
{ "code": 225, "label": "á" },
{ "code": 226, "label": "â" },
{ "code": 227, "label": "ã" },
{ "code": 257, "label": "ā" },
{ "code": 230, "label": "æ" },
{ "code": 228, "label": "ä" },
{ "code": 224, "label": "à" }
{ "$": "auto_text_key", "code": 229, "label": "å" },
{ "$": "auto_text_key", "code": 225, "label": "á" },
{ "$": "auto_text_key", "code": 226, "label": "â" },
{ "$": "auto_text_key", "code": 227, "label": "ã" },
{ "$": "auto_text_key", "code": 257, "label": "ā" },
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 228, "label": "ä" },
{ "$": "auto_text_key", "code": 224, "label": "à" }
]
},
"c": {
"relevant": [
{ "code": 231, "label": "ç" }
{ "$": "auto_text_key", "code": 231, "label": "ç" }
]
},
"e": {
"relevant": [
{ "code": 233, "label": "é" },
{ "code": 275, "label": "ē" },
{ "code": 281, "label": "ę" },
{ "code": 279, "label": "ė" },
{ "code": 234, "label": "ê" },
{ "code": 232, "label": "è" },
{ "code": 235, "label": "ë" }
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 275, "label": "ē" },
{ "$": "auto_text_key", "code": 281, "label": "ę" },
{ "$": "auto_text_key", "code": 279, "label": "ė" },
{ "$": "auto_text_key", "code": 234, "label": "ê" },
{ "$": "auto_text_key", "code": 232, "label": "è" },
{ "$": "auto_text_key", "code": 235, "label": "ë" }
]
},
"o": {
"relevant": [
{ "code": 248, "label": "ø" },
{ "code": 333, "label": "ō" },
{ "code": 339, "label": "œ" },
{ "code": 243, "label": "ó" },
{ "code": 245, "label": "õ" },
{ "code": 242, "label": "ò" },
{ "code": 244, "label": "ô" },
{ "code": 246, "label": "ö" }
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 333, "label": "ō" },
{ "$": "auto_text_key", "code": 339, "label": "œ" },
{ "$": "auto_text_key", "code": 243, "label": "ó" },
{ "$": "auto_text_key", "code": 245, "label": "õ" },
{ "$": "auto_text_key", "code": 242, "label": "ò" },
{ "$": "auto_text_key", "code": 244, "label": "ô" },
{ "$": "auto_text_key", "code": 246, "label": "ö" }
]
},
"u": {
"relevant": [
{ "code": 252, "label": "ü" },
{ "code": 363, "label": "ū" },
{ "code": 249, "label": "ù" },
{ "code": 251, "label": "û" },
{ "code": 250, "label": "ú" }
{ "$": "auto_text_key", "code": 252, "label": "ü" },
{ "$": "auto_text_key", "code": 363, "label": "ū" },
{ "$": "auto_text_key", "code": 249, "label": "ù" },
{ "$": "auto_text_key", "code": 251, "label": "û" },
{ "$": "auto_text_key", "code": 250, "label": "ú" }
]
},
"æ": {
"relevant": [
{ "code": 228, "label": "ä" }
{ "$": "auto_text_key", "code": 228, "label": "ä" }
]
},
"ø": {
"relevant": [
{ "code": 246, "label": "ö" }
{ "$": "auto_text_key", "code": 246, "label": "ö" }
]
},
"~right": {

View File

@@ -6,71 +6,71 @@
"all": {
"a": {
"relevant": [
{ "code": 229, "label": "å" },
{ "code": 225, "label": "á" },
{ "code": 226, "label": "â" },
{ "code": 227, "label": "ã" },
{ "code": 257, "label": "ā" },
{ "code": 230, "label": "æ" },
{ "code": 228, "label": "ä" },
{ "code": 224, "label": "à" }
{ "$": "auto_text_key", "code": 229, "label": "å" },
{ "$": "auto_text_key", "code": 225, "label": "á" },
{ "$": "auto_text_key", "code": 226, "label": "â" },
{ "$": "auto_text_key", "code": 227, "label": "ã" },
{ "$": "auto_text_key", "code": 257, "label": "ā" },
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 228, "label": "ä" },
{ "$": "auto_text_key", "code": 224, "label": "à" }
]
},
"c": {
"relevant": [
{ "code": 231, "label": "ç" }
{ "$": "auto_text_key", "code": 231, "label": "ç" }
]
},
"e": {
"relevant": [
{ "code": 233, "label": "é" },
{ "code": 275, "label": "ē" },
{ "code": 281, "label": "ę" },
{ "code": 279, "label": "ė" },
{ "code": 234, "label": "ê" },
{ "code": 232, "label": "è" },
{ "code": 235, "label": "ë" }
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 275, "label": "ē" },
{ "$": "auto_text_key", "code": 281, "label": "ę" },
{ "$": "auto_text_key", "code": 279, "label": "ė" },
{ "$": "auto_text_key", "code": 234, "label": "ê" },
{ "$": "auto_text_key", "code": 232, "label": "è" },
{ "$": "auto_text_key", "code": 235, "label": "ë" }
]
},
"i": {
"relevant": [
{ "code": 236, "label": "ì" }
{ "$": "auto_text_key", "code": 236, "label": "ì" }
]
},
"o": {
"relevant": [
{ "code": 248, "label": "ø" },
{ "code": 333, "label": "ō" },
{ "code": 339, "label": "œ" },
{ "code": 243, "label": "ó" },
{ "code": 245, "label": "õ" },
{ "code": 242, "label": "ò" },
{ "code": 244, "label": "ô" },
{ "code": 246, "label": "ö" }
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 333, "label": "ō" },
{ "$": "auto_text_key", "code": 339, "label": "œ" },
{ "$": "auto_text_key", "code": 243, "label": "ó" },
{ "$": "auto_text_key", "code": 245, "label": "õ" },
{ "$": "auto_text_key", "code": 242, "label": "ò" },
{ "$": "auto_text_key", "code": 244, "label": "ô" },
{ "$": "auto_text_key", "code": 246, "label": "ö" }
]
},
"u": {
"relevant": [
{ "code": 252, "label": "ü" },
{ "code": 363, "label": "ū" },
{ "code": 249, "label": "ù" },
{ "code": 251, "label": "û" },
{ "code": 250, "label": "ú" }
{ "$": "auto_text_key", "code": 252, "label": "ü" },
{ "$": "auto_text_key", "code": 363, "label": "ū" },
{ "$": "auto_text_key", "code": 249, "label": "ù" },
{ "$": "auto_text_key", "code": 251, "label": "û" },
{ "$": "auto_text_key", "code": 250, "label": "ú" }
]
},
"y": {
"relevant": [
{ "code": 7923, "label": "ỳ" }
{ "$": "auto_text_key", "code": 7923, "label": "ỳ" }
]
},
"æ": {
"relevant": [
{ "code": 228, "label": "ä" }
{ "$": "auto_text_key", "code": 228, "label": "ä" }
]
},
"ø": {
"relevant": [
{ "code": 246, "label": "ö" }
{ "$": "auto_text_key", "code": 246, "label": "ö" }
]
},
"~right": {

View File

@@ -6,49 +6,49 @@
"all": {
"a": {
"relevant": [
{ "code": 261, "label": "ą" },
{ "code": 224, "label": "à" }
{ "$": "auto_text_key", "code": 261, "label": "ą" },
{ "$": "auto_text_key", "code": 224, "label": "à" }
]
},
"c": {
"relevant": [
{ "code": 263, "label": "ć" }
{ "$": "auto_text_key", "code": 263, "label": "ć" }
]
},
"e": {
"relevant": [
{ "code": 281, "label": "ę" }
{ "$": "auto_text_key", "code": 281, "label": "ę" }
]
},
"l": {
"relevant": [
{ "code": 322, "label": "ł" }
{ "$": "auto_text_key", "code": 322, "label": "ł" }
]
},
"n": {
"relevant": [
{ "code": 324, "label": "ń" }
{ "$": "auto_text_key", "code": 324, "label": "ń" }
]
},
"o": {
"relevant": [
{ "code": 243, "label": "ó" }
{ "$": "auto_text_key", "code": 243, "label": "ó" }
]
},
"s": {
"relevant": [
{ "code": 347, "label": "ś" }
{ "$": "auto_text_key", "code": 347, "label": "ś" }
]
},
"x": {
"relevant": [
{ "code": 378, "label": "ź" }
{ "$": "auto_text_key", "code": 378, "label": "ź" }
]
},
"z": {
"relevant": [
{ "code": 378, "label": "ź" },
{ "code": 380, "label": "ż" }
{ "$": "auto_text_key", "code": 378, "label": "ź" },
{ "$": "auto_text_key", "code": 380, "label": "ż" }
]
},
"~right": {

View File

@@ -6,70 +6,70 @@
"all": {
"a": {
"relevant": [
{ "code": 228, "label": "ä" },
{ "code": 229, "label": "å" },
{ "code": 230, "label": "æ" },
{ "code": 170, "label": "ª" },
{ "code": 225, "label": "á" },
{ "code": 227, "label": "ã" },
{ "code": 224, "label": "à" },
{ "code": 226, "label": "â" }
{ "$": "auto_text_key", "code": 228, "label": "ä" },
{ "$": "auto_text_key", "code": 229, "label": "å" },
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 170, "label": "ª" },
{ "$": "auto_text_key", "code": 225, "label": "á" },
{ "$": "auto_text_key", "code": 227, "label": "ã" },
{ "$": "auto_text_key", "code": 224, "label": "à" },
{ "$": "auto_text_key", "code": 226, "label": "â" }
]
},
"c": {
"relevant": [
{ "code": 269, "label": "č" },
{ "code": 231, "label": "ç" },
{ "code": 263, "label": "ć" }
{ "$": "auto_text_key", "code": 269, "label": "č" },
{ "$": "auto_text_key", "code": 231, "label": "ç" },
{ "$": "auto_text_key", "code": 263, "label": "ć" }
]
},
"e": {
"relevant": [
{ "code": 275, "label": "ē" },
{ "code": 281, "label": "ę" },
{ "code": 279, "label": "ė" },
{ "code": 235, "label": "ë" },
{ "code": 234, "label": "ê" },
{ "code": 233, "label": "é" },
{ "code": 232, "label": "è" }
{ "$": "auto_text_key", "code": 275, "label": "ē" },
{ "$": "auto_text_key", "code": 281, "label": "ę" },
{ "$": "auto_text_key", "code": 279, "label": "ė" },
{ "$": "auto_text_key", "code": 235, "label": "ë" },
{ "$": "auto_text_key", "code": 234, "label": "ê" },
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 232, "label": "è" }
]
},
"i": {
"relevant": [
{ "code": 299, "label": "ī" },
{ "code": 239, "label": "ï" },
{ "code": 303, "label": "į" },
{ "code": 236, "label": "ì" },
{ "code": 238, "label": "î" },
{ "code": 237, "label": "í" }
{ "$": "auto_text_key", "code": 299, "label": "ī" },
{ "$": "auto_text_key", "code": 239, "label": "ï" },
{ "$": "auto_text_key", "code": 303, "label": "į" },
{ "$": "auto_text_key", "code": 236, "label": "ì" },
{ "$": "auto_text_key", "code": 238, "label": "î" },
{ "$": "auto_text_key", "code": 237, "label": "í" }
]
},
"n": {
"relevant": [
{ "code": 241, "label": "ñ" },
{ "code": 324, "label": "ń" }
{ "$": "auto_text_key", "code": 241, "label": "ñ" },
{ "$": "auto_text_key", "code": 324, "label": "ń" }
]
},
"o": {
"relevant": [
{ "code": 186, "label": "º" },
{ "code": 333, "label": "ō" },
{ "code": 248, "label": "ø" },
{ "code": 339, "label": "œ" },
{ "code": 246, "label": "ö" },
{ "code": 242, "label": "ò" },
{ "code": 244, "label": "ô" },
{ "code": 245, "label": "õ" },
{ "code": 243, "label": "ó" }
{ "$": "auto_text_key", "code": 186, "label": "º" },
{ "$": "auto_text_key", "code": 333, "label": "ō" },
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 339, "label": "œ" },
{ "$": "auto_text_key", "code": 246, "label": "ö" },
{ "$": "auto_text_key", "code": 242, "label": "ò" },
{ "$": "auto_text_key", "code": 244, "label": "ô" },
{ "$": "auto_text_key", "code": 245, "label": "õ" },
{ "$": "auto_text_key", "code": 243, "label": "ó" }
]
},
"u": {
"relevant": [
{ "code": 363, "label": "ū" },
{ "code": 249, "label": "ù" },
{ "code": 251, "label": "û" },
{ "code": 252, "label": "ü" },
{ "code": 250, "label": "ú" }
{ "$": "auto_text_key", "code": 363, "label": "ū" },
{ "$": "auto_text_key", "code": 249, "label": "ù" },
{ "$": "auto_text_key", "code": 251, "label": "û" },
{ "$": "auto_text_key", "code": 252, "label": "ü" },
{ "$": "auto_text_key", "code": 250, "label": "ú" }
]
},
"~right": {

View File

@@ -6,70 +6,70 @@
"all": {
"a": {
"relevant": [
{ "code": 228, "label": "ä" },
{ "code": 229, "label": "å" },
{ "code": 230, "label": "æ" },
{ "code": 170, "label": "ª" },
{ "code": 225, "label": "á" },
{ "code": 227, "label": "ã" },
{ "code": 224, "label": "à" },
{ "code": 226, "label": "â" }
{ "$": "auto_text_key", "code": 228, "label": "ä" },
{ "$": "auto_text_key", "code": 229, "label": "å" },
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 170, "label": "ª" },
{ "$": "auto_text_key", "code": 225, "label": "á" },
{ "$": "auto_text_key", "code": 227, "label": "ã" },
{ "$": "auto_text_key", "code": 224, "label": "à" },
{ "$": "auto_text_key", "code": 226, "label": "â" }
]
},
"c": {
"relevant": [
{ "code": 231, "label": "ç" },
{ "code": 263, "label": "ć" },
{ "code": 269, "label": "č" }
{ "$": "auto_text_key", "code": 231, "label": "ç" },
{ "$": "auto_text_key", "code": 263, "label": "ć" },
{ "$": "auto_text_key", "code": 269, "label": "č" }
]
},
"e": {
"relevant": [
{ "code": 235, "label": "ë" },
{ "code": 279, "label": "ė" },
{ "code": 275, "label": "ē" },
{ "code": 233, "label": "é" },
{ "code": 232, "label": "è" },
{ "code": 234, "label": "ê" },
{ "code": 281, "label": "ę" }
{ "$": "auto_text_key", "code": 235, "label": "ë" },
{ "$": "auto_text_key", "code": 279, "label": "ė" },
{ "$": "auto_text_key", "code": 275, "label": "ē" },
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 232, "label": "è" },
{ "$": "auto_text_key", "code": 234, "label": "ê" },
{ "$": "auto_text_key", "code": 281, "label": "ę" }
]
},
"i": {
"relevant": [
{ "code": 299, "label": "ī" },
{ "code": 239, "label": "ï" },
{ "code": 303, "label": "į" },
{ "code": 236, "label": "ì" },
{ "code": 237, "label": "í" },
{ "code": 238, "label": "î" }
{ "$": "auto_text_key", "code": 299, "label": "ī" },
{ "$": "auto_text_key", "code": 239, "label": "ï" },
{ "$": "auto_text_key", "code": 303, "label": "į" },
{ "$": "auto_text_key", "code": 236, "label": "ì" },
{ "$": "auto_text_key", "code": 237, "label": "í" },
{ "$": "auto_text_key", "code": 238, "label": "î" }
]
},
"n": {
"relevant": [
{ "code": 241, "label": "ñ" },
{ "code": 324, "label": "ń" }
{ "$": "auto_text_key", "code": 241, "label": "ñ" },
{ "$": "auto_text_key", "code": 324, "label": "ń" }
]
},
"o": {
"relevant": [
{ "code": 186, "label": "º" },
{ "code": 333, "label": "ō" },
{ "code": 248, "label": "ø" },
{ "code": 339, "label": "œ" },
{ "code": 246, "label": "ö" },
{ "code": 242, "label": "ò" },
{ "code": 244, "label": "ô" },
{ "code": 243, "label": "ó" },
{ "code": 245, "label": "õ" }
{ "$": "auto_text_key", "code": 186, "label": "º" },
{ "$": "auto_text_key", "code": 333, "label": "ō" },
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 339, "label": "œ" },
{ "$": "auto_text_key", "code": 246, "label": "ö" },
{ "$": "auto_text_key", "code": 242, "label": "ò" },
{ "$": "auto_text_key", "code": 244, "label": "ô" },
{ "$": "auto_text_key", "code": 243, "label": "ó" },
{ "$": "auto_text_key", "code": 245, "label": "õ" }
]
},
"u": {
"relevant": [
{ "code": 363, "label": "ū" },
{ "code": 249, "label": "ù" },
{ "code": 250, "label": "ú" },
{ "code": 252, "label": "ü" },
{ "code": 251, "label": "û" }
{ "$": "auto_text_key", "code": 363, "label": "ū" },
{ "$": "auto_text_key", "code": 249, "label": "ù" },
{ "$": "auto_text_key", "code": 250, "label": "ú" },
{ "$": "auto_text_key", "code": 252, "label": "ü" },
{ "$": "auto_text_key", "code": 251, "label": "û" }
]
},
"~right": {

View File

@@ -5,19 +5,19 @@
"mapping": {
"all": {
"a": {
"main": { "code": 259, "label": "ă" },
"main": { "$": "auto_text_key", "code": 259, "label": "ă" },
"relevant": [
{ "code": 226, "label": "â" }
{ "$": "auto_text_key", "code": 226, "label": "â" }
]
},
"i": {
"main": { "code": 238, "label": "î" }
"main": { "$": "auto_text_key", "code": 238, "label": "î" }
},
"s": {
"main": {"code": 537, "label": "ș"}
"main": { "$": "auto_text_key", "code": 537, "label": "ș" }
},
"t": {
"main": {"code": 539, "label": "ț"}
"main": { "$": "auto_text_key", "code": 539, "label": "ț" }
},
"~right": {
"main": { "code": 44, "label": "," },

View File

@@ -1,12 +1,17 @@
{
"type": "characters/extended_popups",
"name": "ru",
"authors": [ "williamtheaker" ],
"authors": [ "williamtheaker", "33kk" ],
"mapping": {
"all": {
"е": {
"relevant": [
{ "code": 1105, "label": "ё" }
{ "$": "auto_text_key", "code": 1105, "label": "ё" }
]
},
"ь": {
"relevant": [
{ "$": "auto_text_key", "code": 1098, "label": "ъ" }
]
},
"~right": {

View File

@@ -0,0 +1,116 @@
{
"type": "characters/extended_popups",
"name": "sk",
"authors": [ "stefan-misik", "majso" ],
"mapping": {
"all": {
"e": {
"relevant": [
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 283, "label": "ě" }
]
},
"r": {
"relevant": [
{ "$": "auto_text_key", "code": 341, "label": "ŕ" },
{ "$": "auto_text_key", "code": 345, "label": "ř" }
]
},
"t": {
"relevant": [
{ "$": "auto_text_key", "code": 357, "label": "ť" }
]
},
"y": {
"relevant": [
{ "$": "auto_text_key", "code": 253, "label": "ý" }
]
},
"u": {
"relevant": [
{ "$": "auto_text_key", "code": 250, "label": "ú" },
{ "$": "auto_text_key", "code": 367, "label": "ů" }
]
},
"i": {
"relevant": [
{ "$": "auto_text_key", "code": 237, "label": "í" }
]
},
"o": {
"relevant": [
{ "$": "auto_text_key", "code": 243, "label": "ó" },
{ "$": "auto_text_key", "code": 244, "label": "ô" }
]
},
"a": {
"relevant": [
{ "$": "auto_text_key", "code": 225, "label": "á" },
{ "$": "auto_text_key", "code": 228, "label": "ä" }
]
},
"s": {
"relevant": [
{ "$": "auto_text_key", "code": 353, "label": "š" }
]
},
"d": {
"relevant": [
{ "$": "auto_text_key", "code": 271, "label": "ď" }
]
},
"l": {
"relevant": [
{ "$": "auto_text_key", "code": 318, "label": "ľ" },
{ "$": "auto_text_key", "code": 314, "label": "ĺ" }
]
},
"z": {
"relevant": [
{ "$": "auto_text_key", "code": 382, "label": "ž" }
]
},
"c": {
"relevant": [
{ "$": "auto_text_key", "code": 269, "label": "č" }
]
},
"n": {
"relevant": [
{ "$": "auto_text_key", "code": 328, "label": "ň" }
]
},
"~right": {
"main": { "code": 44, "label": "," },
"relevant": [
{ "code": 38, "label": "&" },
{ "code": 37, "label": "%" },
{ "code": 43, "label": "+" },
{ "code": 34, "label": "\"" },
{ "code": 45, "label": "-" },
{ "code": 58, "label": ":" },
{ "code": 39, "label": "'" },
{ "code": 64, "label": "@" },
{ "code": 59, "label": ";" },
{ "code": 47, "label": "/" },
{ "code": 40, "label": "(" },
{ "code": 41, "label": ")" },
{ "code": 35, "label": "#" },
{ "code": 33, "label": "!" },
{ "code": 63, "label": "?" }
]
}
},
"uri": {
"~right": {
"main": { "code": -255, "label": ".com" },
"relevant": [
{ "code": -255, "label": ".sk" },
{ "code": -255, "label": ".eu" },
{ "code": -255, "label": ".org" },
{ "code": -255, "label": ".net" }
]
}
}
}
}

View File

@@ -6,22 +6,22 @@
"all": {
"c": {
"relevant": [
{ "code": 269, "label": "č" },
{ "code": 263, "label": "ć" }
{ "$": "auto_text_key", "code": 269, "label": "č" },
{ "$": "auto_text_key", "code": 263, "label": "ć" }
]
},
"d": {
"relevant": [
{ "code": 273, "label": "đ" }
{ "$": "auto_text_key", "code": 273, "label": "đ" }
]
},
"s": {
"relevant": [
{ "code": 353, "label": "š" }
{ "$": "auto_text_key", "code": 353, "label": "š" }
]
},
"z": {
"main": { "code": 382, "label": "ž" }
"main": { "$": "auto_text_key", "code": 382, "label": "ž" }
},
"~right": {
"main": { "code": 44, "label": "," },

View File

@@ -6,122 +6,122 @@
"all": {
"a": {
"relevant": [
{ "code": 228, "label": "ä" },
{ "code": 224, "label": "à" },
{ "code": 226, "label": "â" },
{ "code": 261, "label": "ą" },
{ "code": 227, "label": "ã" },
{ "code": 229, "label": "å" },
{ "code": 230, "label": "æ" },
{ "code": 225, "label": "á" }
{ "$": "auto_text_key", "code": 228, "label": "ä" },
{ "$": "auto_text_key", "code": 224, "label": "à" },
{ "$": "auto_text_key", "code": 226, "label": "â" },
{ "$": "auto_text_key", "code": 261, "label": "ą" },
{ "$": "auto_text_key", "code": 227, "label": "ã" },
{ "$": "auto_text_key", "code": 229, "label": "å" },
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 225, "label": "á" }
]
},
"c": {
"relevant": [
{ "code": 231, "label": "ç" },
{ "code": 269, "label": "č" },
{ "code": 263, "label": "ć" }
{ "$": "auto_text_key", "code": 231, "label": "ç" },
{ "$": "auto_text_key", "code": 269, "label": "č" },
{ "$": "auto_text_key", "code": 263, "label": "ć" }
]
},
"d": {
"relevant": [
{ "code": 240, "label": "ð" },
{ "code": 271, "label": "ď" }
{ "$": "auto_text_key", "code": 240, "label": "ð" },
{ "$": "auto_text_key", "code": 271, "label": "ď" }
]
},
"e": {
"relevant": [
{ "code": 233, "label": "é" },
{ "code": 234, "label": "ê" },
{ "code": 232, "label": "è" },
{ "code": 235, "label": "ë" },
{ "code": 281, "label": "ę" }
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 234, "label": "ê" },
{ "$": "auto_text_key", "code": 232, "label": "è" },
{ "$": "auto_text_key", "code": 235, "label": "ë" },
{ "$": "auto_text_key", "code": 281, "label": "ę" }
]
},
"i": {
"relevant": [
{ "code": 237, "label": "í" },
{ "code": 239, "label": "ï" },
{ "code": 299, "label": "ī" },
{ "code": 303, "label": "į" },
{ "code": 238, "label": "î" },
{ "code": 236, "label": "ì" }
{ "$": "auto_text_key", "code": 237, "label": "í" },
{ "$": "auto_text_key", "code": 239, "label": "ï" },
{ "$": "auto_text_key", "code": 299, "label": "ī" },
{ "$": "auto_text_key", "code": 303, "label": "į" },
{ "$": "auto_text_key", "code": 238, "label": "î" },
{ "$": "auto_text_key", "code": 236, "label": "ì" }
]
},
"l": {
"relevant": [
{ "code": 322, "label": "ł" }
{ "$": "auto_text_key", "code": 322, "label": "ł" }
]
},
"n": {
"relevant": [
{ "code": 324, "label": "ń" },
{ "code": 328, "label": "ň" },
{ "code": 241, "label": "ñ" }
{ "$": "auto_text_key", "code": 324, "label": "ń" },
{ "$": "auto_text_key", "code": 328, "label": "ň" },
{ "$": "auto_text_key", "code": 241, "label": "ñ" }
]
},
"o": {
"relevant": [
{ "code": 246, "label": "ö" },
{ "code": 333, "label": "ō" },
{ "code": 245, "label": "õ" },
{ "code": 242, "label": "ò" },
{ "code": 244, "label": "ô" },
{ "code": 243, "label": "ó" },
{ "code": 339, "label": "œ" },
{ "code": 248, "label": "ø" }
{ "$": "auto_text_key", "code": 246, "label": "ö" },
{ "$": "auto_text_key", "code": 333, "label": "ō" },
{ "$": "auto_text_key", "code": 245, "label": "õ" },
{ "$": "auto_text_key", "code": 242, "label": "ò" },
{ "$": "auto_text_key", "code": 244, "label": "ô" },
{ "$": "auto_text_key", "code": 243, "label": "ó" },
{ "$": "auto_text_key", "code": 339, "label": "œ" },
{ "$": "auto_text_key", "code": 248, "label": "ø" }
]
},
"r": {
"relevant": [
{ "code": 345, "label": "ř" }
{ "$": "auto_text_key", "code": 345, "label": "ř" }
]
},
"s": {
"relevant": [
{ "code": 347, "label": "ś" },
{ "code": 353, "label": "š" },
{ "code": 351, "label": "ş" },
{ "code": 223, "label": "ß" }
{ "$": "auto_text_key", "code": 347, "label": "ś" },
{ "$": "auto_text_key", "code": 353, "label": "š" },
{ "$": "auto_text_key", "code": 351, "label": "ş" },
{ "$": "auto_text_key", "code": 223, "label": "ß" }
]
},
"t": {
"relevant": [
{ "code": 357, "label": "ť" },
{ "code": 254, "label": "þ" }
{ "$": "auto_text_key", "code": 357, "label": "ť" },
{ "$": "auto_text_key", "code": 254, "label": "þ" }
]
},
"u": {
"relevant": [
{ "code": 252, "label": "ü" },
{ "code": 363, "label": "ū" },
{ "code": 249, "label": "ù" },
{ "code": 250, "label": "ú" },
{ "code": 251, "label": "û" }
{ "$": "auto_text_key", "code": 252, "label": "ü" },
{ "$": "auto_text_key", "code": 363, "label": "ū" },
{ "$": "auto_text_key", "code": 249, "label": "ù" },
{ "$": "auto_text_key", "code": 250, "label": "ú" },
{ "$": "auto_text_key", "code": 251, "label": "û" }
]
},
"y": {
"relevant": [
{ "code": 253, "label": "ý" },
{ "code": 255, "label": "ÿ" }
{ "$": "auto_text_key", "code": 253, "label": "ý" },
{ "$": "auto_text_key", "code": 255, "label": "ÿ" }
]
},
"z": {
"relevant": [
{ "code": 378, "label": "ź" },
{ "code": 380, "label": "ż" },
{ "code": 382, "label": "ž" }
{ "$": "auto_text_key", "code": 378, "label": "ź" },
{ "$": "auto_text_key", "code": 380, "label": "ż" },
{ "$": "auto_text_key", "code": 382, "label": "ž" }
]
},
"ä": {
"relevant": [
{ "code": 230, "label": "æ" }
{ "$": "auto_text_key", "code": 230, "label": "æ" }
]
},
"ö": {
"relevant": [
{ "code": 248, "label": "ø" },
{ "code": 339, "label": "œ" }
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 339, "label": "œ" }
]
},
"~right": {

View File

@@ -6,93 +6,93 @@
"all": {
"a": {
"relevant": [
{ "code": 226, "label": "â" },
{ "code": 228, "label": "ä" },
{ "code": 225, "label": "á" }
{ "$": "auto_text_key", "code": 226, "label": "â" },
{ "$": "auto_text_key", "code": 228, "label": "ä" },
{ "$": "auto_text_key", "code": 225, "label": "á" }
]
},
"c": {
"main": { "code": 231, "label": "ç" },
"main": { "$": "auto_text_key", "code": 231, "label": "ç" },
"relevant": [
{ "code": 269, "label": "č" },
{ "code": 263, "label": "ć" }
{ "$": "auto_text_key", "code": 269, "label": "č" },
{ "$": "auto_text_key", "code": 263, "label": "ć" }
]
},
"e": {
"relevant": [
{ "code": 233, "label": "é" },
{ "code": 601, "label": "ə" },
{ "code": 234, "label": "ê" }
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 601, "label": "ə" },
{ "$": "auto_text_key", "code": 234, "label": "ê" }
]
},
"g": {
"main": { "code": 287, "label": "ğ" }
"main": { "$": "auto_text_key", "code": 287, "label": "ğ" }
},
"i": {
"main": { "code": 305, "label": "ı" },
"main": { "$": "auto_text_key", "code": 305, "label": "ı" },
"relevant": [
{ "code": 303, "label": "į" },
{ "code": 236, "label": "ì" },
{ "code": 237, "label": "í" },
{ "code": 299, "label": "ī" },
{ "code": 238, "label": "î" },
{ "code": 239, "label": "ï" }
{ "$": "auto_text_key", "code": 303, "label": "į" },
{ "$": "auto_text_key", "code": 236, "label": "ì" },
{ "$": "auto_text_key", "code": 237, "label": "í" },
{ "$": "auto_text_key", "code": 299, "label": "ī" },
{ "$": "auto_text_key", "code": 238, "label": "î" },
{ "$": "auto_text_key", "code": 239, "label": "ï" }
]
},
"ı": {
"main": { "code": 105, "label": "i" },
"main": { "$": "auto_text_key", "code": 105, "label": "i" },
"relevant": [
{ "code": 303, "label": "į" },
{ "code": 236, "label": "ì" },
{ "code": 237, "label": "í" },
{ "code": 299, "label": "ī" },
{ "code": 238, "label": "î" },
{ "code": 239, "label": "ï" }
{ "$": "auto_text_key", "code": 303, "label": "į" },
{ "$": "auto_text_key", "code": 236, "label": "ì" },
{ "$": "auto_text_key", "code": 237, "label": "í" },
{ "$": "auto_text_key", "code": 299, "label": "ī" },
{ "$": "auto_text_key", "code": 238, "label": "î" },
{ "$": "auto_text_key", "code": 239, "label": "ï" }
]
},
"n": {
"relevant": [
{ "code": 328, "label": "ň" },
{ "code": 241, "label": "ñ" }
{ "$": "auto_text_key", "code": 328, "label": "ň" },
{ "$": "auto_text_key", "code": 241, "label": "ñ" }
]
},
"o": {
"main": { "code": 246, "label": "ö" },
"main": { "$": "auto_text_key", "code": 246, "label": "ö" },
"relevant": [
{ "code": 333, "label": "ō" },
{ "code": 248, "label": "ø" },
{ "code": 243, "label": "ó" },
{ "code": 245, "label": "õ" },
{ "code": 242, "label": "ò" },
{ "code": 339, "label": "œ" },
{ "code": 244, "label": "ô" }
{ "$": "auto_text_key", "code": 333, "label": "ō" },
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 243, "label": "ó" },
{ "$": "auto_text_key", "code": 245, "label": "õ" },
{ "$": "auto_text_key", "code": 242, "label": "ò" },
{ "$": "auto_text_key", "code": 339, "label": "œ" },
{ "$": "auto_text_key", "code": 244, "label": "ô" }
]
},
"s": {
"main": { "code": 351, "label": "ş" },
"main": { "$": "auto_text_key", "code": 351, "label": "ş" },
"relevant": [
{ "code": 347, "label": "ś" },
{ "code": 223, "label": "ß" },
{ "code": 353, "label": "š" }
{ "$": "auto_text_key", "code": 347, "label": "ś" },
{ "$": "auto_text_key", "code": 223, "label": "ß" },
{ "$": "auto_text_key", "code": 353, "label": "š" }
]
},
"u": {
"main": { "code": 252, "label": "ü" },
"main": { "$": "auto_text_key", "code": 252, "label": "ü" },
"relevant": [
{ "code": 363, "label": "ū" },
{ "code": 249, "label": "ù" },
{ "code": 250, "label": "ú" },
{ "code": 251, "label": "û" }
{ "$": "auto_text_key", "code": 363, "label": "ū" },
{ "$": "auto_text_key", "code": 249, "label": "ù" },
{ "$": "auto_text_key", "code": 250, "label": "ú" },
{ "$": "auto_text_key", "code": 251, "label": "û" }
]
},
"y": {
"relevant": [
{ "code": 253, "label": "ý" }
{ "$": "auto_text_key", "code": 253, "label": "ý" }
]
},
"z": {
"relevant": [
{ "code": 382, "label": "ž" }
{ "$": "auto_text_key", "code": 382, "label": "ž" }
]
},
"~right": {

View File

@@ -0,0 +1,45 @@
{
"type": "characters/extended_popups",
"name": "uk",
"authors": [ "williamtheaker", "33kk" ],
"mapping": {
"all": {
"і": {
"relevant": [
{ "$": "auto_text_key", "code": 1111, "label": "ї" }
]
},
"~right": {
"main": { "code": 44, "label": "," },
"relevant": [
{ "code": 38, "label": "&" },
{ "code": 37, "label": "%" },
{ "code": 43, "label": "+" },
{ "code": 34, "label": "\"" },
{ "code": 45, "label": "-" },
{ "code": 58, "label": ":" },
{ "code": 39, "label": "'" },
{ "code": 64, "label": "@" },
{ "code": 59, "label": ";" },
{ "code": 47, "label": "/" },
{ "code": 40, "label": "(" },
{ "code": 41, "label": ")" },
{ "code": 35, "label": "#" },
{ "code": 33, "label": "!" },
{ "code": 63, "label": "?" }
]
}
},
"uri": {
"~right": {
"main": { "code": -255, "label": ".com" },
"relevant": [
{ "code": -255, "label": ".ua" },
{ "code": -255, "label": ".edu" },
{ "code": -255, "label": ".org" },
{ "code": -255, "label": ".net" }
]
}
}
}
}

View File

@@ -6,39 +6,39 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 121, "label": "y" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" },
{ "code": 240, "label": "ð" }
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 240, "label": "ð" }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" },
{ "code": 230, "label": "æ" },
{ "code": 248, "label": "ø" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 248, "label": "ø" }
],
[
{ "code": 122, "label": "z" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" }
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" }
]
]
}

View File

@@ -6,41 +6,43 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 122, "label": "z" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" },
{ "code": 252, "label": "ü" }
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 252, "label": "ü" }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" },
{ "code": 246, "label": "ö" },
{ "code": 228, "label": "ä" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 246, "label": "ö" },
{ "$": "auto_text_key", "code": 228, "label": "ä" }
],
[
{ "code": 121, "label": "y" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" },
{ "code": 223, "label": "ß", "popup": {
}, "shift": { "code": 7838, "label": "" } }
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" },
{ "$": "case_selector",
"lower": { "code": 223, "label": "ß" },
"upper": { "code": 7838, "label": "ẞ" }
}
]
]
}

View File

@@ -6,36 +6,36 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 894, "label": ";" },
{ "code": 962, "label": "ς" },
{ "code": 949, "label": "ε" },
{ "code": 961, "label": "ρ" },
{ "code": 964, "label": "τ" },
{ "code": 965, "label": "υ" },
{ "code": 952, "label": "θ" },
{ "code": 953, "label": "ι" },
{ "code": 959, "label": "ο" },
{ "code": 960, "label": "π" }
{ "$": "auto_text_key", "code": 894, "label": ";" },
{ "$": "auto_text_key", "code": 962, "label": "ς" },
{ "$": "auto_text_key", "code": 949, "label": "ε" },
{ "$": "auto_text_key", "code": 961, "label": "ρ" },
{ "$": "auto_text_key", "code": 964, "label": "τ" },
{ "$": "auto_text_key", "code": 965, "label": "υ" },
{ "$": "auto_text_key", "code": 952, "label": "θ" },
{ "$": "auto_text_key", "code": 953, "label": "ι" },
{ "$": "auto_text_key", "code": 959, "label": "ο" },
{ "$": "auto_text_key", "code": 960, "label": "π" }
],
[
{ "code": 945, "label": "α" },
{ "code": 963, "label": "σ" },
{ "code": 948, "label": "δ" },
{ "code": 966, "label": "φ" },
{ "code": 947, "label": "γ" },
{ "code": 951, "label": "η" },
{ "code": 958, "label": "ξ" },
{ "code": 954, "label": "κ" },
{ "code": 955, "label": "λ" }
{ "$": "auto_text_key", "code": 945, "label": "α" },
{ "$": "auto_text_key", "code": 963, "label": "σ" },
{ "$": "auto_text_key", "code": 948, "label": "δ" },
{ "$": "auto_text_key", "code": 966, "label": "φ" },
{ "$": "auto_text_key", "code": 947, "label": "γ" },
{ "$": "auto_text_key", "code": 951, "label": "η" },
{ "$": "auto_text_key", "code": 958, "label": "ξ" },
{ "$": "auto_text_key", "code": 954, "label": "κ" },
{ "$": "auto_text_key", "code": 955, "label": "λ" }
],
[
{ "code": 950, "label": "ζ" },
{ "code": 967, "label": "χ" },
{ "code": 968, "label": "ψ" },
{ "code": 969, "label": "ω" },
{ "code": 946, "label": "β" },
{ "code": 957, "label": "ν" },
{ "code": 956, "label": "μ" }
{ "$": "auto_text_key", "code": 950, "label": "ζ" },
{ "$": "auto_text_key", "code": 967, "label": "χ" },
{ "$": "auto_text_key", "code": 968, "label": "ψ" },
{ "$": "auto_text_key", "code": 969, "label": "ω" },
{ "$": "auto_text_key", "code": 946, "label": "β" },
{ "$": "auto_text_key", "code": 957, "label": "ν" },
{ "$": "auto_text_key", "code": 956, "label": "μ" }
]
]
}

View File

@@ -1,41 +1,45 @@
{
"type": "characters",
"name": "hungarian",
"label": "Hungarian (QWERTZ)",
"authors": [ "zoli111, gabik65" ],
"label": "Hungarian",
"authors": [ "zoli111, gabik65", "patrickgold" ],
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 122, "label": "z" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" }
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 246, "label": "ö" }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 233, "label": "é" },
{ "$": "auto_text_key", "code": 225, "label": "á" }
],
[
{ "code": 121, "label": "y" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" }
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" },
{ "$": "auto_text_key", "code": 252, "label": "ü" }
]
]
}

View File

@@ -6,40 +6,40 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 121, "label": "y" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" },
{ "code": 240, "label": "ð" }
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 240, "label": "ð" }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" },
{ "code": 230, "label": "æ" },
{ "code": 246, "label": "ö" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 246, "label": "ö" }
],
[
{ "code": 122, "label": "z" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" },
{ "code": 254, "label": "þ" }
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" },
{ "$": "auto_text_key", "code": 254, "label": "þ" }
]
]
}

View File

@@ -2,281 +2,279 @@
"type": "characters",
"name": "ipa",
"label": "International Phonetic Alphabet",
"authors": [
"Huy-Ngo"
],
"authors": [ "Huy-Ngo" ],
"direction": "ltr",
"arrangement": [
[
{
"code": 113, "label": "q"
"$": "auto_text_key", "code": 113, "label": "q"
},
{
"code": 119, "label": "w",
"$": "auto_text_key", "code": 119, "label": "w",
"popup": {
"relevant": [
{ "code": 695, "label": "◌ʷ" },
{ "code": 653, "label": "ʍ" }
{ "$": "auto_text_key", "code": 695, "label": "◌ʷ" },
{ "$": "auto_text_key", "code": 653, "label": "ʍ" }
]
}
},
{
"code": 101, "label": "e",
"$": "auto_text_key", "code": 101, "label": "e",
"popup": {
"relevant": [
{ "code": 600, "label": "ɘ" },
{ "code": 604, "label": "ɜ" },
{ "code": 601, "label": "ə" },
{ "code": 602, "label": "ɚ" },
{ "code": 7498, "label": "◌ᵊ" },
{ "code": 603, "label": "ɛ" }
{ "$": "auto_text_key", "code": 600, "label": "ɘ" },
{ "$": "auto_text_key", "code": 604, "label": "ɜ" },
{ "$": "auto_text_key", "code": 601, "label": "ə" },
{ "$": "auto_text_key", "code": 602, "label": "ɚ" },
{ "$": "auto_text_key", "code": 7498, "label": "◌ᵊ" },
{ "$": "auto_text_key", "code": 603, "label": "ɛ" }
]
}
},
{
"code": 114, "label": "r",
"$": "auto_text_key", "code": 114, "label": "r",
"popup": {
"relevant": [
{ "code": 637, "label": "ɽ" },
{ "code": 633, "label": "ɹ" },
{ "code": 638, "label": "ɾ" },
{ "code": 635, "label": "ɻ" },
{ "code": 641, "label": "ʁ" },
{ "code": 734, "label": "◌˞" },
{ "code": 640, "label": "ʀ" }
{ "$": "auto_text_key", "code": 637, "label": "ɽ" },
{ "$": "auto_text_key", "code": 633, "label": "ɹ" },
{ "$": "auto_text_key", "code": 638, "label": "ɾ" },
{ "$": "auto_text_key", "code": 635, "label": "ɻ" },
{ "$": "auto_text_key", "code": 641, "label": "ʁ" },
{ "$": "auto_text_key", "code": 734, "label": "◌˞" },
{ "$": "auto_text_key", "code": 640, "label": "ʀ" }
]
}
},
{
"code": 116, "label": "t",
"$": "auto_text_key", "code": 116, "label": "t",
"popup": {
"relevant": [
{ "code": 648, "label": "ʈ" },
{ "code": 7615, "label": "◌ᶿ" },
{ "code": 952, "label": "θ" }
{ "$": "auto_text_key", "code": 648, "label": "ʈ" },
{ "$": "auto_text_key", "code": 7615, "label": "◌ᶿ" },
{ "$": "auto_text_key", "code": 952, "label": "θ" }
]
}
},
{
"code": 121, "label": "y",
"$": "auto_text_key", "code": 121, "label": "y",
"popup": {
"relevant": [
{ "code": 612, "label": "ɤ" },
{ "code": 655, "label": "ʏ" }
{ "$": "auto_text_key", "code": 612, "label": "ɤ" },
{ "$": "auto_text_key", "code": 655, "label": "ʏ" }
]
}
},
{
"code": 117, "label": "u",
"$": "auto_text_key", "code": 117, "label": "u",
"popup": {
"relevant": [
{ "code": 7551, "label": "ᵿ" },
{ "code": 649, "label": "ʉ" },
{ "code": 650, "label": "ʊ" }
{ "$": "auto_text_key", "code": 7551, "label": "ᵿ" },
{ "$": "auto_text_key", "code": 649, "label": "ʉ" },
{ "$": "auto_text_key", "code": 650, "label": "ʊ" }
]
}
},
{
"code": 105, "label": "i",
"$": "auto_text_key", "code": 105, "label": "i",
"popup": {
"relevant": [
{ "code": 7574, "label": "ᵻ" },
{ "code": 616, "label": "ɨ" },
{ "code": 618, "label": "ɪ" }
{ "$": "auto_text_key", "code": 7574, "label": "ᵻ" },
{ "$": "auto_text_key", "code": 616, "label": "ɨ" },
{ "$": "auto_text_key", "code": 618, "label": "ɪ" }
]
}
},
{
"code": 111, "label": "o",
"$": "auto_text_key", "code": 111, "label": "o",
"popup": {
"relevant": [
{ "code": 664, "label": "ʘ" },
{ "code": 248, "label": "ø" },
{ "code": 606, "label": "ɞ" },
{ "code": 339, "label": "œ" },
{ "code": 629, "label": "ɵ" },
{ "code": 630, "label": "ɶ" },
{ "code": 596, "label": "ɔ" }
{ "$": "auto_text_key", "code": 664, "label": "ʘ" },
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 606, "label": "ɞ" },
{ "$": "auto_text_key", "code": 339, "label": "œ" },
{ "$": "auto_text_key", "code": 629, "label": "ɵ" },
{ "$": "auto_text_key", "code": 630, "label": "ɶ" },
{ "$": "auto_text_key", "code": 596, "label": "ɔ" }
]
}
},
{
"code": 112, "label": "p"
"$": "auto_text_key", "code": 112, "label": "p"
}
],
[
{
"code": 97, "label": "a",
"$": "auto_text_key", "code": 97, "label": "a",
"popup": {
"relevant": [
{ "code": 230, "label": "æ" },
{ "code": 594, "label": "ɒ" },
{ "code": 592, "label": "ɐ" },
{ "code": 593, "label": "ɑ" }
{ "$": "auto_text_key", "code": 230, "label": "æ" },
{ "$": "auto_text_key", "code": 594, "label": "ɒ" },
{ "$": "auto_text_key", "code": 592, "label": "ɐ" },
{ "$": "auto_text_key", "code": 593, "label": "ɑ" }
]
}
},
{
"code": 115, "label": "s",
"$": "auto_text_key", "code": 115, "label": "s",
"popup": {
"relevant": [
{ "code": 642, "label": "ʂ" },
{ "code": 597, "label": "ɕ" },
{ "code": 643, "label": "ʃ" }
{ "$": "auto_text_key", "code": 642, "label": "ʂ" },
{ "$": "auto_text_key", "code": 597, "label": "ɕ" },
{ "$": "auto_text_key", "code": 643, "label": "ʃ" }
]
}
},
{
"code": 100, "label": "d",
"$": "auto_text_key", "code": 100, "label": "d",
"popup": {
"relevant": [
{ "code": 598, "label": "ɖ" },
{ "code": 599, "label": "ɗ" },
{ "code": 240, "label": "ð" }
{ "$": "auto_text_key", "code": 598, "label": "ɖ" },
{ "$": "auto_text_key", "code": 599, "label": "ɗ" },
{ "$": "auto_text_key", "code": 240, "label": "ð" }
]
}
},
{
"code": 102, "label": "f",
"$": "auto_text_key", "code": 102, "label": "f",
"popup": {
"relevant": [
{ "code": 632, "label": "ɸ" }
{ "$": "auto_text_key", "code": 632, "label": "ɸ" }
]
}
},
{
"code": 609, "label": "ɡ",
"$": "auto_text_key", "code": 609, "label": "ɡ",
"popup": {
"main": { "code": 103, "label": "g" },
"main": { "$": "auto_text_key", "code": 103, "label": "g" },
"relevant": [
{ "code": 608, "label": "ɠ" },
{ "code": 610, "label": "ɢ" },
{ "code": 667, "label": "ʛ" },
{ "code": 667, "label": "ʛ" },
{ "code": 736, "label": "◌ˠ" },
{ "code": 611, "label": "ɣ" }
{ "$": "auto_text_key", "code": 608, "label": "ɠ" },
{ "$": "auto_text_key", "code": 610, "label": "ɢ" },
{ "$": "auto_text_key", "code": 667, "label": "ʛ" },
{ "$": "auto_text_key", "code": 667, "label": "ʛ" },
{ "$": "auto_text_key", "code": 736, "label": "◌ˠ" },
{ "$": "auto_text_key", "code": 611, "label": "ɣ" }
]
}
},
{
"code": 104, "label": "h",
"$": "auto_text_key", "code": 104, "label": "h",
"popup": {
"relevant": [
{ "code": 614, "label": "ɦ" },
{ "code": 615, "label": "ɧ" },
{ "code": 295, "label": "ħ" },
{ "code": 613, "label": "ɥ" },
{ "code": 688, "label": "◌ʰ" },
{ "code": 668, "label": "ʜ" }
{ "$": "auto_text_key", "code": 614, "label": "ɦ" },
{ "$": "auto_text_key", "code": 615, "label": "ɧ" },
{ "$": "auto_text_key", "code": 295, "label": "ħ" },
{ "$": "auto_text_key", "code": 613, "label": "ɥ" },
{ "$": "auto_text_key", "code": 688, "label": "◌ʰ" },
{ "$": "auto_text_key", "code": 668, "label": "ʜ" }
]
}
},
{
"code": 106, "label": "j",
"$": "auto_text_key", "code": 106, "label": "j",
"popup": {
"relevant": [
{ "code": 668, "label": "ʝ" },
{ "code": 607, "label": "ɟ" },
{ "code": 690, "label": "◌ʲ" },
{ "code": 664, "label": "ʄ" }
{ "$": "auto_text_key", "code": 668, "label": "ʝ" },
{ "$": "auto_text_key", "code": 607, "label": "ɟ" },
{ "$": "auto_text_key", "code": 690, "label": "◌ʲ" },
{ "$": "auto_text_key", "code": 664, "label": "ʄ" }
]
}
},
{
"code": 107, "label": "k"
"$": "auto_text_key", "code": 107, "label": "k"
},
{
"code": 108, "label": "l",
"$": "auto_text_key", "code": 108, "label": "l",
"popup": {
"relevant": [
{ "code": 620, "label": "ɬ" },
{ "code": 634, "label": "ɺ" },
{ "code": 671, "label": "ʟ" },
{ "code": 654, "label": "ʎ" },
{ "code": 737, "label": "◌ˡ" },
{ "code": 622, "label": "ɮ" }
{ "$": "auto_text_key", "code": 620, "label": "ɬ" },
{ "$": "auto_text_key", "code": 634, "label": "ɺ" },
{ "$": "auto_text_key", "code": 671, "label": "ʟ" },
{ "$": "auto_text_key", "code": 654, "label": "ʎ" },
{ "$": "auto_text_key", "code": 737, "label": "◌ˡ" },
{ "$": "auto_text_key", "code": 622, "label": "ɮ" }
]
}
},
{
"code": 660, "label": "ʔ",
"$": "auto_text_key", "code": 660, "label": "ʔ",
"popup": {
"relevant": [
{ "code": 661, "label": "ʕ" },
{ "code": 674, "label": "ʢ" },
{ "code": 740, "label": "◌ˤ" },
{ "code": 673, "label": "ʡ" }
{ "$": "auto_text_key", "code": 661, "label": "ʕ" },
{ "$": "auto_text_key", "code": 674, "label": "ʢ" },
{ "$": "auto_text_key", "code": 740, "label": "◌ˤ" },
{ "$": "auto_text_key", "code": 673, "label": "ʡ" }
]
}
}
],
[
{
"code": 122, "label": "z",
"$": "auto_text_key", "code": 122, "label": "z",
"popup": {
"relevant": [
{ "code": 656, "label": "ʐ" },
{ "code": 657, "label": "ʑ" },
{ "code": 658, "label": "ʒ" }
{ "$": "auto_text_key", "code": 656, "label": "ʐ" },
{ "$": "auto_text_key", "code": 657, "label": "ʑ" },
{ "$": "auto_text_key", "code": 658, "label": "ʒ" }
]
}
},
{
"code": 120, "label": "x",
"$": "auto_text_key", "code": 120, "label": "x",
"popup": {
"relevant": [
{ "code": 739, "label": "◌ˣ" },
{ "code": 967, "label": "χ" }
{ "$": "auto_text_key", "code": 739, "label": "◌ˣ" },
{ "$": "auto_text_key", "code": 967, "label": "χ" }
]
}
},
{
"code": 99, "label": "c",
"$": "auto_text_key", "code": 99, "label": "c",
"popup": {
"relevant": [
{ "code": 231, "label": "ç" }
{ "$": "auto_text_key", "code": 231, "label": "ç" }
]
}
},
{
"code": 118, "label": "v",
"$": "auto_text_key", "code": 118, "label": "v",
"popup": {
"relevant": [
{ "code": 651, "label": "ʋ" },
{ "code": 652, "label": "ʌ" }
{ "$": "auto_text_key", "code": 651, "label": "ʋ" },
{ "$": "auto_text_key", "code": 652, "label": "ʌ" }
]
}
},
{
"code": 98, "label": "b",
"$": "auto_text_key", "code": 98, "label": "b",
"popup": {
"relevant": [
{ "code": 595, "label": "ɓ" },
{ "code": 665, "label": "ʙ" },
{ "code": 946, "label": "β" }
{ "$": "auto_text_key", "code": 595, "label": "ɓ" },
{ "$": "auto_text_key", "code": 665, "label": "ʙ" },
{ "$": "auto_text_key", "code": 946, "label": "β" }
]
}
},
{
"code": 110, "label": "n",
"$": "auto_text_key", "code": 110, "label": "n",
"popup": {
"relevant": [
{ "code": 626, "label": "ɲ" },
{ "code": 627, "label": "ɳ" },
{ "code": 628, "label": "ɴ" },
{ "code": 8319, "label": "◌ⁿ" },
{ "code": 771, "label": "◌̃" },
{ "code": 631, "label": "ŋ" }
{ "$": "auto_text_key", "code": 626, "label": "ɲ" },
{ "$": "auto_text_key", "code": 627, "label": "ɳ" },
{ "$": "auto_text_key", "code": 628, "label": "ɴ" },
{ "$": "auto_text_key", "code": 8319, "label": "◌ⁿ" },
{ "$": "auto_text_key", "code": 771, "label": "◌̃" },
{ "$": "auto_text_key", "code": 631, "label": "ŋ" }
]
}
},
{
"code": 109, "label": "m",
"$": "auto_text_key", "code": 109, "label": "m",
"popup": {
"relevant": [
{ "code": 625, "label": "ɱ" },
{ "code": 624, "label": "ɰ" },
{ "code": 623, "label": "ɯ" }
{ "$": "auto_text_key", "code": 625, "label": "ɱ" },
{ "$": "auto_text_key", "code": 624, "label": "ɰ" },
{ "$": "auto_text_key", "code": 623, "label": "ɯ" }
]
}
}

View File

@@ -6,42 +6,41 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 1081, "label": "й" },
{ "code": 1094, "label": "ц" },
{ "code": 1091, "label": "у" },
{ "code": 1082, "label": "к" },
{ "code": 1077, "label": "е" },
{ "code": 1085, "label": "н" },
{ "code": 1075, "label": "г" },
{ "code": 1096, "label": "ш" },
{ "code": 1097, "label": "щ" },
{ "code": 1079, "label": "з" },
{ "code": 1093, "label": "х" },
{ "code": 1098, "label": "ъ" }
{ "$": "auto_text_key", "code": 1081, "label": "й" },
{ "$": "auto_text_key", "code": 1094, "label": "ц" },
{ "$": "auto_text_key", "code": 1091, "label": "у" },
{ "$": "auto_text_key", "code": 1082, "label": "к" },
{ "$": "auto_text_key", "code": 1077, "label": "е" },
{ "$": "auto_text_key", "code": 1085, "label": "н" },
{ "$": "auto_text_key", "code": 1075, "label": "г" },
{ "$": "auto_text_key", "code": 1096, "label": "ш" },
{ "$": "auto_text_key", "code": 1097, "label": "щ" },
{ "$": "auto_text_key", "code": 1079, "label": "з" },
{ "$": "auto_text_key", "code": 1093, "label": "х" }
],
[
{ "code": 1092 , "label": "ф" },
{ "code": 1099 , "label": "ы" },
{ "code": 1074 , "label": "в" },
{ "code": 1072 , "label": "а" },
{ "code": 1087 , "label": "п" },
{ "code": 1088 , "label": "р" },
{ "code": 1086 , "label": "о" },
{ "code": 1083 , "label": "л" },
{ "code": 1076 , "label": "д" },
{ "code": 1078 , "label": "ж" },
{ "code": 1101 , "label": "э" }
{ "$": "auto_text_key", "code": 1092 , "label": "ф" },
{ "$": "auto_text_key", "code": 1099 , "label": "ы" },
{ "$": "auto_text_key", "code": 1074 , "label": "в" },
{ "$": "auto_text_key", "code": 1072 , "label": "а" },
{ "$": "auto_text_key", "code": 1087 , "label": "п" },
{ "$": "auto_text_key", "code": 1088 , "label": "р" },
{ "$": "auto_text_key", "code": 1086 , "label": "о" },
{ "$": "auto_text_key", "code": 1083 , "label": "л" },
{ "$": "auto_text_key", "code": 1076 , "label": "д" },
{ "$": "auto_text_key", "code": 1078 , "label": "ж" },
{ "$": "auto_text_key", "code": 1101 , "label": "э" }
],
[
{ "code": 1103 , "label": "я" },
{ "code": 1095 , "label": "ч" },
{ "code": 1089 , "label": "с" },
{ "code": 1084 , "label": "м" },
{ "code": 1080 , "label": "и" },
{ "code": 1090 , "label": "т" },
{ "code": 1100 , "label": "ь" },
{ "code": 1073 , "label": "б" },
{ "code": 1102 , "label": "ю" }
{ "$": "auto_text_key", "code": 1103 , "label": "я" },
{ "$": "auto_text_key", "code": 1095 , "label": "ч" },
{ "$": "auto_text_key", "code": 1089 , "label": "с" },
{ "$": "auto_text_key", "code": 1084 , "label": "м" },
{ "$": "auto_text_key", "code": 1080 , "label": "и" },
{ "$": "auto_text_key", "code": 1090 , "label": "т" },
{ "$": "auto_text_key", "code": 1100 , "label": "ь" },
{ "$": "auto_text_key", "code": 1073 , "label": "б" },
{ "$": "auto_text_key", "code": 1102 , "label": "ю" }
]
]
}

View File

@@ -0,0 +1,46 @@
{
"type": "characters",
"name": "jcuken_ukrainian",
"label": "Ukrainian (JCUKEN)",
"authors": [ "williamtheaker", "33kk" ],
"direction": "ltr",
"arrangement": [
[
{ "$": "auto_text_key", "code": 1081, "label": "й" },
{ "$": "auto_text_key", "code": 1094, "label": "ц" },
{ "$": "auto_text_key", "code": 1091, "label": "у" },
{ "$": "auto_text_key", "code": 1082, "label": "к" },
{ "$": "auto_text_key", "code": 1077, "label": "е" },
{ "$": "auto_text_key", "code": 1085, "label": "н" },
{ "$": "auto_text_key", "code": 1075, "label": "г" },
{ "$": "auto_text_key", "code": 1096, "label": "ш" },
{ "$": "auto_text_key", "code": 1097, "label": "щ" },
{ "$": "auto_text_key", "code": 1079, "label": "з" },
{ "$": "auto_text_key", "code": 1093, "label": "х" }
],
[
{ "$": "auto_text_key", "code": 1092 , "label": "ф" },
{ "$": "auto_text_key", "code": 1110 , "label": "і" },
{ "$": "auto_text_key", "code": 1074 , "label": "в" },
{ "$": "auto_text_key", "code": 1072 , "label": "а" },
{ "$": "auto_text_key", "code": 1087 , "label": "п" },
{ "$": "auto_text_key", "code": 1088 , "label": "р" },
{ "$": "auto_text_key", "code": 1086 , "label": "о" },
{ "$": "auto_text_key", "code": 1083 , "label": "л" },
{ "$": "auto_text_key", "code": 1076 , "label": "д" },
{ "$": "auto_text_key", "code": 1078 , "label": "ж" },
{ "$": "auto_text_key", "code": 1108 , "label": "є" }
],
[
{ "$": "auto_text_key", "code": 1103 , "label": "я" },
{ "$": "auto_text_key", "code": 1095 , "label": "ч" },
{ "$": "auto_text_key", "code": 1089 , "label": "с" },
{ "$": "auto_text_key", "code": 1084 , "label": "м" },
{ "$": "auto_text_key", "code": 1080 , "label": "и" },
{ "$": "auto_text_key", "code": 1090 , "label": "т" },
{ "$": "auto_text_key", "code": 1100 , "label": "ь" },
{ "$": "auto_text_key", "code": 1073 , "label": "б" },
{ "$": "auto_text_key", "code": 1102 , "label": "ю" }
]
]
}

View File

@@ -6,41 +6,41 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 121, "label": "y" },
{ "code": 117, "label": "u" },
{ "code": 305, "label": "ı" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" },
{ "code": 251, "label": "û" }
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 305, "label": "ı" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 251, "label": "û" }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" },
{ "code": 234, "label": "ê" },
{ "code": 238, "label": "î" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 234, "label": "ê" },
{ "$": "auto_text_key", "code": 238, "label": "î" }
],
[
{ "code": 122, "label": "z" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" },
{ "code": 231, "label": "ç" },
{ "code": 351, "label": "ş" }
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" },
{ "$": "auto_text_key", "code": 231, "label": "ç" },
{ "$": "auto_text_key", "code": 351, "label": "ş" }
]
]
}

View File

@@ -12,17 +12,15 @@
],
[
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
{ "code": 64, "label": "@", "groupId": 1, "variation": "email_address" },
{ "code": 44, "label": ",", "groupId": 1, "variation": "normal" },
{ "code": 44, "label": ",", "groupId": 1, "variation": "password" },
{ "code": 47, "label": "/", "groupId": 1, "variation": "uri" },
{ "$": "variation_selector",
"default": { "code": 44, "label": ",", "groupId": 1 },
"email": { "code": 64, "label": "@", "groupId": 1 },
"uri": { "code": 47, "label": "/", "groupId": 1 }
},
{ "code": -210, "label": "language_switch", "type": "system_gui" },
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui" },
{ "code": 32, "label": "space" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "email_address" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "normal" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "password" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "uri" },
{ "code": 46, "label": ".", "groupId": 2 },
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
]
]

View File

@@ -11,17 +11,15 @@
],
[
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
{ "code": 64, "label": "@", "groupId": 1, "variation": "email_address" },
{ "code": 1548, "label": "،", "groupId": 1, "variation": "normal" },
{ "code": 1548, "label": "،", "groupId": 1, "variation": "password" },
{ "code": 47, "label": "/", "groupId": 1, "variation": "uri" },
{ "$": "variation_selector",
"default": { "code": 1548, "label": "،", "groupId": 1 },
"email": { "code": 64, "label": "@", "groupId": 1 },
"uri": { "code": 47, "label": "/", "groupId": 1 }
},
{ "code": -210, "label": "language_switch", "type": "system_gui" },
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui" },
{ "code": 32, "label": "space" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "email_address" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "normal" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "password" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "uri" },
{ "code": 46, "label": ".", "groupId": 2 },
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
]
]

View File

@@ -12,14 +12,11 @@
],
[
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
{ "code": 113, "label": "q", "groupId": 1 },
{ "$": "auto_text_key", "code": 113, "label": "q", "groupId": 1 },
{ "code": -210, "label": "language_switch", "type": "system_gui" },
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui" },
{ "code": 32, "label": "space" },
{ "code": 122, "label": "z", "groupId": 2, "variation": "email_address" },
{ "code": 122, "label": "z", "groupId": 2, "variation": "normal" },
{ "code": 122, "label": "z", "groupId": 2, "variation": "password" },
{ "code": 122, "label": "z", "groupId": 2, "variation": "uri" },
{ "$": "auto_text_key", "code": 122, "label": "z", "groupId": 2 },
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
]
]

View File

@@ -11,17 +11,15 @@
],
[
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
{ "code": 64, "label": "@", "groupId": 1, "variation": "email_address" },
{ "code": 44, "label": ",", "groupId": 1, "variation": "normal" },
{ "code": 44, "label": ",", "groupId": 1, "variation": "password" },
{ "code": 47, "label": "/", "groupId": 1, "variation": "uri" },
{ "$": "variation_selector",
"default": { "code": 44, "label": ",", "groupId": 1 },
"email": { "code": 64, "label": "@", "groupId": 1 },
"uri": { "code": 47, "label": "/", "groupId": 1 }
},
{ "code": -210, "label": "language_switch", "type": "system_gui" },
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui" },
{ "code": 32, "label": "space" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "email_address" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "normal" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "password" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "uri" },
{ "code": 46, "label": ".", "groupId": 2 },
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
]
]

View File

@@ -11,17 +11,16 @@
],
[
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
{ "code": 64, "label": "@", "groupId": 1, "variation": "email_address" },
{ "code": 1567, "label": "؟", "groupId": 1, "variation": "normal" },
{ "code": 1548, "label": "،", "groupId": 1, "variation": "password" },
{ "code": 47, "label": "/", "groupId": 1, "variation": "uri" },
{ "$": "variation_selector",
"default": { "code": 1567, "label": "؟", "groupId": 1 },
"password": { "code": 1548, "label": "،", "groupId": 1 },
"email": { "code": 64, "label": "@", "groupId": 1 },
"uri": { "code": 47, "label": "/", "groupId": 1 }
},
{ "code": -210, "label": "language_switch", "type": "system_gui" },
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui" },
{ "code": 32, "label": " " },
{ "code": 46, "label": ".", "groupId": 2, "variation": "email_address" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "normal" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "password" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "uri" },
{ "code": 46, "label": ".", "groupId": 2 },
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
]
]

View File

@@ -11,21 +11,17 @@
],
[
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
{ "code": 64, "label": "@", "groupId": 1, "variation": "email_address" },
{ "code": 1548, "label": "،", "groupId": 1, "variation": "normal" },
{ "code": 1548, "label": "،", "groupId": 1, "variation": "password" },
{ "code": 47, "label": "/", "groupId": 1, "variation": "uri" },
{ "$": "variation_selector",
"default": { "code": 1548, "label": "،", "groupId": 1 },
"email": { "code": 64, "label": "@", "groupId": 1 },
"uri": { "code": 47, "label": "/", "groupId": 1 }
},
{ "code": -210, "label": "language_switch", "type": "system_gui" },
{ "code": -213, "label": "switch_to_media_context", "type": "system_gui" },
{ "code": 32, "label": "space" },
{ "code": 8204, "label": "half_space", "variation": "normal" },
{ "code": 8204, "label": "half_space", "variation": "password" },
{ "code": 1600, "label": "kashida", "variation": "normal" },
{ "code": 1600, "label": "kashida", "variation": "password" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "email_address" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "normal" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "password" },
{ "code": 46, "label": ".", "groupId": 2, "variation": "uri" },
{ "code": 8204, "label": "half_space" },
{ "code": 1600, "label": "kashida" },
{ "code": 46, "label": ".", "groupId": 2 },
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
]
]

View File

@@ -6,39 +6,39 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 121, "label": "y" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" },
{ "code": 229, "label": "å" }
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 229, "label": "å" }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" },
{ "code": 248, "label": "ø" },
{ "code": 230, "label": "æ" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 248, "label": "ø" },
{ "$": "auto_text_key", "code": 230, "label": "æ" }
],
[
{ "code": 122, "label": "z" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" }
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" }
]
]
}

View File

@@ -6,36 +6,36 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 121, "label": "y" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" }
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" }
],
[
{ "code": 122, "label": "z" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" }
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" }
]
]
}

View File

@@ -6,36 +6,36 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 122, "label": "z" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" }
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" }
],
[
{ "code": 121, "label": "y" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" }
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" }
]
]
}

View File

@@ -6,40 +6,40 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 1113, "label": "љ" },
{ "code": 1114, "label": "њ" },
{ "code": 1077, "label": "е" },
{ "code": 1088, "label": "р" },
{ "code": 1090, "label": "т" },
{ "code": 1079, "label": "з" },
{ "code": 1091, "label": "у" },
{ "code": 1080, "label": "и" },
{ "code": 1086, "label": "о" },
{ "code": 1087, "label": "п" },
{ "code": 1096, "label": "ш" }
{ "$": "auto_text_key", "code": 1113, "label": "љ" },
{ "$": "auto_text_key", "code": 1114, "label": "њ" },
{ "$": "auto_text_key", "code": 1077, "label": "е" },
{ "$": "auto_text_key", "code": 1088, "label": "р" },
{ "$": "auto_text_key", "code": 1090, "label": "т" },
{ "$": "auto_text_key", "code": 1079, "label": "з" },
{ "$": "auto_text_key", "code": 1091, "label": "у" },
{ "$": "auto_text_key", "code": 1080, "label": "и" },
{ "$": "auto_text_key", "code": 1086, "label": "о" },
{ "$": "auto_text_key", "code": 1087, "label": "п" },
{ "$": "auto_text_key", "code": 1096, "label": "ш" }
],
[
{ "code": 1072, "label": "а" },
{ "code": 1089, "label": "с" },
{ "code": 1076, "label": "д" },
{ "code": 1092, "label": "ф" },
{ "code": 1075, "label": "г" },
{ "code": 1093, "label": "х" },
{ "code": 1112, "label": "ј" },
{ "code": 1082, "label": "к" },
{ "code": 1083, "label": "л" },
{ "code": 1095, "label": "ч" },
{ "code": 1115, "label": "ћ" }
{ "$": "auto_text_key", "code": 1072, "label": "а" },
{ "$": "auto_text_key", "code": 1089, "label": "с" },
{ "$": "auto_text_key", "code": 1076, "label": "д" },
{ "$": "auto_text_key", "code": 1092, "label": "ф" },
{ "$": "auto_text_key", "code": 1075, "label": "г" },
{ "$": "auto_text_key", "code": 1093, "label": "х" },
{ "$": "auto_text_key", "code": 1112, "label": "ј" },
{ "$": "auto_text_key", "code": 1082, "label": "к" },
{ "$": "auto_text_key", "code": 1083, "label": "л" },
{ "$": "auto_text_key", "code": 1095, "label": "ч" },
{ "$": "auto_text_key", "code": 1115, "label": "ћ" }
],
[
{ "code": 1119, "label": "џ" },
{ "code": 1094, "label": "ц" },
{ "code": 1074, "label": "в" },
{ "code": 1073, "label": "б" },
{ "code": 1085, "label": "н" },
{ "code": 1084, "label": "м" },
{ "code": 1106, "label": "ђ" },
{ "code": 1078, "label": "ж" }
{ "$": "auto_text_key", "code": 1119, "label": "џ" },
{ "$": "auto_text_key", "code": 1094, "label": "ц" },
{ "$": "auto_text_key", "code": 1074, "label": "в" },
{ "$": "auto_text_key", "code": 1073, "label": "б" },
{ "$": "auto_text_key", "code": 1085, "label": "н" },
{ "$": "auto_text_key", "code": 1084, "label": "м" },
{ "$": "auto_text_key", "code": 1106, "label": "ђ" },
{ "$": "auto_text_key", "code": 1078, "label": "ж" }
]
]
}

View File

@@ -6,41 +6,41 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 122, "label": "z" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" },
{ "code": 353, "label": "š" }
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 353, "label": "š" }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" },
{ "code": 269, "label": "č" },
{ "code": 263, "label": "ć" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 269, "label": "č" },
{ "$": "auto_text_key", "code": 263, "label": "ć" }
],
[
{ "code": 121, "label": "y" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" },
{ "code": 273, "label": "đ" },
{ "code": 382, "label": "ž" }
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" },
{ "$": "auto_text_key", "code": 273, "label": "đ" },
{ "$": "auto_text_key", "code": 382, "label": "ž" }
]
]
}

View File

@@ -6,37 +6,37 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 121, "label": "y" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" }
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" },
{ "code": 241, "label": "ñ" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 241, "label": "ñ" }
],
[
{ "code": 122, "label": "z" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" }
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" }
]
]
}

View File

@@ -6,39 +6,39 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 121, "label": "y" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" },
{ "code": 229, "label": "å" }
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 229, "label": "å" }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" },
{ "code": 246, "label": "ö" },
{ "code": 228, "label": "ä" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 246, "label": "ö" },
{ "$": "auto_text_key", "code": 228, "label": "ä" }
],
[
{ "code": 122, "label": "z" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" }
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" }
]
]
}

View File

@@ -6,51 +6,51 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 122, "label": "z" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" },
{ "code": 232, "label": "è", "popup": {
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 232, "label": "è", "popup": {
"relevant": [
{ "code": 252, "label": "ü" }
{ "$": "auto_text_key", "code": 252, "label": "ü" }
]
} }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" },
{ "code": 233, "label": "é", "popup": {
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 233, "label": "é", "popup": {
"relevant": [
{ "code": 246, "label": "ö" }
{ "$": "auto_text_key", "code": 246, "label": "ö" }
]
} },
{ "code": 224, "label": "à", "popup": {
{ "$": "auto_text_key", "code": 224, "label": "à", "popup": {
"relevant": [
{ "code": 228, "label": "ä" }
{ "$": "auto_text_key", "code": 228, "label": "ä" }
]
} }
],
[
{ "code": 121, "label": "y" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" }
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" }
]
]
}

View File

@@ -6,51 +6,51 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 122, "label": "z" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" },
{ "code": 252, "label": "ü", "popup": {
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 252, "label": "ü", "popup": {
"relevant": [
{ "code": 232, "label": "è" }
{ "$": "auto_text_key", "code": 232, "label": "è" }
]
} }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" },
{ "code": 246, "label": "ö", "popup": {
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 246, "label": "ö", "popup": {
"relevant": [
{ "code": 233, "label": "é" }
{ "$": "auto_text_key", "code": 233, "label": "é" }
]
} },
{ "code": 228, "label": "ä", "popup": {
{ "$": "auto_text_key", "code": 228, "label": "ä", "popup": {
"relevant": [
{ "code": 224, "label": "à" }
{ "$": "auto_text_key", "code": 224, "label": "à" }
]
} }
],
[
{ "code": 121, "label": "y" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" }
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" }
]
]
}

View File

@@ -6,51 +6,51 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 122, "label": "z" },
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" },
{ "code": 252, "label": "ü", "popup": {
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 252, "label": "ü", "popup": {
"relevant": [
{ "code": 232, "label": "è" }
{ "$": "auto_text_key", "code": 232, "label": "è" }
]
} }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" },
{ "code": 246, "label": "ö", "popup": {
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 246, "label": "ö", "popup": {
"relevant": [
{ "code": 233, "label": "é" }
{ "$": "auto_text_key", "code": 233, "label": "é" }
]
} },
{ "code": 228, "label": "ä", "popup": {
{ "$": "auto_text_key", "code": 228, "label": "ä", "popup": {
"relevant": [
{ "code": 224, "label": "à" }
{ "$": "auto_text_key", "code": 224, "label": "à" }
]
} }
],
[
{ "code": 121, "label": "y" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" }
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" }
]
]
}

View File

@@ -6,42 +6,42 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 287, "label": "ğ" },
{ "code": 305, "label": "ı" },
{ "code": 111, "label": "o" },
{ "code": 100, "label": "d" },
{ "code": 114, "label": "r" },
{ "code": 110, "label": "n" },
{ "code": 104, "label": "h" },
{ "code": 112, "label": "p" },
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" }
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 287, "label": "ğ" },
{ "$": "auto_text_key", "code": 305, "label": "ı" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" }
],
[
{ "code": 117, "label": "u" },
{ "code": 105, "label": "i" },
{ "code": 101, "label": "e" },
{ "code": 97, "label": "a" },
{ "code": 252, "label": "ü" },
{ "code": 116, "label": "t" },
{ "code": 107, "label": "k" },
{ "code": 109, "label": "m" },
{ "code": 108, "label": "l" },
{ "code": 121, "label": "y" },
{ "code": 351, "label": "ş" }
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 252, "label": "ü" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 109, "label": "m" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 351, "label": "ş" }
],
[
{ "code": 106, "label": "j" },
{ "code": 246, "label": "ö" },
{ "code": 118, "label": "v" },
{ "code": 99, "label": "c" },
{ "code": 231, "label": "ç" },
{ "code": 122, "label": "z" },
{ "code": 115, "label": "s" },
{ "code": 98, "label": "b" },
{ "code": 120, "label": "x" }
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 246, "label": "ö" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 231, "label": "ç" },
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 120, "label": "x" }
]
]
}

View File

@@ -6,42 +6,42 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 119, "label": "w" },
{ "code": 101, "label": "e" },
{ "code": 114, "label": "r" },
{ "code": 116, "label": "t" },
{ "code": 121, "label": "y" },
{ "code": 117, "label": "u" },
{ "code": 305, "label": "ı" },
{ "code": 111, "label": "o" },
{ "code": 112, "label": "p" },
{ "code": 287, "label": "ğ" },
{ "code": 252, "label": "ü" }
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 305, "label": "ı" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 287, "label": "ğ" },
{ "$": "auto_text_key", "code": 252, "label": "ü" }
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 100, "label": "d" },
{ "code": 102, "label": "f" },
{ "code": 103, "label": "g" },
{ "code": 104, "label": "h" },
{ "code": 106, "label": "j" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" },
{ "code": 351, "label": "ş" },
{ "code": 105, "label": "i" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" },
{ "$": "auto_text_key", "code": 351, "label": "ş" },
{ "$": "auto_text_key", "code": 105, "label": "i" }
],
[
{ "code": 122, "label": "z" },
{ "code": 120, "label": "x" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 98, "label": "b" },
{ "code": 110, "label": "n" },
{ "code": 109, "label": "m" },
{ "code": 246, "label": "ö" },
{ "code": 231, "label": "ç" }
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 109, "label": "m" },
{ "$": "auto_text_key", "code": 246, "label": "ö" },
{ "$": "auto_text_key", "code": 231, "label": "ç" }
]
]
}

View File

@@ -6,41 +6,48 @@
"direction": "ltr",
"arrangement": [
[
{ "code": 113, "label": "q" },
{ "code": 100, "label": "d" },
{ "code": 114, "label": "r" },
{ "code": 119, "label": "w" },
{ "code": 98, "label": "b" },
{ "code": 106, "label": "j" },
{ "code": 102, "label": "f" },
{ "code": 117, "label": "u" },
{ "code": 112, "label": "p" },
{ "code": 59, "label": ";", "popup": {
"relevant": [
{ "code": 58, "label": ":" }
]
}, "shift": { "code": 58, "label": ":" } }
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "case_selector",
"lower": { "code": 59, "label": ";", "popup": {
"relevant": [
{ "code": 58, "label": ":" }
]
} },
"upper": { "code": 58, "label": ":", "popup": {
"relevant": [
{ "code": 59, "label": ";" }
]
} }
}
],
[
{ "code": 97, "label": "a" },
{ "code": 115, "label": "s" },
{ "code": 104, "label": "h" },
{ "code": 116, "label": "t" },
{ "code": 103, "label": "g" },
{ "code": 121, "label": "y" },
{ "code": 110, "label": "n" },
{ "code": 101, "label": "e" },
{ "code": 111, "label": "o" },
{ "code": 105, "label": "i" }
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 115, "label": "s" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 105, "label": "i" }
],
[
{ "code": 122, "label": "z" },
{ "code": 120, "label": "x" },
{ "code": 109, "label": "m" },
{ "code": 99, "label": "c" },
{ "code": 118, "label": "v" },
{ "code": 107, "label": "k" },
{ "code": 108, "label": "l" }
{ "$": "auto_text_key", "code": 122, "label": "z" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 109, "label": "m" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 108, "label": "l" }
]
]
}

View File

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

View File

@@ -16,39 +16,97 @@
package dev.patrickgold.florisboard.crashutility
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.*
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import dev.patrickgold.florisboard.BuildConfig
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.databinding.CrashDialogBinding
import dev.patrickgold.florisboard.debug.*
import dev.patrickgold.florisboard.ime.core.PrefHelper
import java.util.*
class CrashDialogActivity : AppCompatActivity() {
private lateinit var binding: CrashDialogBinding
private var stacktrace: String = ""
private var stacktraces: List<CrashUtility.Stacktrace> = listOf()
private var errorReport: StringBuilder = StringBuilder()
private var prefs: PrefHelper? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = CrashDialogBinding.inflate(layoutInflater)
setContentView(binding.root)
stacktrace = CrashUtility.getUnhandledStacktrace(this)
binding.stacktrace.text = stacktrace
// We secure the PrefHelper usage here because the PrefHelper could potentially be the
// source of the crash, thus making the crash dialog unusable if not wrapped.
try {
prefs = PrefHelper.getDefaultInstance(this)
} catch (_: Exception) {
}
stacktraces = CrashUtility.getUnhandledStacktraces(this)
errorReport.apply {
appendLine("#### Environment information")
appendLine("- FlorisBoard Version: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})")
appendLine("- Device: ${getDeviceName()}")
appendLine("- Android: ${getAndroidVersion()}")
appendLine()
appendLine("#### Features enabled")
appendLine("```kt")
val prefs = prefs
if (prefs != null) {
try {
appendLine("smartbar = ${prefs.smartbar.enabled}")
appendLine("suggestions = ${prefs.suggestion.enabled}")
appendLine("suggestions_clipboard = ${prefs.suggestion.clipboardContentEnabled}")
appendLine("suggestions_next_word = ${prefs.suggestion.usePrevWords}")
appendLine("glide = ${prefs.glide.enabled}")
appendLine("clipboard_internal = ${prefs.clipboard.enableInternal}")
appendLine("clipboard_history = ${prefs.clipboard.enableHistory}")
} catch (_: Exception) {
appendLine("error: Exception was thrown while retrieving preferences, instance not null.")
}
} else {
appendLine("error: Failed to fetch preferences: PrefHelper instance was null!")
}
appendLine("```")
appendLine()
if (stacktraces.isNotEmpty()) {
appendLine("#### Attached stacktrace files")
stacktraces.forEach {
generateCollapsibleStacktrace(this, it)
}
} else {
flogWarning(LogTopic.CRASH_UTILITY) {
"Stacktrace file list is empty."
}
}
}
binding.stacktrace.text = errorReport
binding.reportInstructions.text =
binding.reportInstructions.text.toString().format(
resources.getString(R.string.crash_dialog__bug_report_template)
)
binding.copyToClipboard.setOnClickListener {
val clipboardManager = getSystemService(Context.CLIPBOARD_SERVICE)
if (clipboardManager != null && clipboardManager is ClipboardManager) {
clipboardManager.setPrimaryClip(ClipData.newPlainText(stacktrace, stacktrace))
val toastMessage: String = if (clipboardManager != null && clipboardManager is ClipboardManager) {
clipboardManager.setPrimaryClip(ClipData.newPlainText(errorReport, errorReport))
resources.getString(R.string.crash_dialog__copy_to_clipboard_success)
} else {
resources.getString(R.string.crash_dialog__copy_to_clipboard_failure)
}
Toast.makeText(this, toastMessage, Toast.LENGTH_SHORT).show()
}
binding.openBugReportForm.setOnClickListener {
val browserIntent = Intent(
Intent.ACTION_VIEW,
Uri.parse(resources.getString(R.string.florisboard__issue_tracker_new_issue_url))
Uri.parse(resources.getString(R.string.florisboard__issue_tracker_url))
)
startActivity(browserIntent)
}
@@ -57,4 +115,39 @@ class CrashDialogActivity : AppCompatActivity() {
finish()
}
}
/**
* Rules for collapsible markdown on GitHub:
* https://gist.github.com/pierrejoubert73/902cc94d79424356a8d20be2b382e1ab
*/
private fun generateCollapsibleStacktrace(sb: StringBuilder, stacktrace: CrashUtility.Stacktrace) {
sb.apply {
appendLine("<details>")
appendLine("<summary>${stacktrace.name}</summary>")
appendLine()
appendLine("```")
appendLine(stacktrace.details)
appendLine("```")
appendLine("</details>")
appendLine()
}
}
private fun getDeviceName(): String {
val manufacturer = Build.MANUFACTURER
val model = Build.MODEL
return if (model.toLowerCase(Locale.ENGLISH).startsWith(manufacturer.toLowerCase(Locale.ENGLISH))) {
model.capitalize(Locale.ENGLISH)
} else {
"${manufacturer.capitalize(Locale.ENGLISH)} $model"
}
}
private fun getAndroidVersion(): String {
val fields = Build.VERSION_CODES::class.java.fields
var codeName: String? = null
fields.filter { it.getInt(Build.VERSION_CODES::class) == Build.VERSION.SDK_INT }
.forEach { codeName = it.name }
return codeName?.let { "${Build.VERSION.RELEASE} (cn=$it sdk=${Build.VERSION.SDK_INT})" } ?: "Unknown"
}
}

View File

@@ -26,9 +26,10 @@ import android.os.Bundle
import android.os.Process
import android.util.Log
import android.view.inputmethod.InputMethodManager
import dev.patrickgold.florisboard.BuildConfig
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.debug.*
import dev.patrickgold.florisboard.ime.core.FlorisBoard
import timber.log.Timber
import java.io.File
import java.lang.ref.WeakReference
import kotlin.system.exitProcess
@@ -46,7 +47,7 @@ abstract class CrashUtility private constructor() {
private const val SHARED_PREFS_FILE = "crash_utility"
private const val SHARED_PREFS_LAST_CRASH_TIMESTAMP = "last_crash_timestamp"
private const val NOTIFICATION_CHANNEL_ID = "dev.patrickgold.florisboard.crashutility"
private const val NOTIFICATION_CHANNEL_ID = "${BuildConfig.APPLICATION_ID}.crashutility"
private const val NOTIFICATION_ID = 0xFBAD0100
private const val UNHANDLED_STACKTRACE_FILE_EXT = "stacktrace"
@@ -63,14 +64,16 @@ abstract class CrashUtility private constructor() {
*/
fun install(context: Context?): Boolean {
if (context == null) {
Timber.e(
"install($context): Can't install crash handler with a null Context object, doing nothing!"
)
flogError(LogTopic.CRASH_UTILITY) {
"Can't install crash handler with a null Context object, doing nothing!"
}
return false
}
val oldHandler = Thread.getDefaultUncaughtExceptionHandler()
if (oldHandler is UncaughtExceptionHandler) {
Timber.i("install($context): Crash handler is already installed, doing nothing!")
flogInfo(LogTopic.CRASH_UTILITY) {
"Crash handler is already installed, doing nothing!"
}
} else {
val application = context.applicationContext
if (application != null && application is Application) {
@@ -82,18 +85,18 @@ abstract class CrashUtility private constructor() {
application.filesDir.absolutePath
)
)
Timber.i(
"install($context): Successfully installed crash handler for this application!"
)
flogInfo(LogTopic.CRASH_UTILITY) {
"Successfully installed crash handler for this application!"
}
} catch (e: SecurityException) {
Timber.e(
"install($context): Failed to install crash handler, probably due to missing runtime permission 'setDefaultUncaughtExceptionHandler':\n$e"
)
flogError(LogTopic.CRASH_UTILITY) {
"Failed to install crash handler, probably due to missing runtime permission 'setDefaultUncaughtExceptionHandler':\n$e"
}
return false
} catch (e: Exception) {
Timber.e(
"install($context): Failed to install crash handler due to an unspecified error:\n$e"
)
flogError(LogTopic.CRASH_UTILITY) {
"Failed to install crash handler due to an unspecified error:\n$e"
}
return false
}
application.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
@@ -126,19 +129,19 @@ abstract class CrashUtility private constructor() {
)
notificationManager.createNotificationChannel(notificationChannel)
}
Timber.i(
"install($context): Successfully created crash handler notification channel!"
)
flogInfo(LogTopic.CRASH_UTILITY) {
"Successfully created crash handler notification channel!"
}
} catch (e: Exception) {
Timber.e(
"install($context): Failed to create crash handler notification channel due to an unspecified error:\n$e"
)
flogError(LogTopic.CRASH_UTILITY) {
"Failed to create crash handler notification channel due to an unspecified error:\n$e"
}
}
}
} else {
Timber.e(
"install($context): Can't install crash handler with a null Application object, doing nothing!"
)
flogError(LogTopic.CRASH_UTILITY) {
"Can't install crash handler with a null Application object, doing nothing!"
}
return false
}
}
@@ -150,24 +153,24 @@ abstract class CrashUtility private constructor() {
*
* @param context The current package context. If null is supplied, this function returns
* an empty string.
* @return All unhandled stacktrace files or an empty string.
* @return All unhandled stacktrace files or an empty list.
*/
fun getUnhandledStacktrace(context: Context?): String {
context ?: return ""
val retString: StringBuilder = StringBuilder()
fun getUnhandledStacktraces(context: Context?): List<Stacktrace> {
context ?: return listOf()
val retList = mutableListOf<Stacktrace>()
val ustDir = getUstDir(context)
if (ustDir.isDirectory) {
(ustDir.listFiles { pathname ->
pathname.name.endsWith(".$UNHANDLED_STACKTRACE_FILE_EXT")
})?.forEach { file ->
val newLine = System.lineSeparator()
Timber.i("Reading unhandled stacktrace: ${file.name}")
retString.append("~~~ ${file.name} ~~~$newLine$newLine")
retString.append(readFile(file))
flogInfo(LogTopic.CRASH_UTILITY) {
"Reading unhandled stacktrace: ${file.name}"
}
retList.add(Stacktrace(file.name, readFile(file)))
file.delete()
}
}
return retString.toString()
return retList.toList()
}
fun hasUnhandledStacktraceFiles(context: Context): Boolean {
@@ -338,6 +341,14 @@ abstract class CrashUtility private constructor() {
}
}
/**
* A simple stacktrace data class capable of holding a [name] and the [details] of a stacktrace.
*/
data class Stacktrace(
val name: String,
val details: String
)
/**
* Custom UncaughtExceptionHandler, which writes the captured stacktrace of the crash to the
* internal storage, pushes a crash notification and kills the current process.
@@ -348,7 +359,9 @@ abstract class CrashUtility private constructor() {
private val path: String
) : Thread.UncaughtExceptionHandler {
override fun uncaughtException(thread: Thread?, throwable: Throwable?) {
Timber.e("Detected application crash, executing custom crash handler.")
flogInfo(LogTopic.CRASH_UTILITY) {
"Detected application crash, executing custom crash handler."
}
thread ?: return
throwable ?: return
val timestamp = System.currentTimeMillis()

View File

@@ -0,0 +1,366 @@
/*
* 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.
*/
@file:OptIn(ExperimentalContracts::class, ExperimentalUnsignedTypes::class)
package dev.patrickgold.florisboard.debug
import android.content.Context
import android.os.Build
import android.util.Log
import java.lang.ref.WeakReference
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/** Type alias for a flog topic Integer. */
typealias FlogTopic = UInt
/** Type alias for a flog level Integer. */
typealias FlogLevel = UInt
/** Type alias for a flog output Integer. */
typealias FlogOutput = UInt
/**
* Logs an error message returned by [block] together with the automatically retrieved
* calling class and method name either to the console or to a log file. The class name
* is used for the tag, the method name prepended to the message.
*
* This method automatically evaluates if logging is enabled and calls [block] only
* if a log message should be generated.
*
* Optionally a [topic] can also be specified to allow to only partially enable
* debug messages across the codebase. The passed [topic] is compared with the
* currently active [Flog.flogTopics] variable and only if at least 1 topic match
* is found, [block] will be called and a log message written.
*
* @param topic The topic of this message. To specify multiple topics, use the binary
* OR operator. Defaults to [Flog.TOPIC_OTHER].
* @param block The lambda expression to evaluate the message which is appended to the
* method name. Is called only if logging is enabled and the topics match. Must return
* a [String]. If this argument is omitted, only the calling method name will be used
* as the log message.
*/
inline fun flogError(topic: FlogTopic = Flog.TOPIC_OTHER, block: () -> String = { "" }) {
contract {
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
}
if (Flog.checkShouldFlog(topic, Flog.LEVEL_ERROR)) {
Flog.log(Flog.LEVEL_ERROR, block())
}
}
/**
* Logs a warning message returned by [block] together with the automatically retrieved
* calling class and method name either to the console or to a log file. The class name
* is used for the tag, the method name prepended to the message.
*
* This method automatically evaluates if logging is enabled and calls [block] only
* if a log message should be generated.
*
* Optionally a [topic] can also be specified to allow to only partially enable
* debug messages across the codebase. The passed [topic] is compared with the
* currently active [Flog.flogTopics] variable and only if at least 1 topic match
* is found, [block] will be called and a log message written.
*
* @param topic The topic of this message. To specify multiple topics, use the binary
* OR operator. Defaults to [Flog.TOPIC_OTHER].
* @param block The lambda expression to evaluate the message which is appended to the
* method name. Is called only if logging is enabled and the topics match. Must return
* a [String]. If this argument is omitted, only the calling method name will be used
* as the log message.
*/
inline fun flogWarning(topic: FlogTopic = Flog.TOPIC_OTHER, block: () -> String = { "" }) {
contract {
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
}
if (Flog.checkShouldFlog(topic, Flog.LEVEL_WARNING)) {
Flog.log(Flog.LEVEL_WARNING, block())
}
}
/**
* Logs a info message returned by [block] together with the automatically retrieved
* calling class and method name either to the console or to a log file. The class name
* is used for the tag, the method name prepended to the message.
*
* This method automatically evaluates if logging is enabled and calls [block] only
* if a log message should be generated.
*
* Optionally a [topic] can also be specified to allow to only partially enable
* debug messages across the codebase. The passed [topic] is compared with the
* currently active [Flog.flogTopics] variable and only if at least 1 topic match
* is found, [block] will be called and a log message written.
*
* @param topic The topic of this message. To specify multiple topics, use the binary
* OR operator. Defaults to [Flog.TOPIC_OTHER].
* @param block The lambda expression to evaluate the message which is appended to the
* method name. Is called only if logging is enabled and the topics match. Must return
* a [String]. If this argument is omitted, only the calling method name will be used
* as the log message.
*/
inline fun flogInfo(topic: FlogTopic = Flog.TOPIC_OTHER, block: () -> String = { "" }) {
contract {
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
}
if (Flog.checkShouldFlog(topic, Flog.LEVEL_INFO)) {
Flog.log(Flog.LEVEL_INFO, block())
}
}
/**
* Logs a debug message returned by [block] together with the automatically retrieved
* calling class and method name either to the console or to a log file. The class name
* is used for the tag, the method name prepended to the message.
*
* This method automatically evaluates if logging is enabled and calls [block] only
* if a log message should be generated.
*
* Optionally a [topic] can also be specified to allow to only partially enable
* debug messages across the codebase. The passed [topic] is compared with the
* currently active [Flog.flogTopics] variable and only if at least 1 topic match
* is found, [block] will be called and a log message written.
*
* @param topic The topic of this message. To specify multiple topics, use the binary
* OR operator. Defaults to [Flog.TOPIC_OTHER].
* @param block The lambda expression to evaluate the message which is appended to the
* method name. Is called only if logging is enabled and the topics match. Must return
* a [String]. If this argument is omitted, only the calling method name will be used
* as the log message.
*/
inline fun flogDebug(topic: FlogTopic = Flog.TOPIC_OTHER, block: () -> String = { "" }) {
contract {
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
}
if (Flog.checkShouldFlog(topic, Flog.LEVEL_DEBUG)) {
Flog.log(Flog.LEVEL_DEBUG, block())
}
}
/**
* Logs a wtf message returned by [block] together with the automatically retrieved
* calling class and method name either to the console or to a log file. The class name
* is used for the tag, the method name prepended to the message.
*
* This method automatically evaluates if logging is enabled and calls [block] only
* if a log message should be generated.
*
* Optionally a [topic] can also be specified to allow to only partially enable
* debug messages across the codebase. The passed [topic] is compared with the
* currently active [Flog.flogTopics] variable and only if at least 1 topic match
* is found, [block] will be called and a log message written.
*
* @param topic The topic of this message. To specify multiple topics, use the binary
* OR operator. Defaults to [Flog.TOPIC_OTHER].
* @param block The lambda expression to evaluate the message which is appended to the
* method name. Is called only if logging is enabled and the topics match. Must return
* a [String]. If this argument is omitted, only the calling method name will be used
* as the log message.
*/
inline fun flogWtf(topic: FlogTopic = Flog.TOPIC_OTHER, block: () -> String = { "" }) {
contract {
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
}
if (Flog.checkShouldFlog(topic, Flog.LEVEL_WTF)) {
Flog.log(Flog.LEVEL_WTF, block())
}
}
/**
* Helper function to evaluate if a bit flag is set in an integer value.
*
* @param flag The flag to check if it is set.
*
* @return True if the flag is set, false otherwise.
*/
private infix fun UInt.isSet(flag: UInt): Boolean {
return (this and flag) == flag
}
/**
* Main helper object for FlorisBoard logging (=Flog). Manages the enabled
* state and the active topics. Provides relevant helper functions for the
* flog methods to properly work.
*
* This helper object uses some parts of the Timber library to assist in
* logging. In particular:
* - [createTag] (converted to Kotlin, renamed from "createStackElementTag",
* removed manual tagging).
* - [getStacktraceElement] (converted to Kotlin, renamed from "getTag",
* method now returns stack trace element).
* - [log] (only the [OUTPUT_CONSOLE] part, converted to Kotlin).
* Timber is licensed under the Apache 2.0 license, see the repo here:
* https://github.com/JakeWharton/timber
*/
@Suppress("MemberVisibilityCanBePrivate")
object Flog {
const val TOPIC_NONE: FlogTopic = UInt.MIN_VALUE
const val TOPIC_OTHER: FlogTopic = 0x80000000u
const val TOPIC_ALL: FlogTopic = UInt.MAX_VALUE
const val LEVEL_NONE: FlogLevel = UInt.MIN_VALUE
const val LEVEL_ERROR: FlogLevel = 0x01u
const val LEVEL_WARNING: FlogLevel = 0x02u
const val LEVEL_INFO: FlogLevel = 0x04u
const val LEVEL_DEBUG: FlogLevel = 0x08u
const val LEVEL_WTF: FlogLevel = 0x10u
const val LEVEL_ALL: FlogLevel = UInt.MAX_VALUE
const val OUTPUT_CONSOLE: FlogOutput = 0x01u
const val OUTPUT_FILE: FlogOutput = 0x02u
/** The relevant call stack element is always on the 4th position, thus 4-1=3. */
private const val CALL_STACK_INDEX: Int = 3
/** The maximum log length limit. */
private const val MAX_LOG_LENGTH: Int = 4000
/** The maximum tag length limit. */
private const val MAX_TAG_LENGTH: Int = 23
private var applicationContext: WeakReference<Context> = WeakReference(null)
private var isFloggingEnabled: Boolean = false
private var flogTopics: FlogTopic = TOPIC_NONE
private var flogLevels: FlogLevel = LEVEL_NONE
private var flogOutputs: FlogOutput = OUTPUT_CONSOLE
/**
* Installs the flog utility for given [applicationContext] and sets the relevant
* configuration variables based on the given config values.
*
* @param applicationContext The application context, used for file logging. The context
* will be wrapped in a [WeakReference] to prevent memory leaks.
* @param isFloggingEnabled If logging is enabled. If this value is false, all calls to
* the flog methods will be ignored and no logs will be written, regardless of the topics
* and levels set.
* @param flogTopics The enabled topics for this installation. Use [TOPIC_ALL] to enable
* all topics. If this value is [TOPIC_NONE], this essentially disables all logging.
* @param flogLevels The enabled levels for this installation. Use [LEVEL_ALL] to enable
* all levels. If this value is [LEVEL_NONE], this essentially disables all logging.
* @param flogOutputs The enabled outputs for this installation. Use either [OUTPUT_CONSOLE]
* for logging to Logcat or [OUTPUT_FILE] to a logging file.
*/
fun install(
applicationContext: Context,
isFloggingEnabled: Boolean,
flogTopics: FlogTopic,
flogLevels: FlogLevel,
flogOutputs: FlogOutput
) {
this.applicationContext = WeakReference(applicationContext)
this.isFloggingEnabled = isFloggingEnabled
this.flogTopics = flogTopics
this.flogLevels = flogLevels
this.flogOutputs = flogOutputs
}
/**
* Checks if a log message should be evaluated by checking [isFloggingEnabled] and
* by matching the given [topic] and [level] values with the configured settings.
*
* @param topic The topic(s) to check for.
* @param level The level(s) to check for.
*
* @return True if a log message should be evaluated, false otherwise.
*/
fun checkShouldFlog(topic: FlogTopic, level: FlogLevel): Boolean {
return isFloggingEnabled && (flogTopics isSet topic) && (flogLevels isSet level)
}
/**
* Extract the tag which should be used for the message from the `element`.
*/
private fun createTag(element: StackTraceElement): String {
var tag = element.className
tag = tag.substring(tag.lastIndexOf('.') + 1)
// Tag length limit was removed in API 24.
return if (tag.length <= MAX_TAG_LENGTH || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
tag
} else {
tag.substring(0, MAX_TAG_LENGTH)
}
}
private fun createMessage(element: StackTraceElement, msg: String): String {
return StringBuilder().run {
append(element.methodName)
append('(')
append(')')
if (msg.isNotBlank()) {
append(' ')
append('-')
append(' ')
append(msg)
}
toString()
}
}
private fun getStacktraceElement(): StackTraceElement {
val stackTrace = Throwable().stackTrace
check(stackTrace.size > CALL_STACK_INDEX) {
"Synthetic stacktrace didn't have enough elements: are you using proguard?"
}
return stackTrace[CALL_STACK_INDEX]
}
fun log(level: FlogLevel, msg: String) {
when {
flogOutputs isSet OUTPUT_CONSOLE -> {
if (msg.length < MAX_LOG_LENGTH) {
androidLog(level, msg)
} else {
// Split by line, then ensure each line can fit into Log's maximum length.
var i = 0
val length: Int = msg.length
while (i < length) {
var newline: Int = msg.indexOf('\n', i)
newline = if (newline != -1) newline else length
do {
val end = newline.coerceAtMost(i + MAX_LOG_LENGTH)
val part: String = msg.substring(i, end)
androidLog(level, part)
i = end
} while (i < newline)
i++
}
}
}
flogOutputs isSet OUTPUT_FILE -> {
fileLog(level, msg)
}
}
}
private fun androidLog(level: FlogLevel, msg: String) {
val ste = getStacktraceElement()
val tag = createTag(ste)
val message = createMessage(ste, msg)
when {
level isSet LEVEL_ERROR -> Log.e(tag, message)
level isSet LEVEL_WARNING -> Log.w(tag, message)
level isSet LEVEL_INFO -> Log.i(tag, message)
level isSet LEVEL_DEBUG -> Log.d(tag, message)
level isSet LEVEL_WTF -> Log.wtf(tag, message)
}
}
private fun fileLog(level: FlogLevel, msg: String) {
val context = applicationContext.get() ?: return
// TODO: introduce file logging here for runtime debug logging
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.
*/
@file:OptIn(ExperimentalUnsignedTypes::class)
package dev.patrickgold.florisboard.debug
/**
* This object holds all custom log topics for the [Flog] utility.
*
* _Contributors:_ if you add a new feature which is relatively large, you can
* add a new topic here, just make sure it is a 2^n value and does not
* exceed the maximum value of [FlogTopic].
*/
@Suppress("MemberVisibilityCanBePrivate", "Unused")
object LogTopic {
const val NONE: FlogTopic = Flog.TOPIC_NONE
const val OTHER: FlogTopic = Flog.TOPIC_OTHER
const val ALL: FlogTopic = Flog.TOPIC_ALL
const val IMS_EVENTS: FlogTopic = 1u
const val KEY_EVENTS: FlogTopic = 2u
const val SUBTYPE_MANAGER: FlogTopic = 4u
const val LAYOUT_MANAGER: FlogTopic = 8u
const val TEXT_KEYBOARD_VIEW: FlogTopic = 16u
const val GESTURES: FlogTopic = 32u
const val GLIDE: FlogTopic = 512u
const val CLIPBOARD: FlogTopic = 1024u
const val CRASH_UTILITY: FlogTopic = 2048u
}

View File

@@ -97,7 +97,7 @@ class ClipboardHistoryItemAdapter(
viewHolder.imgView.visibility = GONE
// For very large images, this can take a bit
FlorisClipboardManager.getInstance().executor.execute {
val resolver = FlorisBoard.getInstance().context.contentResolver
val resolver = FlorisBoard.getInstance().contentResolver
val inputStream = resolver.openInputStream(uri!!)
val drawable = Drawable.createFromStream(inputStream, "clipboard URI")

View File

@@ -1,8 +1,6 @@
package dev.patrickgold.florisboard.ime.clip
import android.annotation.SuppressLint
import android.os.Handler
import android.os.Looper
import android.view.MotionEvent
import android.view.View
import android.widget.*
@@ -15,7 +13,7 @@ import dev.patrickgold.florisboard.ime.core.FlorisBoard
import dev.patrickgold.florisboard.ime.core.InputKeyEvent
import dev.patrickgold.florisboard.ime.core.InputView
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.key.KeyData
import dev.patrickgold.florisboard.ime.text.keyboard.BasicTextKeyData
import kotlinx.coroutines.*
import kotlin.math.pow
@@ -23,10 +21,9 @@ import kotlin.math.pow
* Handles the clipboard view and allows for communication between UI and logic.
*/
class ClipboardInputManager private constructor() : CoroutineScope by MainScope(),
FlorisBoard.EventListener{
FlorisBoard.EventListener {
private val florisboard = FlorisBoard.getInstance()
private var repeatedKeyPressHandler: Handler? = null
private var recyclerView: RecyclerView? = null
private var adapter: ClipboardHistoryItemAdapter? = null
@@ -46,20 +43,13 @@ class ClipboardInputManager private constructor() : CoroutineScope by MainScope(
florisboard.addEventListener(this)
}
override fun onCreateInputView() {
super.onCreateInputView()
repeatedKeyPressHandler = Handler(florisboard.context.mainLooper)
}
/**
* Called when a new input view has been registered. Used to initialize all media-relevant
* views and layouts.
*/
@SuppressLint("ClickableViewAccessibility")
override fun onRegisterInputView(inputView: InputView) {
launch(Dispatchers.Default) {
inputView.findViewById<ImageButton>(R.id.back_to_keyboard_button)
.setOnTouchListener { view, event -> onButtonPressEvent(view, event) }
@@ -82,16 +72,14 @@ class ClipboardInputManager private constructor() : CoroutineScope by MainScope(
* Clean-up of resources and stopping all coroutines.
*/
override fun onDestroy() {
cancel()
instance = null
}
/**
* Returns a reference to the [ClipboardHistoryView]
*/
fun getClipboardHistoryView() : ClipboardHistoryView{
fun getClipboardHistoryView(): ClipboardHistoryView{
return FlorisBoard.getInstance().inputView?.mainViewFlipper?.getChildAt(2) as ClipboardHistoryView
}
@@ -144,13 +132,14 @@ class ClipboardInputManager private constructor() : CoroutineScope by MainScope(
* Handles clicks on the back to keyboard button.
*/
private fun onButtonPressEvent(view: View, event: MotionEvent?): Boolean {
event ?: return false
val data = when (view.id) {
R.id.back_to_keyboard_button -> KeyData(code = KeyCode.SWITCH_TO_TEXT_CONTEXT)
R.id.clear_clipboard_history -> KeyData(code = KeyCode.CLEAR_CLIPBOARD_HISTORY)
R.id.back_to_keyboard_button -> BasicTextKeyData(code = KeyCode.SWITCH_TO_TEXT_CONTEXT)
R.id.clear_clipboard_history -> BasicTextKeyData(code = KeyCode.CLEAR_CLIPBOARD_HISTORY)
else -> null
}!!
} ?: return false
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
florisboard.keyPressVibrate()
@@ -176,7 +165,7 @@ class ClipboardInputManager private constructor() : CoroutineScope by MainScope(
* @param pins The pins to link to
*/
fun initClipboard(dataSet: ArrayDeque<FlorisClipboardManager.TimedClipData>, pins: ArrayDeque<ClipboardItem>) {
this.adapter = ClipboardHistoryItemAdapter(dataSet = dataSet, pins= pins)
this.adapter = ClipboardHistoryItemAdapter(dataSet = dataSet, pins = pins)
}
/**
@@ -189,7 +178,7 @@ class ClipboardInputManager private constructor() : CoroutineScope by MainScope(
fun clearClipboardWithAnimation(start: Int, size: Int): Long {
// list of views to animate
val views = arrayListOf<View>()
for(i in 0 until size){
for (i in 0 until size) {
recyclerView?.findViewHolderForLayoutPosition(i + start)?.let {
views.add(it.itemView)
}
@@ -205,14 +194,13 @@ class ClipboardInputManager private constructor() : CoroutineScope by MainScope(
}
// a little while later we reset the views so they can be reused.
Handler(Looper.getMainLooper()).postDelayed({
launch(Dispatchers.Main) {
delay(450 + delay)
for (view in views) {
view.translationX = 0f
}
}, 450 + delay)
}
return 280 + delay
}
}

View File

@@ -38,7 +38,6 @@ import kotlin.collections.ArrayDeque
* [ClipboardPopupView] is the view representing a popup displayed when long pressing on a clipboard history item.
*/
class FlorisClipboardManager private constructor() : ClipboardManager.OnPrimaryClipChangedListener, Closeable {
private lateinit var pinsDao: PinnedClipboardItemDao
lateinit var executor: ExecutorService
@@ -102,7 +101,6 @@ class FlorisClipboardManager private constructor() : ClipboardManager.OnPrimaryC
}
}
/**
* Adds a new item to the clipboard history (if enabled).
*/
@@ -119,12 +117,10 @@ class FlorisClipboardManager private constructor() : ClipboardManager.OnPrimaryC
ClipboardInputManager.getInstance().notifyItemRangeRemoved(history.size, numRemoved)
}
val timed = TimedClipData(newData, System.currentTimeMillis())
history.addFirst(timed)
ClipboardInputManager.getInstance().notifyItemInserted(pins.size)
}
}
/**
@@ -153,7 +149,6 @@ class FlorisClipboardManager private constructor() : ClipboardManager.OnPrimaryC
onPrimaryClipChangedListeners.forEach { it.onPrimaryClipChanged() }
}
/**
* Change the current text on clipboard, update history (if enabled).
*
@@ -196,37 +191,37 @@ class FlorisClipboardManager private constructor() : ClipboardManager.OnPrimaryC
*/
override fun onPrimaryClipChanged() {
// Run on async thread to avoid blocking.
if (systemClipboardManager.primaryClip?.getItemAt(0)?.text == null &&
systemClipboardManager.primaryClip?.getItemAt(0)?.uri == null) {
val internalPrimaryClip = primaryClip
val systemPrimaryClip = systemClipboardManager.primaryClip
if (systemPrimaryClip?.getItemAt(0)?.text == null &&
systemPrimaryClip?.getItemAt(0)?.uri == null) {
return
}
val isEqual = when (primaryClip?.type) {
ItemType.TEXT -> primaryClip?.text == systemClipboardManager.primaryClip?.getItemAt(0)?.text
ItemType.IMAGE -> primaryClip?.uri == systemClipboardManager.primaryClip?.getItemAt(0)?.uri
null -> false
val isEqual = when (internalPrimaryClip?.type) {
ItemType.TEXT -> internalPrimaryClip.text == systemPrimaryClip.getItemAt(0)?.text
ItemType.IMAGE -> internalPrimaryClip.uri == systemPrimaryClip.getItemAt(0)?.uri
else -> false
}
systemClipboardManager.primaryClip?.let {
if (prefHelper.clipboard.enableInternal) {
// In the event that the internal clipboard is enabled, sync to internal clipboard is enabled
// and the item is not already in internal clipboard, add it.
if (prefHelper.clipboard.syncToFloris && !isEqual) {
addNewClip(ClipboardItem.fromClipData(it, true))
}
} else if (prefHelper.clipboard.enableHistory) {
// in the event history is enabled, and it should be updated it is updated
if (shouldUpdateHistory) {
updateHistory(ClipboardItem.fromClipData(it, false))
} else {
shouldUpdateHistory = true
}
if (prefHelper.clipboard.enableInternal) {
// In the event that the internal clipboard is enabled, sync to internal clipboard is enabled
// and the item is not already in internal clipboard, add it.
if (prefHelper.clipboard.syncToFloris && !isEqual) {
addNewClip(ClipboardItem.fromClipData(systemPrimaryClip, true))
}
} else if (prefHelper.clipboard.enableHistory) {
// in the event history is enabled, and it should be updated it is updated
if (shouldUpdateHistory) {
updateHistory(ClipboardItem.fromClipData(systemPrimaryClip, false))
} else {
shouldUpdateHistory = true
}
}
}
fun hasPrimaryClip(): Boolean {
return this.primaryClip != null
return primaryClip != null
}
/**
@@ -242,21 +237,19 @@ class FlorisClipboardManager private constructor() : ClipboardManager.OnPrimaryC
/**
* Initialize the floris clipboard manager. Exists to avoid dependency loop due to reference
* to [FlorisBoard.context]
* to [FlorisBoard].
*
* Sets up the clipboard cleanup task, links the recycler view in clipInputManager to [history].
*
* @param context Required to register as an onPrimaryClipChangedListener of ClipboardManager
*/
fun initialize(context: Context) {
this.systemClipboardManager = (context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager)
systemClipboardManager = (context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager)
systemClipboardManager.addPrimaryClipChangedListener(this)
prefHelper = PrefHelper.getDefaultInstance(context)
val cleanUpClipboard = Runnable {
if (!prefHelper.clipboard.cleanUpOld) {
return@Runnable
}
@@ -288,7 +281,6 @@ class FlorisClipboardManager private constructor() : ClipboardManager.OnPrimaryC
}
}
/**
* Clears the history with an animation.
*/
@@ -400,6 +392,4 @@ class FlorisClipboardManager private constructor() : ClipboardManager.OnPrimaryC
}
} == true
}
}

View File

@@ -1,45 +1,39 @@
package dev.patrickgold.florisboard.ime.clip.provider
import android.net.Uri
import dev.patrickgold.florisboard.debug.*
import dev.patrickgold.florisboard.ime.core.FlorisBoard
import timber.log.Timber
import java.io.File
import java.io.InputStream
/**
* Backend class which is used by [FlorisContentProvider] to serve content.
* Backend helper object which is used by [FlorisContentProvider] to serve content.
*/
class FileStorage private constructor() {
companion object {
private const val BUF_SIZE = 1024 * 8
private var instance: FileStorage? = null
private var offset = 0
fun getInstance() : FileStorage {
if (this.instance == null){
this.instance = FileStorage()
}
return instance!!
}
}
object FileStorage {
private const val BUF_SIZE = 1024 * 8
private var offset = 0
/**
* Clones a content URI to internal storage.
*
* @param uri The URI
* @return the file's name which is a unique long
* @return The file's name which is a unique long
*/
@Synchronized
fun cloneURI(uri: Uri) : Long {
val context = FlorisBoard.getInstance().context
fun cloneURI(uri: Uri): Result<Long> {
val context = FlorisBoard.getInstance()
// nanoTime + the number of items created so that it's unique.
val name = (System.nanoTime() + offset)
// Just a normal copy from input stream to output stream.
val source = context.contentResolver.openInputStream(uri)!!
val source: InputStream
try {
source = context.contentResolver.openInputStream(uri) ?: return Result.failure(
NullPointerException("Input stream for given URI '$uri' is null!")
)
} catch (e: Exception) {
return Result.failure(e)
}
val sink = File(context.filesDir, name.toString()).outputStream()
var nread = 0L
val buf = ByteArray(BUF_SIZE)
@@ -52,14 +46,14 @@ class FileStorage private constructor() {
source.close()
sink.close()
return name
return Result.success(name)
}
/**
* Deletes the file corresponding to an id.
*/
fun deleteById(id: Long) {
Timber.d("Cleaning up $id")
flogDebug(LogTopic.CLIPBOARD) { "Cleaning up $id" }
val file = File(FlorisBoard.getInstance().filesDir, id.toString())
file.delete()
}
@@ -70,6 +64,4 @@ class FileStorage private constructor() {
fun getAddress(id: Long): String {
return FlorisBoard.getInstance().filesDir.toString() + "/$id"
}
}

View File

@@ -6,10 +6,13 @@ import android.net.Uri
import android.os.ParcelFileDescriptor
import androidx.room.Room
import dev.patrickgold.florisboard.BuildConfig
import dev.patrickgold.florisboard.ime.core.FlorisBoard
import dev.patrickgold.florisboard.debug.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.File
import java.util.concurrent.ExecutorService
/**
* Allows apps to access images on the clipboard.
@@ -20,7 +23,27 @@ import java.util.concurrent.ExecutorService
class FlorisContentProvider : ContentProvider() {
private lateinit var fileUriDao: FileUriDao
private val mimeTypes: HashMap<Long, Array<String>> = hashMapOf()
private lateinit var executor: ExecutorService
private val ioScope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
companion object {
const val AUTHORITY = "${BuildConfig.APPLICATION_ID}.provider.clip"
val CONTENT_URI: Uri = Uri.parse("content://$AUTHORITY")
val CLIPS_URI: Uri = Uri.parse("content://$AUTHORITY/clips")
private var instance: FlorisContentProvider? = null
fun getInstance(): FlorisContentProvider {
return instance!!
}
private const val CLIPS_TABLE = 1
private const val CLIP_ITEM = 0
val matcher: UriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
addURI(AUTHORITY, "clips/#", CLIP_ITEM)
addURI(AUTHORITY, "clips", CLIPS_TABLE)
}
}
override fun onCreate(): Boolean {
instance = this
@@ -32,13 +55,11 @@ class FlorisContentProvider : ContentProvider() {
return
}
fileUriDao = Room.databaseBuilder(
context!!,
FileUriDatabase::class.java, "fileuridb"
).build().fileUriDao()
executor = FlorisBoard.getInstance().asyncExecutor
for (fileUri in fileUriDao.getAll()) {
mimeTypes[fileUri.fileName] = fileUri.mimeTypes
}
@@ -65,7 +86,7 @@ class FlorisContentProvider : ContentProvider() {
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor {
val id = ContentUris.parseId(uri)
val path = File(FileStorage.getInstance().getAddress(id))
val path = File(FileStorage.getAddress(id))
// Nothing has permission to write anyway.
return ParcelFileDescriptor.open(path, ParcelFileDescriptor.MODE_READ_ONLY)
@@ -74,16 +95,18 @@ class FlorisContentProvider : ContentProvider() {
override fun insert(uri: Uri, values: ContentValues?): Uri {
when (matcher.match(uri)){
CLIPS_TABLE -> {
val id = FileStorage.getInstance().cloneURI(Uri.parse(values?.getAsString("uri")))
val id = FileStorage.cloneURI(Uri.parse(values?.getAsString("uri"))).getOrElse {
flogError(LogTopic.CLIPBOARD) { it.toString() }
return uri.buildUpon().appendPath("0").build()
}
val mimes = values?.getAsString("mimetypes")?.split(",")?.toTypedArray()
mimes?.let {
mimeTypes[id] = mimes
executor.execute {
ioScope.launch {
Timber.d("Inserted file uri $id")
fileUriDao.insert(FileUri(id, mimes))
}
}
return ContentUris.withAppendedId(CLIPS_URI, id)
}
else -> throw IllegalArgumentException("Don't know what this is $uri")
@@ -94,10 +117,10 @@ class FlorisContentProvider : ContentProvider() {
when (matcher.match(uri)){
CLIP_ITEM -> {
val id = ContentUris.parseId(uri)
FileStorage.getInstance().deleteById(id)
FileStorage.deleteById(id)
mimeTypes.remove(id)
context?.revokeUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
executor.execute {
ioScope.launch {
fileUriDao.delete(id)
}
return 1
@@ -109,23 +132,4 @@ class FlorisContentProvider : ContentProvider() {
override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?): Int {
throw IllegalArgumentException("This ContentProvider does not support update.")
}
companion object {
private var instance: FlorisContentProvider? = null
const val AUTHORITY = "${BuildConfig.APPLICATION_ID}.provider.clip"
val CONTENT_URI: Uri = Uri.parse("content://$AUTHORITY")
val CLIPS_URI: Uri = Uri.parse("content://$AUTHORITY/clips")
fun getInstance(): FlorisContentProvider {
return instance!!
}
private const val CLIPS_TABLE = 1
private const val CLIP_ITEM = 0
val matcher: UriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
addURI(AUTHORITY, "clips/#", CLIP_ITEM)
addURI(AUTHORITY, "clips", CLIPS_TABLE)
}
}
}

View File

@@ -5,10 +5,10 @@ import android.content.ContentValues
import android.net.Uri
import android.provider.BaseColumns
import androidx.room.*
import dev.patrickgold.florisboard.ime.clip.FlorisClipboardManager
import dev.patrickgold.florisboard.ime.core.FlorisBoard
import java.io.Closeable
enum class ItemType(val value: Int) {
TEXT(1),
IMAGE(2);
@@ -20,7 +20,6 @@ enum class ItemType(val value: Int) {
}
}
/**
* Represents an item on the clipboard.
* The URI stored belongs to FlorisContentProvider, not whatever app copied the image
@@ -43,7 +42,7 @@ data class ClipboardItem(
fun toClipData(): ClipData {
return when (type) {
ItemType.IMAGE -> {
ClipData.newUri(FlorisBoard.getInstance().context.contentResolver, "Clipboard data", uri)
ClipData.newUri(FlorisBoard.getInstance().contentResolver, "Clipboard data", uri)
}
ItemType.TEXT -> {
ClipData.newPlainText("Clipboard data", text)
@@ -56,7 +55,7 @@ data class ClipboardItem(
*/
override fun close() {
if (type == ItemType.IMAGE) {
FlorisBoard.getInstance().context.contentResolver.delete(this.uri!!, null, null)
FlorisBoard.getInstance().contentResolver.delete(this.uri!!, null, null)
}
}
@@ -105,12 +104,10 @@ data class ClipboardItem(
* @param cloneUri Whether to store the image using [FlorisContentProvider].
*/
fun fromClipData(data: ClipData, cloneUri: Boolean) : ClipboardItem {
val type = when {
data.getItemAt(0)?.uri != null -> ItemType.IMAGE
data.getItemAt(0)?.text != null -> ItemType.TEXT
else -> null
}!!
data.getItemAt(0)?.uri != null && data.description.hasMimeType("image/*") -> ItemType.IMAGE
else -> ItemType.TEXT
}
val uri = if (type == ItemType.IMAGE) {
if (data.getItemAt(0).uri.authority == FlorisContentProvider.CONTENT_URI.authority || !cloneUri){
@@ -120,11 +117,11 @@ data class ClipboardItem(
put("uri", data.getItemAt(0).uri.toString())
put("mimetypes", data.description.filterMimeTypes("*/*").joinToString(","))
}
FlorisBoard.getInstance().context.contentResolver.insert(FlorisContentProvider.CLIPS_URI, values)
FlorisBoard.getInstance().contentResolver.insert(FlorisContentProvider.CLIPS_URI, values)
}
} else { null }
val text = data.getItemAt(0).text?.toString()
val text = FlorisBoard.getInstanceOrNull()?.let { data.getItemAt(0).coerceToText(it).toString() } ?: "#ERROR"
val mimeTypes = when (type) {
ItemType.IMAGE -> {
(0 until data.description.mimeTypeCount).map {
@@ -174,7 +171,6 @@ class Converters {
}
}
@Dao
interface PinnedClipboardItemDao {
@Query("SELECT * FROM pins")
@@ -199,7 +195,7 @@ abstract class PinnedItemsDatabase : RoomDatabase() {
if (instance == null) {
instance = Room.databaseBuilder(
FlorisBoard.getInstance().context,
FlorisBoard.getInstance(),
PinnedItemsDatabase::class.java,
"pins").build()
}

View File

@@ -28,11 +28,13 @@ import android.view.KeyEvent
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputConnection
import androidx.annotation.RequiresApi
import androidx.core.text.isDigitsOnly
import androidx.core.view.inputmethod.InputConnectionCompat
import androidx.core.view.inputmethod.InputContentInfoCompat
import dev.patrickgold.florisboard.ime.clip.FlorisClipboardManager
import dev.patrickgold.florisboard.ime.clip.provider.ClipboardItem
import dev.patrickgold.florisboard.ime.clip.provider.ItemType
import dev.patrickgold.florisboard.ime.text.TextInputManager
import timber.log.Timber
/**
@@ -91,7 +93,9 @@ class EditorInstance private constructor(
}
fun from(editorInfo: EditorInfo?, ims: InputMethodService?): EditorInstance {
return if (editorInfo == null) { default() } else {
return if (editorInfo == null) {
default()
} else {
EditorInstance(
ims = ims,
imeOptions = ImeOptions.fromImeOptionsInt(editorInfo.imeOptions),
@@ -213,38 +217,23 @@ class EditorInstance private constructor(
/**
* Commit a word generated by a gesture.
*/
fun commitGesture(text: String): Boolean{
fun commitGesture(text: String): Boolean {
val ic = inputConnection ?: return false
return if (isRawInputEditor) {
false
} else {
ic.beginBatchEdit()
ic.finishComposingText()
if (selection.start > 0 && getTextBeforeCursor(1).isNotBlank()) {
ic.commitText(" ", 1)
if (selection.start > 0) {
val previous = getTextBeforeCursor(1)
if (CachedInput.isWordComponent(previous) ||
previous.isDigitsOnly() ||
previous in TextInputManager.getInstance().symbolsWithSpaceAfter
) {
ic.commitText(" ", 1)
}
}
ic.commitText(text, 1)
isPhantomSpaceActive = true
wasPhantomSpaceActiveLastUpdate = false
ic.endBatchEdit()
true
}
}
/**
* Replaces the previous word with the given [text]. Used to correct gestures.
*/
fun commitGestureCorrection(text: String): Boolean {
val ic = inputConnection ?: return false
return if (isRawInputEditor) {
false
} else {
ic.beginBatchEdit()
markComposingRegion(Region(this, cachedInput.getWordForIndex(-1).start, cachedInput.getWordForIndex(-1).end))
ic.commitText(text, 1)
markComposingRegion(null)
isPhantomSpaceActive = true
wasPhantomSpaceActiveLastUpdate = false
ic.endBatchEdit()
true
}
@@ -260,7 +249,7 @@ class EditorInstance private constructor(
*/
fun commitClipboardItem(item: ClipboardItem): Boolean {
val mimeTypes = item.mimeTypes
return when (item.type){
return when (item.type) {
ItemType.IMAGE -> {
val inputContentInfo = InputContentInfoCompat(
item.uri!!,
@@ -272,8 +261,12 @@ class EditorInstance private constructor(
var flags = 0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
flags = flags or InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION
}else {
FlorisBoard.getInstance().context.grantUriPermission(editorInfo.packageName, item.uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
} else {
FlorisBoard.getInstance().grantUriPermission(
editorInfo.packageName,
item.uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
}
InputConnectionCompat.commitContent(ic, editorInfo, inputContentInfo, flags, null)
}
@@ -283,7 +276,6 @@ class EditorInstance private constructor(
}
}
/**
* Executes a backward delete on this editor's text. If a text selection is active, all
* characters inside this selection will be removed, else only the left-most character from
@@ -317,13 +309,18 @@ class EditorInstance private constructor(
markComposingRegion(null)
try {
getWordsInString(cachedInput.rawText.substring(0,
(selection.start - cachedInput.offset).coerceAtLeast(0))).run {
getWordsInString(
cachedInput.rawText.substring(
0,
(selection.start - cachedInput.offset).coerceAtLeast(0)
)
).run {
get(size - n.coerceAtLeast(0)).range
}.run {
ic.setSelection(first + cachedInput.offset, selection.start)
}
} catch (e: Exception) {}
} catch (e: Exception) {
}
ic.commitText("", 1)
@@ -372,7 +369,7 @@ class EditorInstance private constructor(
* @param string String to select words from
* @return Words in [string] as a List of [MatchResult]
*/
private fun getWordsInString(string: String):List<MatchResult>{
private fun getWordsInString(string: String): List<MatchResult> {
val wordRegexPattern = "[\\p{L}]+".toRegex()
return wordRegexPattern.findAll(
string
@@ -384,14 +381,15 @@ class EditorInstance private constructor(
*
* @return True on success, false if no new words are selected.
*/
fun leftAppendWordToSelection(): Boolean{
fun leftAppendWordToSelection(): Boolean {
isPhantomSpaceActive = false
wasPhantomSpaceActiveLastUpdate = false
// no words left to select
if (selection.start <= 0) {
return false
}
val stringBeforeSelection = cachedInput.rawText.substring(0, (selection.start - cachedInput.offset).coerceAtLeast(0))
val stringBeforeSelection =
cachedInput.rawText.substring(0, (selection.start - cachedInput.offset).coerceAtLeast(0))
getWordsInString(stringBeforeSelection).last().range.apply {
selection.updateAndNotify(first + cachedInput.offset, selection.end)
}
@@ -403,7 +401,7 @@ class EditorInstance private constructor(
*
* @return True on success, false if no new words are deselected.
*/
fun leftPopWordFromSelection(): Boolean{
fun leftPopWordFromSelection(): Boolean {
isPhantomSpaceActive = false
wasPhantomSpaceActiveLastUpdate = false
// no words left to pop
@@ -667,6 +665,7 @@ class ImeOptions private constructor(imeOptions: Int) {
val flagNoEnterAction: Boolean = imeOptions and EditorInfo.IME_FLAG_NO_ENTER_ACTION > 0
val flagNoExtractUi: Boolean = imeOptions and EditorInfo.IME_FLAG_NO_EXTRACT_UI > 0
val flagNoFullscreen: Boolean = imeOptions and EditorInfo.IME_FLAG_NO_FULLSCREEN > 0
@RequiresApi(Build.VERSION_CODES.O)
val flagNoPersonalizedLearning: Boolean = imeOptions and EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING > 0
@@ -886,12 +885,15 @@ open class Region(
/** The start marker for this region. */
var start: Int = initStart ?: -1
private set
/** The end marker for this region. */
var end: Int = initEnd ?: -1
private set
/** Returns true if the region's start and end markers are valid, false otherwise. */
val isValid: Boolean
get() = start >= 0 && end >= 0 && length >= 0
/** The length of the region. */
val length: Int
get() = end - start
@@ -906,7 +908,9 @@ open class Region(
val eiText = editorInstance.cachedInput.rawText
val eiStart = (start - editorInstance.cachedInput.offset).coerceIn(0, eiText.length)
val eiEnd = (end - editorInstance.cachedInput.offset).coerceIn(0, eiText.length)
return if (!isValid || eiEnd - eiStart <= 0) { "" } else {
return if (!isValid || eiEnd - eiStart <= 0) {
""
} else {
eiText.substring(eiStart, eiEnd)
}
}

View File

@@ -19,6 +19,8 @@ package dev.patrickgold.florisboard.ime.core
import android.app.Application
import dev.patrickgold.florisboard.BuildConfig
import dev.patrickgold.florisboard.crashutility.CrashUtility
import dev.patrickgold.florisboard.debug.Flog
import dev.patrickgold.florisboard.debug.LogTopic
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
import dev.patrickgold.florisboard.ime.extension.AssetManager
import dev.patrickgold.florisboard.ime.theme.ThemeManager
@@ -26,15 +28,24 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
import timber.log.Timber
@Suppress("unused")
class FlorisApplication : Application(), CoroutineScope by MainScope() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
Flog.install(
applicationContext = this,
isFloggingEnabled = BuildConfig.DEBUG,
flogTopics = LogTopic.ALL,
flogLevels = Flog.LEVEL_ALL,
flogOutputs = Flog.OUTPUT_CONSOLE
)
CrashUtility.install(this)
val prefHelper = PrefHelper.getDefaultInstance(this)
val assetManager = AssetManager.init(this)
SubtypeManager.init(this, prefHelper)
DictionaryManager.init(this)
ThemeManager.init(this, assetManager, prefHelper)
prefHelper.initDefaultPreferences()

View File

@@ -25,7 +25,6 @@ import android.inputmethodservice.ExtractEditText
import android.inputmethodservice.InputMethodService
import android.media.AudioManager
import android.os.Build
import android.os.Handler
import android.os.VibrationEffect
import android.os.Vibrator
import android.provider.Settings
@@ -35,29 +34,33 @@ import android.view.inputmethod.InputConnection
import android.view.inputmethod.InputMethodManager
import android.widget.Button
import android.widget.FrameLayout
import androidx.annotation.StyleRes
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.lifecycle.*
import com.squareup.moshi.Json
import dev.patrickgold.florisboard.BuildConfig
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.debug.*
import dev.patrickgold.florisboard.ime.clip.ClipboardInputManager
import dev.patrickgold.florisboard.ime.clip.FlorisClipboardManager
import dev.patrickgold.florisboard.ime.keyboard.KeyData
import dev.patrickgold.florisboard.ime.landscapeinput.LandscapeInputUiMode
import dev.patrickgold.florisboard.ime.media.MediaInputManager
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
import dev.patrickgold.florisboard.ime.popup.PopupLayerView
import dev.patrickgold.florisboard.ime.text.TextInputManager
import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
import dev.patrickgold.florisboard.ime.text.key.CurrencySet
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.key.KeyData
import dev.patrickgold.florisboard.ime.text.key.*
import dev.patrickgold.florisboard.ime.text.keyboard.KeyboardMode
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import dev.patrickgold.florisboard.ime.theme.Theme
import dev.patrickgold.florisboard.ime.theme.ThemeManager
import dev.patrickgold.florisboard.setup.SetupActivity
import dev.patrickgold.florisboard.util.*
import timber.log.Timber
import kotlinx.coroutines.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import java.lang.ref.WeakReference
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.ExecutorService
@@ -66,6 +69,8 @@ import java.util.concurrent.Executors
/**
* Variable which holds the current [FlorisBoard] instance. To get this instance from another
* package, see [FlorisBoard.getInstance].
* TODO: The end goal is to have no static field for service/manager class anymore. This will take a long time to
* rework the codebase but it should be doable.
*/
private var florisboardInstance: FlorisBoard? = null
@@ -76,13 +81,22 @@ private var florisboardInstance: FlorisBoard? = null
class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager.OnPrimaryClipChangedListener,
ThemeManager.OnThemeUpdatedListener {
private val serviceLifecycleDispatcher: ServiceLifecycleDispatcher = ServiceLifecycleDispatcher(this)
private val uiScope: LifecycleCoroutineScope
get() = lifecycle.coroutineScope
private var devtoolsOverlaySyncJob: Job? = null
/**
* The theme context for the UI. Must only be used for inflating/creating Views for the keyboard UI, else the IME
* service class should be used directly.
*/
private var _themeContext: Context? = null
private val themeContext: Context
get() = _themeContext ?: this
lateinit var prefs: PrefHelper
private set
val context: Context
get() = inputWindowView?.context ?: this
private val serviceLifecycleDispatcher: ServiceLifecycleDispatcher = ServiceLifecycleDispatcher(this)
private var extractEditLayout: WeakReference<ViewGroup?> = WeakReference(null)
var inputView: InputView? = null
private set
@@ -92,11 +106,10 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
private var eventListeners: CopyOnWriteArrayList<EventListener> = CopyOnWriteArrayList()
private var audioManager: AudioManager? = null
var imeManager:InputMethodManager? = null
var imeManager: InputMethodManager? = null
var florisClipboardManager: FlorisClipboardManager? = null
private val themeManager: ThemeManager = ThemeManager.default()
private var vibrator: Vibrator? = null
private val osHandler = Handler()
private var internalBatchNestingLevel: Int = 0
private val internalSelectionCache = object {
@@ -111,7 +124,7 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
var activeEditorInstance: EditorInstance = EditorInstance.default()
lateinit var subtypeManager: SubtypeManager
val subtypeManager: SubtypeManager get() = SubtypeManager.default()
lateinit var activeSubtype: Subtype
private var currentThemeIsNight: Boolean = false
private var currentThemeResId: Int = 0
@@ -135,47 +148,6 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
lateinit var asyncExecutor: ExecutorService
companion object {
private const val IME_ID: String = "dev.patrickgold.florisboard/.ime.core.FlorisBoard"
private const val IME_ID_BETA: String = "dev.patrickgold.florisboard.beta/dev.patrickgold.florisboard.ime.core.FlorisBoard"
private const val IME_ID_DEBUG: String = "dev.patrickgold.florisboard.debug/dev.patrickgold.florisboard.ime.core.FlorisBoard"
fun checkIfImeIsEnabled(context: Context): Boolean {
val activeImeIds = Settings.Secure.getString(
context.contentResolver,
Settings.Secure.ENABLED_INPUT_METHODS
) ?: "(none)"
Timber.i("List of active IMEs: $activeImeIds")
return when {
BuildConfig.DEBUG -> {
activeImeIds.split(":").contains(IME_ID_DEBUG)
}
context.packageName.endsWith(".beta") -> {
activeImeIds.split(":").contains(IME_ID_BETA)
}
else -> {
activeImeIds.split(":").contains(IME_ID)
}
}
}
fun checkIfImeIsSelected(context: Context): Boolean {
val selectedImeId = Settings.Secure.getString(
context.contentResolver,
Settings.Secure.DEFAULT_INPUT_METHOD
) ?: "(none)"
Timber.i("Selected IME: $selectedImeId")
return when {
BuildConfig.DEBUG -> {
selectedImeId == IME_ID_DEBUG
}
context.packageName.endsWith(".beta") -> {
selectedImeId.split(":").contains(IME_ID_BETA)
}
else -> {
selectedImeId == IME_ID
}
}
}
@Synchronized
fun getInstance(): FlorisBoard {
@@ -199,6 +171,10 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
return serviceLifecycleDispatcher.lifecycle
}
private fun updateThemeContext(@StyleRes themeId: Int) {
_themeContext = ContextThemeWrapper(this, themeId)
}
override fun onCreate() {
/*if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
@@ -218,16 +194,15 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
.build()
)
}*/
Timber.i("onCreate()")
flogInfo(LogTopic.IMS_EVENTS)
serviceLifecycleDispatcher.onServicePreSuperOnCreate()
imeManager = getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
audioManager = getSystemService(Context.AUDIO_SERVICE) as? AudioManager
vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as? Vibrator
vibrator = getSystemService(Context.VIBRATOR_SERVICE) as? Vibrator
prefs = PrefHelper.getDefaultInstance(this)
prefs.initDefaultPreferences()
prefs.sync()
subtypeManager = SubtypeManager(this, prefs)
activeSubtype = subtypeManager.getActiveSubtype() ?: Subtype.DEFAULT
currentThemeIsNight = themeManager.activeTheme.isNightTheme
@@ -239,9 +214,10 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
AppVersionUtils.updateVersionOnInstallAndLastUse(this, prefs)
asyncExecutor = Executors.newSingleThreadExecutor()
florisClipboardManager = FlorisClipboardManager.getInstance()
florisClipboardManager!!.initialize(context)
florisClipboardManager?.addPrimaryClipChangedListener(this)
florisClipboardManager = FlorisClipboardManager.getInstance().also {
it.initialize(this)
it.addPrimaryClipChangedListener(this)
}
super.onCreate()
eventListeners.toList().forEach { it?.onCreate() }
@@ -249,11 +225,13 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
@SuppressLint("InflateParams")
override fun onCreateInputView(): View? {
Timber.i("onCreateInputView()")
flogInfo(LogTopic.IMS_EVENTS)
baseContext.setTheme(currentThemeResId)
updateThemeContext(currentThemeResId)
inputWindowView = layoutInflater.inflate(R.layout.florisboard, null) as? InputWindowView
popupLayerView = PopupLayerView(themeContext)
inputWindowView = LayoutInflater.from(themeContext).inflate(R.layout.florisboard, null) as? InputWindowView
inputWindowView?.isHapticFeedbackEnabled = true
eventListeners.toList().forEach { it?.onCreateInputView() }
@@ -291,16 +269,23 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
}
override fun onDestroy() {
Timber.i("onDestroy()")
flogInfo(LogTopic.IMS_EVENTS)
serviceLifecycleDispatcher.onServicePreSuperOnDestroy()
themeManager.unregisterOnThemeUpdatedListener(this)
florisClipboardManager!!.removePrimaryClipChangedListener(this)
florisClipboardManager!!.close()
osHandler.removeCallbacksAndMessages(null)
florisClipboardManager?.let {
it.removePrimaryClipChangedListener(this)
it.close()
florisClipboardManager = null
}
audioManager = null
imeManager = null
vibrator = null
popupLayerView = null
inputView = null
inputWindowView = null
florisboardInstance = null
serviceLifecycleDispatcher.onServicePreSuperOnDestroy()
eventListeners.toList().forEach { it?.onDestroy() }
eventListeners.clear()
super.onDestroy()
@@ -334,12 +319,11 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
}
fun registerInputView(inputView: InputView) {
Timber.i("registerInputView($inputView)")
flogInfo(LogTopic.IMS_EVENTS)
window?.window?.findViewById<View>(android.R.id.content)?.let { content ->
popupLayerView = PopupLayerView(content.context)
if (content is ViewGroup) {
content.addView(popupLayerView)
popupLayerView?.let { content.addView(it) }
}
}
this.inputView = inputView
@@ -352,14 +336,15 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
}
override fun onStartInput(attribute: EditorInfo?, restarting: Boolean) {
Timber.i("onStartInput($attribute, $restarting)")
flogInfo(LogTopic.IMS_EVENTS)
super.onStartInput(attribute, restarting)
currentInputConnection?.requestCursorUpdates(InputConnection.CURSOR_UPDATE_MONITOR)
}
override fun onStartInputView(info: EditorInfo?, restarting: Boolean) {
Timber.i("onStartInputView($info, $restarting)")
Timber.i("onStartInputView: ${info?.debugSummarize()}")
flogInfo(LogTopic.IMS_EVENTS) { "restarting=$restarting"}
flogInfo(LogTopic.IMS_EVENTS) { info?.debugSummarize() ?: "" }
super.onStartInputView(info, restarting)
activeEditorInstance = EditorInstance.from(info, this)
@@ -370,7 +355,7 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
}
override fun onFinishInputView(finishingInput: Boolean) {
Timber.i( "onFinishInputView($finishingInput)")
flogInfo(LogTopic.IMS_EVENTS) { "finishingInput=$finishingInput" }
if (finishingInput) {
activeEditorInstance = EditorInstance.default()
@@ -381,53 +366,66 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
}
override fun onFinishInput() {
Timber.i("onFinishInput()")
flogInfo(LogTopic.IMS_EVENTS)
super.onFinishInput()
currentInputConnection?.requestCursorUpdates(0)
super.onFinishInput()
}
override fun onWindowShown() {
super.onWindowShown()
if (isWindowShown) {
Timber.i("Ignoring onWindowShown()")
flogInfo(LogTopic.IMS_EVENTS) { "Ignoring (is already shown)" }
return
} else {
Timber.i("onWindowShown()")
flogInfo(LogTopic.IMS_EVENTS)
}
isWindowShown = true
prefs.sync()
val newIsNumberRowVisible = prefs.keyboard.numberRow
if (isNumberRowVisible != newIsNumberRowVisible) {
textInputManager.layoutManager.clearLayoutCache(KeyboardMode.CHARACTERS)
textInputManager.keyboards.clear(KeyboardMode.CHARACTERS)
isNumberRowVisible = newIsNumberRowVisible
}
textInputManager.layoutManager.clearLayoutCache()
themeManager.update()
updateOneHandedPanelVisibility()
activeSubtype = subtypeManager.getActiveSubtype() ?: Subtype.DEFAULT
onSubtypeChanged(activeSubtype)
setActiveInput(R.id.text_input)
super.onWindowShown()
if (prefs.devtools.enabled && prefs.devtools.showHeapMemoryStats) {
devtoolsOverlaySyncJob?.cancel()
devtoolsOverlaySyncJob = uiScope.launch(Dispatchers.Default) {
while (true) {
if (!isActive) break
withContext(Dispatchers.Main) { inputView?.invalidate() }
delay(1000)
}
}
}
eventListeners.toList().forEach { it?.onWindowShown() }
}
override fun onWindowHidden() {
super.onWindowHidden()
if (!isWindowShown) {
Timber.i("Ignoring onWindowHidden()")
flogInfo(LogTopic.IMS_EVENTS) { "Ignoring (is already hidden)" }
return
} else {
Timber.i("onWindowHidden()")
flogInfo(LogTopic.IMS_EVENTS)
}
isWindowShown = false
super.onWindowHidden()
devtoolsOverlaySyncJob?.cancel()
devtoolsOverlaySyncJob = null
eventListeners.toList().forEach { it?.onWindowHidden() }
}
override fun onConfigurationChanged(newConfig: Configuration) {
Timber.i("onConfigurationChanged($newConfig)")
flogInfo(LogTopic.IMS_EVENTS)
if (isInputViewShown) {
updateOneHandedPanelVisibility()
}
@@ -485,7 +483,7 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
)
if (internalBatchNestingLevel == 0) {
Timber.i("onUpdateSelection($oldSelStart, $oldSelEnd, $newSelStart, $newSelEnd, $candidatesStart, $candidatesEnd)")
flogInfo(LogTopic.IMS_EVENTS) { "$oldSelStart, $oldSelEnd, $newSelStart, $newSelEnd, $candidatesStart, $candidatesEnd" }
activeEditorInstance.onUpdateSelection(
oldSelStart, oldSelEnd,
newSelStart, newSelEnd,
@@ -493,7 +491,9 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
)
eventListeners.toList().forEach { it?.onUpdateSelection() }
} else {
Timber.i("onUpdateSelection($oldSelStart, $oldSelEnd, $newSelStart, $newSelEnd, $candidatesStart, $candidatesEnd): caught due to internal batch level of $internalBatchNestingLevel!")
flogInfo(LogTopic.IMS_EVENTS) {
"$oldSelStart, $oldSelEnd, $newSelStart, $newSelEnd, $candidatesStart, $candidatesEnd: caught due to internal batch level of $internalBatchNestingLevel!"
}
if (internalSelectionCache.selectionCatchCount++ == 0) {
internalSelectionCache.oldSelStart = oldSelStart
internalSelectionCache.oldSelEnd = oldSelEnd
@@ -620,28 +620,54 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
/**
* Makes a key press vibration if the user has this feature enabled in the preferences.
*/
fun keyPressVibrate() {
fun keyPressVibrate(isMovingGestureEffect: Boolean = false) {
if (prefs.keyboard.vibrationEnabled) {
var vibrationDuration = prefs.keyboard.vibrationDuration.toLong()
var vibrationStrength = prefs.keyboard.vibrationStrength
if (vibrationStrength == -1 && prefs.keyboard.vibrationEnabledSystem) {
val hapticsPerformed =
inputWindowView?.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
if (hapticsPerformed == false) {
vibrationStrength = 36
}
if (!prefs.keyboard.vibrationEnabledSystem && vibrationDuration < 0 && vibrationStrength < 0) {
return
}
if (vibrationStrength > 0) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator?.vibrate(
VibrationEffect.createOneShot(
vibrationStrength.toLong(), VibrationEffect.DEFAULT_AMPLITUDE
)
)
val hapticsPerformed = if (vibrationDuration < 0 && vibrationStrength < 0) {
if (isMovingGestureEffect && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
inputWindowView?.performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE)
} else {
@Suppress("DEPRECATION")
vibrator?.vibrate(vibrationStrength.toLong())
inputWindowView?.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
}
} else {
false
}
if (hapticsPerformed == true) {
return
}
if (vibrationDuration == -1L) {
vibrationDuration = 36
}
if (isMovingGestureEffect) {
vibrationDuration = (vibrationDuration / 8.0).toLong().coerceAtLeast(1)
}
if (vibrationStrength == -1 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrationStrength = VibrationEffect.DEFAULT_AMPLITUDE
} else if (vibrationStrength == -1) {
vibrationStrength = 36
}
if (isMovingGestureEffect && vibrationStrength > 0) {
vibrationStrength = (vibrationStrength / 2.0).toInt().coerceAtLeast(1)
} else if (isMovingGestureEffect) {
vibrationStrength = 8
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator?.vibrate(
VibrationEffect.createOneShot(
vibrationDuration, vibrationStrength
)
)
} else {
@Suppress("DEPRECATION")
vibrator?.vibrate(vibrationDuration)
}
}
}
@@ -652,10 +678,13 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
fun keyPressSound(keyData: KeyData? = null) {
if (prefs.keyboard.soundEnabled) {
val soundVolume = prefs.keyboard.soundVolume
val effect = when (keyData?.code) {
KeyCode.SPACE -> AudioManager.FX_KEYPRESS_SPACEBAR
KeyCode.DELETE -> AudioManager.FX_KEYPRESS_DELETE
KeyCode.ENTER -> AudioManager.FX_KEYPRESS_RETURN
val effect = when (keyData) {
is TextKeyData -> when (keyData.code) {
KeyCode.SPACE -> AudioManager.FX_KEYPRESS_SPACEBAR
KeyCode.DELETE -> AudioManager.FX_KEYPRESS_DELETE
KeyCode.ENTER -> AudioManager.FX_KEYPRESS_RETURN
else -> AudioManager.FX_KEYPRESS_STANDARD
}
else -> AudioManager.FX_KEYPRESS_STANDARD
}
if (soundVolume == -1 && prefs.keyboard.soundEnabledSystem) {
@@ -710,7 +739,7 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
}
}
} catch (e: Exception) {
Timber.e(e,"Unable to switch to the previous IME")
flogError { "Unable to switch to the previous IME" }
imeManager?.showInputMethodPicker()
}
}
@@ -726,22 +755,25 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
}
}
} catch (e: Exception) {
Timber.e(e,"Unable to switch to the next IME")
flogError { "Unable to switch to the next IME" }
imeManager?.showInputMethodPicker()
}
}
fun switchToPrevSubtype() {
flogInfo(LogTopic.IMS_EVENTS)
activeSubtype = subtypeManager.switchToPrevSubtype() ?: Subtype.DEFAULT
onSubtypeChanged(activeSubtype)
}
fun switchToNextSubtype() {
flogInfo(LogTopic.IMS_EVENTS)
activeSubtype = subtypeManager.switchToNextSubtype() ?: Subtype.DEFAULT
onSubtypeChanged(activeSubtype)
}
private fun onSubtypeChanged(newSubtype: Subtype) {
flogInfo(LogTopic.SUBTYPE_MANAGER) { "New subtype: $newSubtype" }
textInputManager.onSubtypeChanged(newSubtype)
mediaInputManager.onSubtypeChanged(newSubtype)
clipInputManager.onSubtypeChanged(newSubtype)
@@ -752,7 +784,7 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
R.id.text_input -> {
inputView?.mainViewFlipper?.displayedChild = 0
if (forceSwitchToCharacters) {
textInputManager.inputEventDispatcher.send(InputKeyEvent.downUp(KeyData.VIEW_CHARACTERS))
textInputManager.inputEventDispatcher.send(InputKeyEvent.downUp(TextKeyData.VIEW_CHARACTERS))
}
}
R.id.media_input -> {
@@ -762,6 +794,7 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
inputView?.mainViewFlipper?.displayedChild = 2
}
}
textInputManager.isGlidePostEffect = false
}
fun toggleOneHandedMode(isRight: Boolean) {
@@ -793,9 +826,9 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
}
}
// Delay execution so this function can return, then refresh the whole layout
osHandler.postDelayed({
uiScope.launch {
refreshLayoutOf(inputView)
}, 0)
}
}
override fun onPrimaryClipChanged() {
@@ -858,16 +891,17 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager
* @property defaultSubtypesLanguageCodes Helper list for Settings Subtype Spinner elements.
* @property defaultSubtypesLanguageNames Helper list for Settings Subtype Spinner elements.
*/
@Serializable
data class ImeConfig(
@Json(name = "package")
@SerialName("package")
val packageName: String,
val currencySets: List<CurrencySet> = listOf(),
val defaultSubtypes: List<DefaultSubtype> = listOf()
) {
val currencySetNames: List<String>
val currencySetLabels: List<String>
val defaultSubtypesLanguageCodes: List<String>
val defaultSubtypesLanguageNames: List<String>
@Transient var currencySetNames: List<String> = listOf()
@Transient var currencySetLabels: List<String> = listOf()
@Transient var defaultSubtypesLanguageCodes: List<String> = listOf()
@Transient var defaultSubtypesLanguageNames: List<String> = listOf()
init {
val tmpCurrencyList = mutableListOf<Pair<String, String>>()

View File

@@ -19,17 +19,9 @@ package dev.patrickgold.florisboard.ime.core
import android.os.SystemClock
import dev.patrickgold.florisboard.BuildConfig
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.key.KeyData
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
/**
@@ -38,14 +30,13 @@ import timber.log.Timber
* events can be dispatched.
*/
class InputEventDispatcher private constructor(
parentScope: CoroutineScope,
channelCapacity: Int,
private val mainDispatcher: CoroutineDispatcher,
private val defaultDispatcher: CoroutineDispatcher,
private val repeatableKeyCodes: IntArray
) : InputKeyEventSender {
private val channel: Channel<InputKeyEvent> = Channel(channelCapacity)
private val scope: CoroutineScope = CoroutineScope(parentScope.coroutineContext)
private val scope: CoroutineScope = CoroutineScope(defaultDispatcher + SupervisorJob())
private val pressedKeys: HashMap<Int, PressedKeyInfo> = hashMapOf()
var lastKeyEventDown: InputKeyEvent? = null
private set
@@ -67,7 +58,6 @@ class InputEventDispatcher private constructor(
/**
* Creates a new [InputEventDispatcher] instance from given arguments and returns it.
*
* @param parentScope The parent coroutine scope which this dispatcher will attach its own scope to.
* @param channelCapacity The capacity of this input channel, defaults to [DEFAULT_CHANNEL_CAPACITY].
* @param mainDispatcher The main dispatcher used to switch the context to call the receiver callbacks.
* Defaults to [Dispatchers.Main].
@@ -78,13 +68,12 @@ class InputEventDispatcher private constructor(
* @return A new [InputEventDispatcher] instance initialized with given arguments.
*/
fun new(
parentScope: CoroutineScope,
channelCapacity: Int = DEFAULT_CHANNEL_CAPACITY,
mainDispatcher: CoroutineDispatcher = Dispatchers.Main,
defaultDispatcher: CoroutineDispatcher = Dispatchers.Default,
repeatableKeyCodes: IntArray = intArrayOf()
): InputEventDispatcher = InputEventDispatcher(
parentScope, channelCapacity, mainDispatcher, defaultDispatcher, repeatableKeyCodes.clone()
channelCapacity, mainDispatcher, defaultDispatcher, repeatableKeyCodes.clone()
)
}
@@ -209,7 +198,7 @@ class InputEventDispatcher private constructor(
data class InputKeyEvent(
val eventTime: Long,
val action: Action,
val data: KeyData,
val data: TextKeyData,
val count: Int
) {
companion object {
@@ -220,7 +209,7 @@ data class InputKeyEvent(
*
* @return The created input key event.
*/
fun down(keyData: KeyData): InputKeyEvent {
fun down(keyData: TextKeyData): InputKeyEvent {
return InputKeyEvent(
eventTime = SystemClock.uptimeMillis(),
action = Action.DOWN,
@@ -237,7 +226,7 @@ data class InputKeyEvent(
*
* @return The created input key event.
*/
fun downUp(keyData: KeyData, count: Int = 1): InputKeyEvent {
fun downUp(keyData: TextKeyData, count: Int = 1): InputKeyEvent {
return InputKeyEvent(
eventTime = SystemClock.uptimeMillis(),
action = Action.DOWN_UP,
@@ -253,7 +242,7 @@ data class InputKeyEvent(
*
* @return The created input key event.
*/
fun up(keyData: KeyData): InputKeyEvent {
fun up(keyData: TextKeyData): InputKeyEvent {
return InputKeyEvent(
eventTime = SystemClock.uptimeMillis(),
action = Action.UP,
@@ -270,7 +259,7 @@ data class InputKeyEvent(
*
* @return The created input key event.
*/
fun repeat(keyData: KeyData, count: Int = 1): InputKeyEvent {
fun repeat(keyData: TextKeyData, count: Int = 1): InputKeyEvent {
return InputKeyEvent(
eventTime = SystemClock.uptimeMillis(),
action = Action.REPEAT,
@@ -286,7 +275,7 @@ data class InputKeyEvent(
*
* @return The created input key event.
*/
fun cancel(keyData: KeyData): InputKeyEvent {
fun cancel(keyData: TextKeyData): InputKeyEvent {
return InputKeyEvent(
eventTime = SystemClock.uptimeMillis(),
action = Action.CANCEL,

View File

@@ -18,6 +18,11 @@ package dev.patrickgold.florisboard.ime.core
import android.content.Context
import android.content.res.Configuration
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Typeface
import android.text.TextPaint
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.view.ViewGroup
@@ -58,6 +63,13 @@ class InputView : LinearLayout {
var oneHandedCtrlPanelEnd: ViewGroup? = null
private set
private val overlayTextPaint: TextPaint = TextPaint().apply {
color = Color.GREEN
textAlign = Paint.Align.RIGHT
textSize = resources.getDimension(R.dimen.devtools_memory_overlay_textSize)
typeface = Typeface.MONOSPACE
}
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
@@ -111,10 +123,7 @@ class InputView : LinearLayout {
baseTextInputHeight += additionalHeight
}
val smartbarDisabled = !prefs.smartbar.enabled ||
tim.keyVariation == KeyVariation.PASSWORD && prefs.keyboard.numberRow ||
tim.getActiveKeyboardMode() == KeyboardMode.NUMERIC ||
tim.getActiveKeyboardMode() == KeyboardMode.PHONE ||
tim.getActiveKeyboardMode() == KeyboardMode.PHONE2
tim.keyVariation == KeyVariation.PASSWORD && prefs.keyboard.numberRow
if (smartbarDisabled) {
baseHeight = baseTextInputHeight
baseSmartbarHeight = 0.0f
@@ -164,4 +173,34 @@ class InputView : LinearLayout {
resources.getDimension(R.dimen.inputView_baseHeight)
)
}
override fun dispatchDraw(canvas: Canvas?) {
super.dispatchDraw(canvas)
canvas ?: return
if (prefs.devtools.enabled && prefs.devtools.showHeapMemoryStats) {
try {
// Note: the below code only gets the heap size in MB, the actual RAM usage (native or others) can be
// a lot higher
// Source: https://stackoverflow.com/a/19267315/6801193
val runtime = Runtime.getRuntime()
val usedMemInMB = (runtime.totalMemory() - runtime.freeMemory()) / 1048576L
val maxHeapSizeInMB = runtime.maxMemory() / 1048576L
val availHeapSizeInMB = maxHeapSizeInMB - usedMemInMB
val output = listOf(
"heap mem:",
String.format("used=%4dMB", usedMemInMB),
String.format("max=%4dMB", maxHeapSizeInMB),
String.format("avail=%4dMB", availHeapSizeInMB),
)
val x = measuredWidth.toFloat()
var y = overlayTextPaint.descent() - overlayTextPaint.ascent()
for (line in output) {
canvas.drawText(line, x, y, overlayTextPaint)
y += overlayTextPaint.descent() - overlayTextPaint.ascent()
}
} catch (_: Throwable) {
}
}
}
}

View File

@@ -32,20 +32,25 @@ import dev.patrickgold.florisboard.ime.text.smartbar.CandidateView
import dev.patrickgold.florisboard.ime.theme.ThemeMode
import dev.patrickgold.florisboard.util.TimeUtil
import dev.patrickgold.florisboard.util.VersionName
import java.lang.ref.WeakReference
/**
* Helper class for an organized access to the shared preferences.
*/
class PrefHelper(
private val context: Context,
context: Context,
val shared: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
) {
private val applicationContext: WeakReference<Context> = WeakReference(context.applicationContext)
private val cacheBoolean: HashMap<String, Boolean> = hashMapOf()
private val cacheInt: HashMap<String, Int> = hashMapOf()
private val cacheString: HashMap<String, String> = hashMapOf()
val advanced = Advanced(this)
val clipboard = Clipboard(this)
val correction = Correction(this)
val devtools = Devtools(this)
val gestures = Gestures(this)
val glide = Glide(this)
val internal = Internal(this)
@@ -54,7 +59,6 @@ class PrefHelper(
val smartbar = Smartbar(this)
val suggestion = Suggestion(this)
val theme = Theme(this)
val clipboard = Clipboard(this)
/**
* Checks the cache if an entry for [key] exists, else calls [getPrefInternal] to retrieve the
@@ -124,7 +128,7 @@ class PrefHelper(
}
companion object {
private val OLD_SUBTYPES_REGEX = """^([\-0-9]+\/[\-a-zA-Z0-9]+\/[a-zA-Z\_]+[;]*)+${'$'}""".toRegex()
private val OLD_SUBTYPES_REGEX = """^([\-0-9]+/[\-a-zA-Z0-9]+/[a-zA-Z_]+[;]*)+${'$'}""".toRegex()
private var defaultInstance: PrefHelper? = null
@Synchronized
@@ -141,11 +145,14 @@ class PrefHelper(
* they have not been initialized yet.
*/
fun initDefaultPreferences() {
PreferenceManager.setDefaultValues(context, R.xml.prefs_advanced, true)
PreferenceManager.setDefaultValues(context, R.xml.prefs_gestures, true)
PreferenceManager.setDefaultValues(context, R.xml.prefs_keyboard, true)
PreferenceManager.setDefaultValues(context, R.xml.prefs_theme, true)
PreferenceManager.setDefaultValues(context, R.xml.prefs_typing, true)
applicationContext.get()?.let { context ->
PreferenceManager.setDefaultValues(context, R.xml.prefs_advanced, true)
PreferenceManager.setDefaultValues(context, R.xml.prefs_gestures, true)
PreferenceManager.setDefaultValues(context, R.xml.prefs_keyboard, true)
PreferenceManager.setDefaultValues(context, R.xml.prefs_theme, true)
PreferenceManager.setDefaultValues(context, R.xml.prefs_typing, true)
}
//theme.dayThemeRef = "assets:ime/theme/floris_day.json"
//theme.nightThemeRef = "assets:ime/theme/floris_night.json"
//setPref(Localization.SUBTYPES, "-234/de-AT/euro/c=qwertz")
@@ -159,13 +166,15 @@ class PrefHelper(
* Syncs the system preference values and clears the cache.
*/
fun sync() {
val contentResolver = context.contentResolver
keyboard.soundEnabledSystem = Settings.System.getInt(
contentResolver, Settings.System.SOUND_EFFECTS_ENABLED, 0
) != 0
keyboard.vibrationEnabledSystem = Settings.System.getInt(
contentResolver, Settings.System.HAPTIC_FEEDBACK_ENABLED, 0
) != 0
applicationContext.get()?.let { context ->
val contentResolver = context.contentResolver
keyboard.soundEnabledSystem = Settings.System.getInt(
contentResolver, Settings.System.SOUND_EFFECTS_ENABLED, 0
) != 0
keyboard.vibrationEnabledSystem = Settings.System.getInt(
contentResolver, Settings.System.HAPTIC_FEEDBACK_ENABLED, 0
) != 0
}
cacheBoolean.clear()
cacheInt.clear()
@@ -214,6 +223,23 @@ class PrefHelper(
set(v) = prefHelper.setPref(REMEMBER_CAPS_LOCK_STATE, v)
}
/**
* Wrapper class for devtools preferences.
*/
class Devtools(private val prefHelper: PrefHelper) {
companion object {
const val ENABLED = "devtools__enabled"
const val SHOW_HEAP_MEMORY_STATS = "devtools__show_heap_memory_stats"
}
var enabled: Boolean
get() = prefHelper.getPref(ENABLED, false)
set(v) = prefHelper.setPref(ENABLED, v)
var showHeapMemoryStats: Boolean
get() = prefHelper.getPref(SHOW_HEAP_MEMORY_STATS, false)
set(v) = prefHelper.setPref(SHOW_HEAP_MEMORY_STATS, v)
}
/**
* Wrapper class for gestures preferences.
*/
@@ -277,6 +303,7 @@ class PrefHelper(
const val TRAIL_DURATION = "glide__trail_fade_duration"
const val SHOW_PREVIEW = "glide__show_preview"
const val PREVIEW_REFRESH_DELAY = "glide__preview_refresh_delay"
const val MAX_TRAIL_LENGTH = "glide__trail_max_length"
}
var enabled: Boolean
@@ -294,6 +321,9 @@ class PrefHelper(
var previewRefreshDelay: Int
get() = prefHelper.getPref(PREVIEW_REFRESH_DELAY, 150)
set(v) = prefHelper.setPref(PREVIEW_REFRESH_DELAY, v)
var trailMaxLength: Int
get() = prefHelper.getPref(MAX_TRAIL_LENGTH, 150)
set(v) = prefHelper.setPref(MAX_TRAIL_LENGTH, v)
}
/**
@@ -349,6 +379,7 @@ class PrefHelper(
const val UTILITY_KEY_ACTION = "keyboard__utility_key_action"
const val UTILITY_KEY_ENABLED = "keyboard__utility_key_enabled"
const val VIBRATION_ENABLED = "keyboard__vibration_enabled"
const val VIBRATION_DURATION = "keyboard__vibration_duration"
const val VIBRATION_STRENGTH = "keyboard__vibration_strength"
}
@@ -420,6 +451,9 @@ class PrefHelper(
get() = prefHelper.getPref(VIBRATION_ENABLED, true)
private set
var vibrationEnabledSystem: Boolean = false
var vibrationDuration: Int = 0
get() = prefHelper.getPref(VIBRATION_DURATION, -1)
private set
var vibrationStrength: Int = 0
get() = prefHelper.getPref(VIBRATION_STRENGTH, -1)
private set

View File

@@ -16,9 +16,14 @@
package dev.patrickgold.florisboard.ime.core
import com.squareup.moshi.Json
import dev.patrickgold.florisboard.ime.text.layout.LayoutType
import dev.patrickgold.florisboard.util.LocaleUtils
import kotlinx.serialization.*
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
import java.util.*
/**
@@ -31,11 +36,13 @@ import java.util.*
* @property layoutMap The layout map to properly display the correct layout for each layout type.
*/
data class Subtype(
var id: Int,
var locale: Locale,
var currencySetName: String,
var layoutMap: SubtypeLayoutMap,
val id: Int,
val locale: Locale,
val currencySetName: String,
val layoutMap: SubtypeLayoutMap,
) {
private val _hashCode: Int
companion object {
/**
* Subtype to use when prefs do not contain any valid subtypes.
@@ -76,6 +83,14 @@ data class Subtype(
}
}
init {
var result = id
result = 31 * result + locale.hashCode()
result = 31 * result + currencySetName.hashCode()
result = 31 * result + layoutMap.hashCode()
_hashCode = result
}
/**
* Converts this object into its string representation. Format:
* <id>/<language_tag>/<currency_set_name>/<layout_map>
@@ -85,6 +100,15 @@ data class Subtype(
return "$id/$languageTag/$currencySetName/$layoutMap"
}
/**
* Converts this object into its short string representation, used for debugging. Format:
* <id>/<language_tag>/<currency_set_name>
*/
fun toShortString(): String {
val languageTag = locale.toLanguageTag()
return "$id/$languageTag/$currencySetName"
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
@@ -100,14 +124,11 @@ data class Subtype(
}
override fun hashCode(): Int {
var result = id
result = 31 * result + locale.hashCode()
result = 31 * result + currencySetName.hashCode()
result = 31 * result + layoutMap.hashCode()
return result
return _hashCode
}
}
@Serializable
data class SubtypeLayoutMap(
val characters: String = CHARACTERS_DEFAULT,
val symbols: String = SYMBOLS_DEFAULT,
@@ -118,6 +139,8 @@ data class SubtypeLayoutMap(
val phone: String = PHONE_DEFAULT,
val phone2: String = PHONE2_DEFAULT,
) {
@Transient private var _hashCode: Int = 0
companion object {
private const val CHARACTERS_CODE = "c"
private const val SYMBOLS_CODE = "s"
@@ -172,6 +195,18 @@ data class SubtypeLayoutMap(
}
}
init {
var result = characters.hashCode()
result = 31 * result + symbols.hashCode()
result = 31 * result + symbols2.hashCode()
result = 31 * result + numeric.hashCode()
result = 31 * result + numericAdvanced.hashCode()
result = 31 * result + numericRow.hashCode()
result = 31 * result + phone.hashCode()
result = 31 * result + phone2.hashCode()
_hashCode = result
}
operator fun get(layoutType: LayoutType): String? {
return when (layoutType) {
LayoutType.CHARACTERS -> characters
@@ -257,15 +292,7 @@ data class SubtypeLayoutMap(
}
override fun hashCode(): Int {
var result = characters.hashCode()
result = 31 * result + symbols.hashCode()
result = 31 * result + symbols2.hashCode()
result = 31 * result + numeric.hashCode()
result = 31 * result + numericAdvanced.hashCode()
result = 31 * result + numericRow.hashCode()
result = 31 * result + phone.hashCode()
result = 31 * result + phone2.hashCode()
return result
return _hashCode
}
}
@@ -277,11 +304,25 @@ data class SubtypeLayoutMap(
* @property currencySetName The currency set name of this subtype. Beware its different name in json: 'currencySet'.
* @property preferred The preferred layout map for this subtype's locale.
*/
@Serializable
data class DefaultSubtype(
var id: Int,
@Json(name = "languageTag")
@Serializable(with = LocaleSerializer::class)
@SerialName("languageTag")
var locale: Locale,
@Json(name = "currencySet")
@SerialName("currencySet")
var currencySetName: String,
var preferred: SubtypeLayoutMap
)
class LocaleSerializer : KSerializer<Locale> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Locale", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Locale) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): Locale {
return LocaleUtils.stringToLocale(decoder.decodeString())
}
}

View File

@@ -17,53 +17,65 @@
package dev.patrickgold.florisboard.ime.core
import android.content.Context
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import dev.patrickgold.florisboard.debug.*
import dev.patrickgold.florisboard.ime.extension.AssetManager
import dev.patrickgold.florisboard.ime.extension.AssetRef
import dev.patrickgold.florisboard.ime.extension.AssetSource
import dev.patrickgold.florisboard.ime.text.key.CurrencySet
import dev.patrickgold.florisboard.util.LocaleUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
import java.util.*
import kotlin.collections.ArrayList
/**
* Class which acts as a high level helper for the raw implementation of subtypes in the prefs.
* Also interprets the default subtype list defined in ime/config.json and provides helper
* arrays for the language spinner.
* @property context Android context, used for interacting with the system.
* @property packageName The package name this SubtypeManager is for.
* @property prefs Reference to the preferences, where the raw subtype settings are accessible.
* @property imeConfig The [FlorisBoard.ImeConfig] of this input method editor.
* @property subtypes Dynamic property which parses the raw subtype list from prefs and returns a
* list of [Subtype]s. When setting this property, the given list is converted to a raw string
* and written to prefs.
* @property subtypes The currently active subtypes.
*/
class SubtypeManager(
private val context: Context,
private val packageName: String,
private val prefs: PrefHelper
) : CoroutineScope by MainScope() {
private val assetManager get() = AssetManager.default()
companion object {
const val IME_CONFIG_FILE_PATH = "ime/config.json"
const val SUBTYPE_LIST_STR_DELIMITER = ";"
private var instance: SubtypeManager? = null
fun init(context: Context, prefs: PrefHelper): SubtypeManager {
val defaultInstance = SubtypeManager(context.packageName, prefs)
instance = defaultInstance
return defaultInstance
}
fun default(): SubtypeManager = instance!!
fun defaultOrNull(): SubtypeManager? = instance
}
var imeConfig: FlorisBoard.ImeConfig = FlorisBoard.ImeConfig(context.packageName)
var subtypes: List<Subtype>
get() {
val listRaw = prefs.localization.subtypes
return if (listRaw.isBlank()) {
listOf()
} else {
listRaw.split(SUBTYPE_LIST_STR_DELIMITER).map {
Subtype.fromString(it)
}
}
}
set(v) {
prefs.localization.subtypes = v.joinToString(SUBTYPE_LIST_STR_DELIMITER)
}
var imeConfig: FlorisBoard.ImeConfig = FlorisBoard.ImeConfig(packageName)
private val _subtypes: ArrayList<Subtype> = ArrayList()
val subtypes: List<Subtype> get() = _subtypes
init {
imeConfig = loadImeConfig(IME_CONFIG_FILE_PATH)
val listRaw = prefs.localization.subtypes
if (listRaw.isNotBlank()) {
listRaw.split(SUBTYPE_LIST_STR_DELIMITER).forEach {
_subtypes.add(Subtype.fromString(it))
}
}
}
private fun syncSubtypeListToPrefs() {
prefs.localization.subtypes = _subtypes.joinToString(SUBTYPE_LIST_STR_DELIMITER)
}
/**
@@ -73,19 +85,10 @@ class SubtypeManager(
* @return The [FlorisBoard.ImeConfig] or a default config.
*/
private fun loadImeConfig(path: String): FlorisBoard.ImeConfig {
val rawJsonData: String = try {
context.assets.open(path).bufferedReader().use { it.readText() }
} catch (e: Exception) {
null
} ?: return FlorisBoard.ImeConfig(context.packageName)
val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.add(LocaleUtils.JsonAdapter())
.build()
val layoutAdapter = moshi.adapter(FlorisBoard.ImeConfig::class.java)
return layoutAdapter.fromJson(rawJsonData) ?: FlorisBoard.ImeConfig(
context.packageName
)
return assetManager.loadJsonAsset<FlorisBoard.ImeConfig>(AssetRef(AssetSource.Assets, path)).getOrElse {
flogError(LogTopic.SUBTYPE_MANAGER) { "Failed to retrieve IME config: $it" }
FlorisBoard.ImeConfig(packageName)
}
}
/**
@@ -96,12 +99,11 @@ class SubtypeManager(
* that the subtype already exists.
*/
private fun addSubtype(subtypeToAdd: Subtype): Boolean {
val subtypeList = subtypes.toMutableList()
if (subtypeList.contains(subtypeToAdd)) {
if (_subtypes.contains(subtypeToAdd)) {
return false
}
subtypeList.add(subtypeToAdd)
subtypes = subtypeList
_subtypes.add(subtypeToAdd)
syncSubtypeListToPrefs()
return true
}
@@ -144,12 +146,12 @@ class SubtypeManager(
* could be determined.
*/
fun getActiveSubtype(): Subtype? {
for (subtype in subtypes) {
val subtypeList = _subtypes
for (subtype in subtypeList) {
if (subtype.id == prefs.localization.activeSubtypeId) {
return subtype
}
}
val subtypeList = subtypes
return if (subtypeList.isNotEmpty()) {
prefs.localization.activeSubtypeId = subtypeList[0].id
subtypeList[0]
@@ -166,7 +168,8 @@ class SubtypeManager(
* @return The subtype or null, if no matching subtype could be found.
*/
fun getSubtypeById(id: Int): Subtype? {
for (subtype in subtypes) {
val subtypeList = _subtypes
for (subtype in subtypeList) {
if (subtype.id == id) {
return subtype
}
@@ -197,16 +200,11 @@ class SubtypeManager(
* @param subtypeToModify The subtype with the new details but same id.
*/
fun modifySubtypeWithSameId(subtypeToModify: Subtype) {
val subtypeList = subtypes
for (subtype in subtypeList) {
if (subtype.id == subtypeToModify.id) {
subtype.locale = subtypeToModify.locale
subtype.currencySetName = subtypeToModify.currencySetName
subtype.layoutMap = subtypeToModify.layoutMap
break
}
val index = _subtypes.indexOfFirst { subtypeToModify.id == it.id }
if (index >= 0 && index < _subtypes.size) {
_subtypes[index] = subtypeToModify
syncSubtypeListToPrefs()
}
subtypes = subtypeList
}
/**
@@ -216,14 +214,14 @@ class SubtypeManager(
* @param subtypeToRemove The subtype which should be removed.
*/
fun removeSubtype(subtypeToRemove: Subtype) {
val subtypeList = subtypes.toMutableList()
val subtypeList = _subtypes
for (subtype in subtypeList) {
if (subtype == subtypeToRemove) {
subtypeList.remove(subtypeToRemove)
break
}
}
subtypes = subtypeList
syncSubtypeListToPrefs()
if (subtypeToRemove.id == prefs.localization.activeSubtypeId) {
getActiveSubtype()
}
@@ -235,7 +233,7 @@ class SubtypeManager(
* @return The new active subtype or null if the determination process failed.
*/
fun switchToPrevSubtype(): Subtype? {
val subtypeList = subtypes
val subtypeList = _subtypes
val activeSubtype = getActiveSubtype() ?: return null
var triggerNextSubtype = false
var newActiveSubtype: Subtype? = null
@@ -263,7 +261,7 @@ class SubtypeManager(
* @return The new active subtype or null if the determination process failed.
*/
fun switchToNextSubtype(): Subtype? {
val subtypeList = subtypes
val subtypeList = _subtypes
val activeSubtype = getActiveSubtype() ?: return null
var triggerNextSubtype = false
var newActiveSubtype: Subtype? = null

View File

@@ -16,8 +16,6 @@
package dev.patrickgold.florisboard.ime.extension
import android.content.Context
/**
* Interface for an Asset to use within FlorisBoard. An asset is everything from a dictionary to a
* keyboard layout to a extended popup mapping, etc. Assets are very important for the splitting
@@ -46,14 +44,4 @@ interface Asset {
* valid, but the best practice is to use the GitHub username.
*/
val authors: List<String>
/**
* "Static" functions which every Asset should provide.
*/
interface Companion<T> {
/**
* Creates an empty Asset of type [T].
*/
fun empty(): T
}
}

View File

@@ -18,26 +18,45 @@ package dev.patrickgold.florisboard.ime.extension
import android.content.Context
import android.net.Uri
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import dev.patrickgold.florisboard.ime.text.key.KeyTypeAdapter
import dev.patrickgold.florisboard.ime.text.key.KeyVariationAdapter
import dev.patrickgold.florisboard.ime.text.layout.LayoutTypeAdapter
import dev.patrickgold.florisboard.ime.keyboard.CaseSelector
import dev.patrickgold.florisboard.ime.keyboard.KeyData
import dev.patrickgold.florisboard.ime.keyboard.VariationSelector
import dev.patrickgold.florisboard.ime.media.emoji.EmojiKeyData
import dev.patrickgold.florisboard.ime.text.keyboard.AutoTextKeyData
import dev.patrickgold.florisboard.ime.text.keyboard.BasicTextKeyData
import dev.patrickgold.florisboard.ime.text.keyboard.MultiTextKeyData
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic
import timber.log.Timber
import java.io.File
import kotlin.reflect.KClass
class AssetManager private constructor(private val applicationContext: Context) {
private val moshi: Moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
/*.add(PolymorphicJsonAdapterFactory.of(Asset::class.java, "\$type")
.withSubtype(PopupExtension::class.java, PopupExtension::class.qualifiedName)
.withSubtype(Theme::class.java, Theme::class.qualifiedName)
)*/
.add(LayoutTypeAdapter())
.add(KeyTypeAdapter())
.add(KeyVariationAdapter())
.build()
class AssetManager private constructor(val applicationContext: Context) {
private val json = Json {
classDiscriminator = "$"
ignoreUnknownKeys = true
isLenient = true
serializersModule = SerializersModule {
polymorphic(KeyData::class) {
subclass(BasicTextKeyData::class, BasicTextKeyData.serializer())
subclass(AutoTextKeyData::class, AutoTextKeyData.serializer())
subclass(MultiTextKeyData::class, MultiTextKeyData.serializer())
subclass(EmojiKeyData::class, EmojiKeyData.serializer())
subclass(CaseSelector::class, CaseSelector.serializer())
subclass(VariationSelector::class, VariationSelector.serializer())
default { BasicTextKeyData.serializer() }
}
polymorphic(TextKeyData::class) {
subclass(BasicTextKeyData::class, BasicTextKeyData.serializer())
subclass(AutoTextKeyData::class, AutoTextKeyData.serializer())
subclass(MultiTextKeyData::class, MultiTextKeyData.serializer())
default { BasicTextKeyData.serializer() }
}
}
}
companion object {
private var defaultInstance: AssetManager? = null
@@ -62,6 +81,8 @@ class AssetManager private constructor(private val applicationContext: Context)
fun defaultOrNull(): AssetManager? = defaultInstance
}
fun jsonBuilder(): Json = json
fun deleteAsset(ref: AssetRef): Result<Unit> {
return when (ref.source) {
AssetSource.Internal -> {
@@ -100,27 +121,23 @@ class AssetManager private constructor(private val applicationContext: Context)
}
}
fun <T : Asset> listAssets(ref: AssetRef, assetClass: KClass<T>): Result<Map<AssetRef, T>> {
inline fun <reified T> listAssets(ref: AssetRef): Result<Map<AssetRef, T>> {
val retMap = mutableMapOf<AssetRef, T>()
return when (ref.source) {
AssetSource.Assets -> {
try {
val list = applicationContext.assets.list(ref.path)
if (list != null) {
for (file in list) {
val fileRef = ref.copy(path = ref.path + "/" + file)
val assetResult = loadAsset(fileRef, assetClass)
assetResult.onSuccess { asset ->
retMap[fileRef.copy()] = asset
}.onFailure { error ->
Timber.e(error.toString())
}
AssetSource.Assets -> runCatching {
val list = applicationContext.assets.list(ref.path)
if (list != null) {
for (file in list) {
val fileRef = ref.copy(path = ref.path + "/" + file)
val assetResult = loadJsonAsset<T>(fileRef)
assetResult.onSuccess { asset ->
retMap[fileRef.copy()] = asset
}.onFailure { error ->
Timber.e(error.toString())
}
}
Result.success(retMap.toMap())
} catch (e: Exception) {
Result.failure(e)
}
retMap.toMap()
}
AssetSource.Internal -> {
val dir = File(applicationContext.filesDir.absolutePath + "/" + ref.path)
@@ -129,7 +146,7 @@ class AssetManager private constructor(private val applicationContext: Context)
it.forEach { file ->
if (file.isFile) {
val fileRef = ref.copy(path = ref.path + "/" + file.name)
val assetResult = loadAsset(fileRef, assetClass)
val assetResult = loadJsonAsset<T>(fileRef)
assetResult.onSuccess { asset ->
retMap[fileRef.copy()] = asset
}.onFailure { error ->
@@ -145,68 +162,28 @@ class AssetManager private constructor(private val applicationContext: Context)
}
}
fun <T : Asset> loadAsset(ref: AssetRef, assetClass: KClass<T>): Result<T> {
val rawJsonData = when (ref.source) {
is AssetSource.Assets -> {
try {
applicationContext.assets.open(ref.path).bufferedReader().use { it.readText() }
} catch (e: Exception) {
return Result.failure(e)
}
}
is AssetSource.Internal -> {
val file = File(applicationContext.filesDir.absolutePath + "/" + ref.path)
val contents = readFile(file)
if (contents.isBlank()) {
"{}"
} else {
contents
}
}
else -> "{}"
}
return try {
val adapter = moshi.adapter(assetClass.java)
val asset = adapter.fromJson(rawJsonData)
if (asset != null) {
Result.success(asset)
} else {
Result.failure(NullPointerException("Asset failed to load!"))
}
} catch (e: Exception) {
Result.failure(e)
}
inline fun <reified T> loadJsonAsset(ref: AssetRef): Result<T> {
return loadTextAsset(ref).fold(
onSuccess = { runCatching { jsonBuilder().decodeFromString(it) } },
onFailure = { Result.failure(it) }
)
}
fun <T : Asset> loadAsset(uri: Uri, assetClass: KClass<T>, maxSize: Int): Result<T> {
val rawJsonData = ExternalContentUtils.readTextFromUri(applicationContext, uri, maxSize).getOrElse {
return Result.failure(it)
}
return try {
val adapter = moshi.adapter(assetClass.java)
val asset = adapter.fromJson(rawJsonData)
if (asset != null) {
Result.success(asset)
} else {
Result.failure(NullPointerException("Asset failed to load!"))
}
} catch (e: Exception) {
Result.failure(e)
}
inline fun <reified T> loadJsonAsset(uri: Uri, maxSize: Int): Result<T> {
return loadTextAsset(uri, maxSize).fold(
onSuccess = { runCatching { jsonBuilder().decodeFromString(it) } },
onFailure = { Result.failure(it) }
)
}
fun loadAssetRaw(ref: AssetRef): Result<String> {
fun loadTextAsset(ref: AssetRef): Result<String> {
return when (ref.source) {
is AssetSource.Assets -> {
try {
Result.success(applicationContext.assets.open(ref.path).bufferedReader().use { it.readText() })
} catch (e: Exception) {
Result.failure(e)
}
is AssetSource.Assets -> runCatching {
applicationContext.assets.open(ref.path).bufferedReader().use { it.readText() }
}
is AssetSource.Internal -> {
val file = File(applicationContext.filesDir.absolutePath + "/" + ref.path)
val contents = readFile(file)
val contents = readTextFile(file).getOrElse { return Result.failure(it) }
if (contents.isBlank()) {
Result.failure(Exception("File is blank!"))
} else {
@@ -217,26 +194,13 @@ class AssetManager private constructor(private val applicationContext: Context)
}
}
fun <T : Asset> writeAsset(ref: AssetRef, assetClass: KClass<T>, asset: T): Result<Unit> {
return when (ref.source) {
AssetSource.Internal -> {
val adapter = moshi.adapter(assetClass.java)
val rawJson = adapter.toJson(asset)
val file = File(applicationContext.filesDir.absolutePath + "/" + ref.path)
writeToFile(file, rawJson)
Result.success(Unit)
}
else -> Result.failure(Exception("Can not write an asset in source '${ref.source}'"))
}
}
/**
* Reads a given [file] and returns its content.
*
* @param file The file object.
* @return The contents of the file or an empty string, if the file does not exist.
*/
private fun readFile(file: File): String {
private fun readTextFile(file: File) = runCatching {
val retText = StringBuilder()
if (file.exists()) {
val newLine = System.lineSeparator()
@@ -245,7 +209,35 @@ class AssetManager private constructor(private val applicationContext: Context)
retText.append(newLine)
}
}
return retText.toString()
retText.toString()
}
fun loadTextAsset(uri: Uri, maxSize: Int): Result<String> {
return ExternalContentUtils.readTextFromUri(applicationContext, uri, maxSize)
}
inline fun <reified T> writeJsonAsset(ref: AssetRef, asset: T): Result<Unit> {
return runCatching { jsonBuilder().encodeToString(asset) }.fold(
onSuccess = { writeTextAsset(ref, it) },
onFailure = { Result.failure(it) }
)
}
inline fun <reified T> writeJsonAsset(uri: Uri, asset: T): Result<Unit> {
return runCatching { jsonBuilder().encodeToString(asset) }.fold(
onSuccess = { writeTextAsset(uri, it) },
onFailure = { Result.failure(it) }
)
}
fun writeTextAsset(ref: AssetRef, text: String): Result<Unit> {
return when (ref.source) {
AssetSource.Internal -> {
val file = File(applicationContext.filesDir.absolutePath + "/" + ref.path)
writeTextFile(file, text)
}
else -> Result.failure(Exception("Can not write an asset in source '${ref.source}'"))
}
}
/**
@@ -256,17 +248,17 @@ class AssetManager private constructor(private val applicationContext: Context)
* @param text The text to write to the file.
* @return The contents of the file or an empty string, if the file does not exist.
*/
private fun writeToFile(file: File, text: String) {
try {
file.parent?.let {
val dir = File(it)
if (!dir.exists()) {
dir.mkdirs()
}
private fun writeTextFile(file: File, text: String) = runCatching {
file.parent?.let {
val dir = File(it)
if (!dir.exists()) {
dir.mkdirs()
}
file.writeText(text)
} catch (e: Exception) {
e.printStackTrace()
}
file.writeText(text)
}
fun writeTextAsset(uri: Uri, text: String): Result<Unit> {
return ExternalContentUtils.writeTextToUri(applicationContext, uri, text)
}
}

View File

@@ -0,0 +1,110 @@
/*
* 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.ime.keyboard
import android.graphics.Rect
/**
* Abstract class describing the smallest computed unit in a computed keyboard. Each key represents exactly one key
* displayed in the UI. It allows to save the absolute location within the parent keyboard, save touch and visual
* bounds, managing the state (enabled, pressed, visibility) as well as layout sizing factors. Each key in this IME
* inherits from this base key class. This allows for a inter-operable usage of a key without knowing the exact
* subclass upfront.
*
* @property data The base key data this key represents.This can be anything - from a basic text key to an emoji key
* to a complex selector.
*/
abstract class Key(open val data: KeyData) {
/**
* Specifies whether this key is enabled or not.
*/
open var isEnabled: Boolean = true
/**
* Specifies whether this key is actively pressed or not. Is used by the parent keyboard view to draw the key
* differently to indicate this state.
*/
open var isPressed: Boolean = false
/**
* Specifies whether this key is visible or not. Is used by the parent keyboard view to omit this key in the
* layout and drawing process. A `false`-value is equivalent to `VISIBILITY_GONE` on Android's View class.
*/
open var isVisible: Boolean = true
/**
* The touch bounds of this key. All bounds defined here are absolute coordinates within the parent keyboard.
*/
open val touchBounds: Rect = Rect()
/**
* The visible bounds of this key. All bounds defined here are absolute coordinates within the parent keyboard.
*/
open val visibleBounds: Rect = Rect()
/**
* The visible drawable bounds of this key. All bounds defined here are absolute coordinates within the parent
* keyboard.
*/
open val visibleDrawableBounds: Rect = Rect()
/**
* The visible label bounds of this key. All bounds defined here are absolute coordinates within the parent
* keyboard.
*/
open val visibleLabelBounds: Rect = Rect()
/**
* Specifies how much this key is willing to shrink if too many keys are in a keyboard row. A value of 0.0
* indicates that the key does not want to shrink in such scenario. This value should not be set manually, only
* by the key's compute method and is used in the layout process to determine the real key width.
*/
open var flayShrink: Double = 0.0
/**
* Specifies how much this key is willing to grow if too few keys are in a keyboard row. A value of 0.0
* indicates that the key does not want to grow in such scenario. This value should not be set manually, only
* by the key's compute method and is used in the layout process to determine the real key width.
*/
open var flayGrow: Double = 0.0
/**
* Specifies the relative proportional width this key aims to get in respective to the keyboard view's desired key
* width. A value of 1.0 indicates that the key wants to be exactly as wide as the desired key width, a value of
* 0.0 is basically equivalent to setting [isVisible] to false. This value should not be set manually, only
* by the key's compute method and is used in the layout process to determine the real key width.
*/
open var flayWidthFactor: Double = 0.0
/**
* The computed UI label of this key. This value is used by the keyboard view to temporarily save the label string
* for UI rendering and should not be set manually.
*/
open var label: String? = null
/**
* The computed UI hint label of this key. This value is used by the keyboard view to temporarily save the hint
* label string for UI rendering and should not be set manually.
*/
open var hintedLabel: String? = null
/**
* The computed UI drawable ID of this key. This value is used by the keyboard view to temporarily save the
* drawable ID for UI rendering and should not be set manually.
*/
open var foregroundDrawableId: Int? = null
}

View File

@@ -0,0 +1,130 @@
/*
* 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.ime.keyboard
import dev.patrickgold.florisboard.ime.text.key.KeyVariation
import dev.patrickgold.florisboard.ime.text.keyboard.TextComputingEvaluator
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* Basic interface for a key data object. Base for all key data objects across the IME, such as text, emojis and
* selectors. The implementation is as abstract as possible, as different features require different implementations.
*/
interface KeyData {
/**
* Computes a [TextKeyData] object for this key data. Returns null if no computation is possible or if the key is
* not relevant based on the result of [evaluator].
*
* @param evaluator The evaluator used to retrieve different states from the parent controller.
*
* @return A [TextKeyData] object or null if no computation is possible.
*/
fun computeTextKeyData(evaluator: TextComputingEvaluator): TextKeyData?
/**
* Returns the data described by this key as a string.
*
* @param isForDisplay Specifies if the returned string is intended to be displayed in a UI label (=true) or if
* it should be computed to be sent to an input connection (=false).
*
* @return The computed string for the key data object. Note: some objects may return an empty string here, meaning
* it is always required to check for the string's length before attempting to directly retrieve the first char.
*/
fun asString(isForDisplay: Boolean): String
}
/**
* Allows to select a [KeyData] based on the current caps 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 [KeyData] to be used here. The JSON class identifier for this selector is `case_selector`.
*
* Example usage in a layout JSON file:
* ```
* { "$": "case_selector",
* "lower": { "code": 59, "label": ";" },
* "upper": { "code": 58, "label": ":" }
* }
* ```
*
* @property lower The property to use if the current caps state is lowercase.
* @property upper The property to use if the current caps state is uppercase.
*/
@Serializable
@SerialName("case_selector")
class CaseSelector(
val lower: KeyData,
val upper: KeyData,
) : KeyData {
override fun computeTextKeyData(evaluator: TextComputingEvaluator): TextKeyData? {
return (if (evaluator.evaluateCaps()) { upper } else { lower }).computeTextKeyData(evaluator)
}
override fun asString(isForDisplay: Boolean): String {
return ""
}
}
/**
* Allows to select a [KeyData] based on the current key 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 for
* any [KeyData] to be used here. The JSON class identifier for this selector is `variation_selector`.
*
* Example usage in a layout JSON file:
* ```
* { "$": "variation_selector",
* "default": { "code": 44, "label": "," },
* "email": { "code": 64, "label": "@" },
* "uri": { "code": 47, "label": "/" }
* }
* ```
*
* @property default The default [KeyData] which should be used in case no key variation is known or for the current
* key variation no override key is defined.
* @property email The key data to use if [KeyVariation.EMAIL_ADDRESS] is the active key variation. If this value is
* null, [default] will be used instead.
* @property uri The key data to use if [KeyVariation.URI] is the active key variation. If this value is null,
* [default] will be used instead.
* @property normal The key data to use if [KeyVariation.NORMAL] is the active key variation. If this value is null,
* [default] will be used instead.
* @property password The key data to use if [KeyVariation.PASSWORD] is the active key variation. If this value is
* null, [default] will be used instead.
*/
@Serializable
@SerialName("variation_selector")
data class VariationSelector(
val default: KeyData,
val email: KeyData? = null,
val uri: KeyData? = null,
val normal: KeyData? = null,
val password: KeyData? = null,
) : KeyData {
override fun computeTextKeyData(evaluator: TextComputingEvaluator): TextKeyData? {
return when (evaluator.getKeyVariation()) {
KeyVariation.ALL -> default
KeyVariation.EMAIL_ADDRESS -> email ?: default
KeyVariation.NORMAL -> normal ?: default
KeyVariation.PASSWORD -> password ?: default
KeyVariation.URI -> uri ?: default
}.computeTextKeyData(evaluator)
}
override fun asString(isForDisplay: Boolean): String {
return ""
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.ime.keyboard
/**
* Abstract class describing a computed keyboard. The exact implementation is dependent on the subclass and the
* structure can vary quite much between different subclasses.
*/
abstract class Keyboard {
/**
* Returns the key for given [pointerX] and [pointerY] coords or null if no key touch hit box is defined at the
* given coords.
*
* @param pointerX The x-coordinate of the input event, absolute within the parent keyboard view.
* @param pointerY The y-coordinate of the input event, absolute within the parent keyboard view.
*
* @return The key for given coords or null if no key touch hit box is defined for this position.
*/
abstract fun getKeyForPos(pointerX: Int, pointerY: Int): Key?
/**
* Returns an iterator which allows to loop through all keys within the layout, independent of the actual
* structure and layout.
*/
abstract fun keys(): Iterator<Key>
/**
* Layouts the keys according the the dimensions and parameters provided by given [keyboardView]. This method's
* exact behavior is highly dependent aon the actual subclass.
*
* @param keyboardView The parent keyboard view which is used to gather the absolute dimensions and other
* subclass specific parameters.
*/
abstract fun layout(keyboardView: KeyboardView)
}

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