Compare commits

..

146 Commits

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

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Update fa3.json

insert character "ئ" as the default character

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

* Update full rule for a, ă

* Add rule for e

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

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

* Full rule for i

* Full rule o

* Full rule for u

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

* Add â + {m, n}

* Add â + {p, t}

* Full â (mark + unmark)

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

* Full ê

* Full ô

* Full ơ

* Full ư

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

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

* Add ươ + {c, i}

* Add ươm

* Add ươ: mark & unmark

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

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

* Add ach, ich, êch

* Fix Vietnamese default subtype using incorrect composer ID

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

Additionally key repeat also uses a system value, however both the default system value and the previous hard-coded value are `50ms`, no nothing should change here for the user.
2022-05-08 13:00:36 +02:00
Patrick Goldinger
83d4fc727d Fix backspace sometimes not working in initial state (#1831) 2022-05-07 18:47:45 +02:00
Patrick Goldinger
5bf4819d83 Fix space swipe not checking for raw input editor 2022-05-07 17:50:57 +02:00
Patrick Goldinger
60221743f6 Fix Smartbar suggestion UI not updating correctly 2022-05-07 17:39:55 +02:00
Patrick Goldinger
9d78661ecb Refactor Room to use KSP + export schema correctly 2022-05-07 11:52:49 +02:00
Patrick Goldinger
addedb6f9b Fix long press action being run outside main thread 2022-05-06 19:08:21 +02:00
Patrick Goldinger
e1701b2ba7 Improve devtools input state overlay 2022-05-05 22:51:13 +02:00
Patrick Goldinger
380a0e4f4c Release v0.3.16-beta01 2022-05-03 19:57:46 +02:00
florisboard-bot
7fb24c0471 Update translations from Crowdin 2022-05-03 19:38:57 +02:00
Patrick Goldinger
aaae483ad8 Update ROADMAP.md and home screen message 2022-05-03 19:37:23 +02:00
GoRaN
578652b9ba Update Kurdish layout and overall improvements (#1798)
* Update kurdish.json

* Update kurdish_standard.json

* Update ckb.json

* Update ckb.json

* Update kurdish_standard.json
2022-05-02 23:25:59 +02:00
Patrick Goldinger
b8133b17fe Merge pull request #1822 from florisboard/editor-instance-rework
Major rework of input logic, connection handling and state management
2022-05-02 23:18:37 +02:00
Patrick Goldinger
8226a4fcb9 Add "Always delete word" toggle for glide typing (#1541, #1036) 2022-05-02 22:51:20 +02:00
Patrick Goldinger
ea85a63da9 Fix input lag for mass selection events (fast cursor movement)
which results from the keyboard. Fast cursor movement from the editor currently is still a laggy experience.
2022-05-02 22:13:39 +02:00
Patrick Goldinger
21e784fb09 Fix crashutility namespace incorrect in manifest 2022-05-02 19:48:42 +02:00
Patrick Goldinger
0a1fb5a51e Fix editor instance issues with phantom space 2022-05-01 19:05:18 +02:00
Patrick Goldinger
2a3c3e07e2 Improve input connection raw key event sending 2022-05-01 15:30:37 +02:00
Patrick Goldinger
f8eb86806e Improve expected content handling to reduce expensive calls 2022-04-30 18:56:29 +02:00
Patrick Goldinger
25a7c47c3c Fix enter key behavior for multiline and shift (#1037) 2022-04-29 19:42:00 +02:00
Patrick Goldinger
f08d92fef4 Fix delete key word selection not working properly 2022-04-29 00:07:37 +02:00
Patrick Goldinger
362a84de51 Rework InputEventDispatcher and remove async channel 2022-04-28 23:57:05 +02:00
Patrick Goldinger
64fefef31a Add delete key long press option in gesture prefs (#637) 2022-04-28 01:02:51 +02:00
Patrick Goldinger
b0fced6f98 Fix delete key precise char selection not triggering precisely 2022-04-27 23:54:08 +02:00
Patrick Goldinger
406f79ee36 Fix state bugs in the commitText and deleteBeforeCursor logic 2022-04-27 23:17:37 +02:00
Patrick Goldinger
893fc758b1 Add expected content functionality baseline 2022-04-26 23:58:48 +02:00
Patrick Goldinger
126de62f80 Improve and fix new caching base logic in editor instance 2022-04-25 23:31:08 +02:00
Patrick Goldinger
0ee634a83d Merge input logic and editor instance
but keep logic separate in an abstract and a normal class
2022-04-23 20:47:27 +02:00
Patrick Goldinger
d4f634e8af Split editor instance into input logic and instance
Base for further work on the split and input cache expected caching
2022-04-22 21:20:58 +02:00
Patrick Goldinger
f544e1203d Fix delete call being async / Update selection docs 2022-04-21 01:24:32 +02:00
Patrick Goldinger
d0a373f5f4 Rework and simplify editor content+instance logic 2022-04-20 23:53:45 +02:00
Patrick Goldinger
e4dfed3940 Improve logic in text updater and caching 2022-04-20 02:03:34 +02:00
Patrick Goldinger
7f674f89d2 Raise minimum API level from 23 to 24 2022-04-20 00:05:43 +02:00
Patrick Goldinger
0d593a07e6 Rework base logic of EditorInstance word logic
Not optimized for performance yet, some features are broken in this commit. See the following commits for improvements in the changes of this commit.
2022-04-19 23:56:49 +02:00
Patrick Goldinger
261c8fbec1 Add FlorisEditorInfo wrapper class 2022-04-18 23:58:59 +02:00
Patrick Goldinger
d5262e3ad2 Move EditorInfo helpers into own file 2022-04-17 20:43:51 +02:00
Patrick Goldinger
527543331a Update android.yml to remove gradle caching 2022-04-16 13:02:29 +02:00
Patrick Goldinger
75623f2a95 Fix space key being used as hint from symbols (#1778) 2022-04-16 12:24:16 +02:00
Patrick Goldinger
29f6658256 Merge pull request #1793 from florisboard/gestures-and-glide-fixes
Fix issues with gesture detection and glide/input logic
2022-04-16 12:05:59 +02:00
Patrick Goldinger
dbc6606237 Add Smartbar visibility toggle swipe action (#1794) 2022-04-15 20:00:37 +02:00
Patrick Goldinger
1108456a8e Fix glide typing not setting phantom space flag (#1777) 2022-04-15 19:29:04 +02:00
Patrick Goldinger
2debf36cb6 Improve double-space period behavior (#1792)
Still isn't fixed though, requires new cached input logic
2022-04-15 19:10:46 +02:00
Patrick Goldinger
896101b840 Fix gesture detector comparing px and dp values 2022-04-15 18:23:20 +02:00
Patrick Goldinger
a84854a5b1 Fix space bar eating input when gestures disabled (#1715) 2022-04-15 17:44:44 +02:00
Patrick Goldinger
461966de96 Add devtools "Show key touch boundaries" toggle 2022-04-14 22:22:06 +02:00
Patrick Goldinger
6dd3713641 Fix confirm delete dialog bug when history empty (#1783) 2022-04-13 23:00:06 +02:00
Patrick Goldinger
d51b301364 Change repo icon to be rounded-corner (#1785) 2022-04-13 22:47:01 +02:00
Patrick Goldinger
f8db5d3881 Merge pull request #1780 from florisboard/kotlin-package-structure-changes
Source file package structure changes
2022-04-13 17:56:12 +02:00
Patrick Goldinger
781a00e7c2 Fix linking to source files & Update ROADMAP.md 2022-04-12 20:30:27 +02:00
Patrick Goldinger
a1bb73ba9d Optimize imports on all main source files 2022-04-12 00:21:13 +02:00
Patrick Goldinger
d6390108c2 Move res package to lib/cache and lib/io 2022-04-12 00:13:41 +02:00
Patrick Goldinger
fee954c95f Move snygg package to lib/snygg 2022-04-12 00:02:07 +02:00
Patrick Goldinger
0347eabc04 Rework structure inside app and move custom compose to lib 2022-04-11 23:57:45 +02:00
Patrick Goldinger
c6bcd8a89c Rename common package to lib and move several packages in 2022-04-11 23:35:07 +02:00
Patrick Goldinger
114bcb0d4b Rename java source dir to kotlin 2022-04-11 22:56:20 +02:00
Patrick Goldinger
f4a1a04997 Merge pull request #1775 from florisboard/change-app-icon-v4
Add new flower-shaped app icon
2022-04-11 17:52:32 +02:00
Patrick Goldinger
70a859e00b Adjust Fastlane to use new icon 2022-04-10 21:28:54 +02:00
Patrick Goldinger
8d3b2ef474 Add new flower-shaped app icon (#1735)
Co-authored-by: BloodRaven0 <bloodraven0@users.noreply.github.com>
2022-04-10 21:09:04 +02:00
Patrick Goldinger
76efc0a0e0 Upgrade AGP to 7.1.3 / Upgrade other dependencies 2022-04-10 14:53:48 +02:00
Patrick Goldinger
166734757f Release v0.3.15 2022-04-10 13:57:55 +02:00
Patrick Goldinger
97f401371f Fix crash in key reset logic (#1771) 2022-04-10 12:26:21 +02:00
Patrick Goldinger
b012e5377b Release v0.3.15-rc02 2022-04-09 19:19:54 +02:00
florisboard-bot
3b18dc33d8 Update translations from Crowdin 2022-04-09 19:07:29 +02:00
Patrick Goldinger
d798f01576 Merge pull request #1769 from florisboard/fix-key-visuals-and-popups-getting-stuck
Fix key visuals and popups getting stuck
2022-04-09 17:35:55 +02:00
Patrick Goldinger
55281bd7d6 Merge pull request #1768 from florisboard/add-guardedbylock-and-fix-glide-issues
Add guardedbylock and fix glide issues
2022-04-09 17:34:59 +02:00
Patrick Goldinger
88e9bde0a9 Fix popup stays active when using multitouch (#1450) 2022-04-09 17:26:11 +02:00
Patrick Goldinger
54d5a16761 Fix keys can get stuck with active visuals (#1446) 2022-04-09 16:43:53 +02:00
Patrick Goldinger
701da50479 Add GuardedByLock to prevent crash (#1632) 2022-04-09 16:02:06 +02:00
Patrick Goldinger
b43b6aa2d0 Remove unnecessary classes and methods in common 2022-04-09 13:07:37 +02:00
Patrick Goldinger
4279e9d100 Fix Unicode non-spacing marks for Thai (#1759) 2022-04-08 20:06:36 +02:00
M-Koushan
e5bd979880 Add German (Gboard) layout (#1737) & Fix Persian layout issue (#1746) 2022-04-08 18:48:00 +02:00
Patrick Goldinger
9a48169bf3 Fix delete key swipe right deleting left words (#1765) 2022-04-08 17:42:47 +02:00
Patrick Goldinger
99cfd99815 Merge pull request #1763 from florisboard/fix-extracted-input-ui-issues
Fix extracted input UI issues
2022-04-08 17:23:19 +02:00
Hayleia
e8f082d885 Fix Korean composition of medial+final and final+final (#1757) 2022-04-07 20:33:45 +02:00
Patrick Goldinger
b63f475a8c Fix NPE crash in extracted input UI (#1758) 2022-04-07 20:12:20 +02:00
Patrick Goldinger
e183f10969 Improve devtools overlay UI and remove unnecessary code 2022-04-06 23:57:57 +02:00
Patrick Goldinger
c1e624b9a0 Fix input view + extracted input UI layouting issues (#1760) 2022-04-06 22:51:51 +02:00
Patrick Goldinger
2d87d0e4d3 Release v0.3.15-rc01 2022-04-05 23:59:07 +02:00
florisboard-bot
bcf1bffc24 Update translations from Crowdin 2022-04-05 23:45:52 +02:00
Patrick Goldinger
f388e8811e Add support for videos on clipboard history (#1721) 2022-04-05 23:42:12 +02:00
Patrick Goldinger
7ededa6293 Merge pull request #1756 from florisboard/quick-glide-fixes
Fix glide typing state and drawing bugs
2022-04-05 22:21:49 +02:00
Patrick Goldinger
7d9e1cf2b5 Fix glide trail not drawing if popups disabled (#1704) 2022-04-05 22:12:52 +02:00
Patrick Goldinger
299f581609 Fix glide not initializing if disabled on app start (#1703) 2022-04-05 21:33:46 +02:00
Patrick Goldinger
c38d4ed90b Merge pull request #1749 from florisboard/re-add-other-default-themes
Re-implement other default themes
2022-04-05 19:43:52 +02:00
Patrick Goldinger
647bc659d7 Decrease visual key height for borderless themes 2022-04-05 19:25:20 +02:00
Patrick Goldinger
61eb09e611 Re-add Pure Night and all borderless themes (#1702) 2022-04-05 19:08:28 +02:00
Patrick Goldinger
1a4118d29a Fix space bar ignoring "No action" setting (#1706) 2022-04-04 23:04:43 +02:00
Denys Honsiorovskyi
48655b3771 Ukrainian popup mappings improvements (#1739)
* Ukrainian popup mappings improvements

* Make all letters relevant not to override default symbols

* Update extension.json

* Remove extra domains: they look ugly
2022-04-04 22:41:53 +02:00
pjtsearch
17649c44bf Fix փ missing in Armenian layouts (#1741)
Co-authored-by: PJTSearch <pjtsignups@gmail.com>
2022-04-04 22:34:31 +02:00
Patrick Goldinger
7cbb19ddcb Add support for Chinese (Simplified) (zh-CN) 2022-04-04 20:04:25 +02:00
Patrick Goldinger
e13ac7c689 Add support for locale display language in devtools 2022-04-04 20:03:58 +02:00
Patrick Goldinger
941733cdc0 Merge pull request #1734 from florisboard/add-extracted-landscape-input-layout
Add support for ExtractedLandscapeInput
2022-04-03 15:16:44 +02:00
Patrick Goldinger
a25289a856 Fix ExtractedLandscapeInput UI themeing 2022-04-02 20:46:59 +02:00
Patrick Goldinger
7bcfeca872 Improve ExtractedLandscapeInput UI and fix compatibility
... with Samsung devices.
2022-04-02 20:01:45 +02:00
Patrick Goldinger
240ebc499a Improve debug summarization of EditorInfo 2022-04-02 17:09:10 +02:00
Patrick Goldinger
831882f419 Add base for ExtractedLandscapeInput 2022-04-01 20:24:08 +02:00
Patrick Goldinger
e0e5259b4c Remove deprecated shift and caps flag from KeyboardState 2022-04-01 18:37:04 +02:00
Patrick Goldinger
33cd2b5d01 Release v0.3.15-beta02 2022-03-31 20:35:03 +02:00
florisboard-bot
d3dda86966 Update translations from Crowdin 2022-03-31 20:21:27 +02:00
Patrick Goldinger
59aa5cdb33 Fix Restore screen too restrictive about media type (#1707) 2022-03-31 20:08:31 +02:00
Patrick Goldinger
d26e820492 Possible fix for setup screen system observer failing (#1731) 2022-03-31 19:49:33 +02:00
Patrick Goldinger
b8b1b04c7e Decrease vertical padding usage in setup screen (#1364) 2022-03-31 19:48:56 +02:00
Patrick Goldinger
26b4acc894 Add limit to clipboard history text item preview (#1730) 2022-03-31 19:19:40 +02:00
Patrick Goldinger
12c4220544 Merge pull request #1720 from florisboard/improve-theme-logic-and-ui
Improve theme logic, appearance and stylesheet editor UI
2022-03-31 18:55:43 +02:00
Patrick Goldinger
5fc4f5ba60 Improve wording of code param in stylesheet editor 2022-03-31 01:39:07 +02:00
Patrick Goldinger
03ea9bcb76 Add key code recording tool in stylesheet editor 2022-03-31 01:03:59 +02:00
Patrick Goldinger
a04c44df98 Improve common Android Toast extension funs 2022-03-30 22:06:55 +02:00
Patrick Goldinger
b1431c7e51 Add key code preview box in stylesheet rule dialog 2022-03-29 01:32:46 +02:00
Patrick Goldinger
c09719ffd6 Fix emoji key (and popup) font color ignoring current theme 2022-03-28 19:49:39 +02:00
Patrick Goldinger
16149d95a1 Fix key shape clipping contents (#1710) 2022-03-28 19:39:37 +02:00
Patrick Goldinger
658e43da9c Move fontSizeMultiplier() to AppPrefs directly 2022-03-28 19:29:56 +02:00
M-Koushan
66ddb451ab Add additional Persian keyboard layout (#1683)
* Adding new layout Persian2

* Fix whitespace issues and charactersMod missing

* Fix issues in fa2.json

Co-authored-by: M-Koushan <koushan405@gmail.com>
Co-authored-by: Patrick Goldinger <patrick@patrickgold.dev>
2022-03-27 13:09:31 +02:00
Patrick Goldinger
f135513f3e Add support for A12 VibratiorManager 2022-03-26 20:00:52 +01:00
Patrick Goldinger
322dfa717b Fix language names not titlecase for some locales (#1686) 2022-03-26 11:26:56 +01:00
416 changed files with 10163 additions and 5639 deletions

BIN
.github/repo_icon.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@@ -29,14 +29,6 @@ jobs:
java-version: 11
- name: Setup CMake and Ninja
uses: lukka/get-cmake@v3.20.1
- uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Build with Gradle
# MUST call gradlew separately because of an OSS license plugin issue.
# See https://github.com/google/play-services-plugins/issues/199

3
.gitignore vendored
View File

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

View File

@@ -64,7 +64,7 @@ For the `code` field of each key, make sure to use the UTF-8 code. An useful too
is [unicode-table.com](https://unicode-table.com/en/). From there, you search for your letter and then use the HTML
code, but without the `&#;`
For internal codes of functional or UI keys, see
[`app/src/main/java/dev/patrickgold/florisboard/ime/text/key/KeyCode.kt`](app/src/main/java/dev/patrickgold/florisboard/ime/text/key/KeyCode.kt)
[`app/src/main/kotlin/dev/patrickgold/florisboard/ime/text/key/KeyCode.kt`](app/src/main/kotlin/dev/patrickgold/florisboard/ime/text/key/KeyCode.kt)
.
The label is equally important and should always match up with the defined code. If `code` and `label` don't match up,

View File

@@ -1,9 +1,9 @@
<img align="left" width="80" height="80"
src="fastlane/metadata/android/en-US/images/icon.png" alt="App icon">
src=".github/repo_icon.png" alt="App icon">
# 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+
**FlorisBoard** is a free and open-source keyboard for Android 7.0+
devices. It aims at being modern, user-friendly and customizable while
fully respecting your privacy. Currently in early-beta state.
@@ -97,6 +97,8 @@ to get more information on this topic.
* [Nuspell](https://github.com/nuspell/nuspell) by
[Nuspell](https://github.com/nuspell)
Many thanks to [Nikolay Anzarov](https://www.behance.net/nikolayanzarov) ([@BloodRaven0](https://github.com/BloodRaven0)) for designing and providing the main app icons to this project!
## License
```
Copyright 2020-2022 Patrick Goldinger

View File

@@ -1,3 +1,4 @@
# FlorisBoard's feature roadmap & milestones
This feature roadmap intents to provide transparency to what I want to add to FlorisBoard in the foreseeable future.
@@ -14,42 +15,13 @@ Releases in this section still follow the old versioning scheme, meaning the pat
naming convention is more confusing than useful, beginning with v0.4.0 development a new release/development cycle will
be introduced.
### 0.3.14 (almost completed, release candidate phase)
### 0.3.15 & 0.3.16 (currently 0.3.15 done, 0.3.16 in work)
- Re-write of the Preference core
- Reduce redundancy in key/default value definitions
- Avoid having to manually add redundant code for adding a new pref
- Goes hand-in-hand with the Settings UI re-write
- Re-write of the Settings UI with Jetpack Compose
- Also re-structure UI into a more list-like panel
- Adjust theme colors of Settings a bit to make it more modern
- Preview the keyboard at any time from within the Settings
- Settings language different than device language
- Re-write the Setup UI in Jetpack Compose
- Simplify screen based on previously discussed ideas and mock-ups
- Improve backend setup logic
- Implement base-UI for extensions and further continue development of existing Flex (FlorisBoard extension) format
- Allows for a continuous experience of customizing FlorisBoard in different areas
- Planned what will use Flex:
- Themes
- Layouts (Characters, symbols, numeric, ...)
- Composers for non-Latin script languages
- Word suggestion dictionaries (in 0.4.0)
- Spell check dictionaries
- User dictionaries (not in 0.3.14)
- Other features that require only data and no logic (not in 0.3.14)
- Maybe full backup of preferences? Not 100% confirmed though and may be pushed back
- Theme rework part I:
- Custom key corner radius
- Custom key border color (not shadow!!)
- Re-work theme internals so they use Flex extension format and FlexCSS
- Improvement of the Smartbar
- Allow to have multiple Smartbars
- Better candidate view (in prep for 0.4.0)
### 0.3.15 & 0.3.16
- Hotfix releases for possible bugs in the preference rework, may be skipped.
- Hotfix releases for possible bugs in the preference rework (in work)
- Lots and lots of bug fixing in general (in work)
- Preparation work for 0.4.0, fixing text state logic and use break iterator (done)
- Reducing or getting rid of input lag some devices experience (done)
- Clean up of project structure for better future development (done)
## 0.4.0
@@ -61,9 +33,9 @@ be introduced.
With this release the versioning scheme changes: the second number now indicates new features, changes in the third "
patch" number now indicates bug fixes and minor feature additions for the stable track. The development cycle for each
0.x release will have `-alphaXX`, `-betaXX` and `-rcXX` (release candidate) releases on the beta track for interested
people to follow along the development. The first release to follow the new scheme will be `0.4.0-alpha01` on the beta
track.
0.x release will have `-alphaXX` (optional and only for large releases), `-betaXX` and `-rcXX` (release candidate)
releases on the beta track for interested people to follow along the development. The first release to follow the new
scheme will be `0.4.0-alpha01` on the beta track.
## 0.5.0

View File

@@ -1,13 +1,14 @@
plugins {
id("com.android.application")
kotlin("android")
kotlin("kapt")
kotlin("plugin.serialization")
id("com.google.devtools.ksp")
id("com.google.android.gms.oss-licenses-plugin")
id("de.mannodermaus.android-junit5")
}
android {
namespace = "dev.patrickgold.florisboard"
compileSdk = 31
buildToolsVersion = "31.0.0"
ndkVersion = "22.1.7171670"
@@ -29,21 +30,17 @@ android {
defaultConfig {
applicationId = "dev.patrickgold.florisboard"
minSdk = 23
minSdk = 24
targetSdk = 31
versionCode = 76
versionName = "0.3.15"
versionCode = 83
versionName = "0.3.16"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
arguments += mapOf(
Pair("room.schemaLocation", "$projectDir/schemas"),
Pair("room.incremental", "true"),
Pair("room.expandProjection", "true")
)
}
ksp {
arg("room.schemaLocation", "$projectDir/schemas")
arg("room.incremental", "true")
arg("room.expandProjection", "true")
}
externalNativeBuild {
@@ -66,6 +63,9 @@ android {
jniLibs {
srcDirs("src/main/icu4c/prebuilt/jniLibs")
}
java {
srcDirs("src/main/kotlin")
}
}
}
}
@@ -115,7 +115,7 @@ android {
create("beta") // Needed because by default the "beta" BuildType does not exist
named("beta").configure {
applicationIdSuffix = ".beta"
versionNameSuffix = "-beta01"
versionNameSuffix = "-beta03"
proguardFiles.add(getDefaultProguardFile("proguard-android-optimize.txt"))
resValue("mipmap", "floris_app_icon", "@mipmap/ic_app_icon_beta")
@@ -127,9 +127,9 @@ android {
named("release").configure {
proguardFiles.add(getDefaultProguardFile("proguard-android-optimize.txt"))
resValue("mipmap", "floris_app_icon", "@mipmap/ic_app_icon_release")
resValue("mipmap", "floris_app_icon_round", "@mipmap/ic_app_icon_release_round")
resValue("drawable", "floris_app_icon_foreground", "@drawable/ic_app_icon_release_foreground")
resValue("mipmap", "floris_app_icon", "@mipmap/ic_app_icon_stable")
resValue("mipmap", "floris_app_icon_round", "@mipmap/ic_app_icon_stable_round")
resValue("drawable", "floris_app_icon_foreground", "@drawable/ic_app_icon_stable_foreground")
resValue("string", "floris_app_name", "@string/app_name")
}
}
@@ -158,26 +158,27 @@ dependencies {
implementation("androidx.compose.ui:ui:1.1.1")
implementation("androidx.compose.ui:ui-tooling-preview:1.1.1")
implementation("androidx.core:core-ktx:1.7.0")
implementation("androidx.core:core-splashscreen:1.0.0-beta01")
implementation("androidx.core:core-splashscreen:1.0.0-beta02")
implementation("androidx.emoji2:emoji2:1.1.0")
implementation("androidx.emoji2:emoji2-views:1.1.0")
implementation("androidx.navigation:navigation-compose:2.4.1")
implementation("com.google.accompanist:accompanist-flowlayout:0.23.0")
implementation("com.google.accompanist:accompanist-insets:0.23.0")
implementation("com.google.accompanist:accompanist-systemuicontroller:0.23.0")
implementation("androidx.navigation:navigation-compose:2.4.2")
implementation("com.google.accompanist:accompanist-flowlayout:0.23.1")
implementation("com.google.accompanist:accompanist-insets:0.23.1")
implementation("com.google.accompanist:accompanist-systemuicontroller:0.23.1")
implementation("dev.patrickgold.jetpref:jetpref-datastore-model:0.1.0-beta08")
implementation("dev.patrickgold.jetpref:jetpref-datastore-ui:0.1.0-beta08")
implementation("dev.patrickgold.jetpref:jetpref-material-ui:0.1.0-beta08")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")
implementation("io.github.reactivecircus.cache4k:cache4k:0.5.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
implementation("androidx.room:room-runtime:2.4.2")
kapt("androidx.room:room-compiler:2.4.2")
ksp("androidx.room:room-compiler:2.4.2")
testImplementation("io.kotest:kotest-runner-junit5:5.1.0")
testImplementation("io.kotest:kotest-assertions-core:5.1.0")
testImplementation("io.kotest:kotest-property:5.1.0")
testImplementation("io.kotest:kotest-runner-junit5:5.2.3")
testImplementation("io.kotest:kotest-assertions-core:5.2.3")
testImplementation("io.kotest:kotest-property:5.2.3")
testImplementation("io.kotest.extensions:kotest-extensions-robolectric:0.5.0")
testImplementation("nl.jqno.equalsverifier:equalsverifier:3.8.3")
testImplementation("nl.jqno.equalsverifier:equalsverifier:3.10")
androidTestImplementation("androidx.test.ext", "junit", "1.1.2")
androidTestImplementation("androidx.test.espresso", "espresso-core", "3.3.0")

View File

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

View File

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

View File

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

View File

@@ -15,8 +15,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="dev.patrickgold.florisboard">
xmlns:tools="http://schemas.android.com/tools">
<!-- Permission needed to vibrate if the user has key press vibration enabled -->
<uses-permission android:name="android.permission.VIBRATE"/>
@@ -52,8 +51,7 @@
android:label="@string/floris_app_name"
android:permission="android.permission.BIND_INPUT_METHOD"
android:directBootAware="true"
android:exported="true"
tools:targetApi="n">
android:exported="true">
<intent-filter>
<action android:name="android.view.InputMethod"/>
</intent-filter>
@@ -105,7 +103,7 @@
<!-- Import File Bridging Activity -->
<activity
android:name="dev.patrickgold.florisboard.app.ui.ext.ImportFileActivity"
android:name="dev.patrickgold.florisboard.app.ext.ImportFileActivity"
android:icon="@mipmap/floris_app_icon"
android:label="@string/settings__title"
android:launchMode="singleTask"
@@ -123,14 +121,14 @@
<!-- Crash Dialog Activity -->
<activity
android:name="dev.patrickgold.florisboard.crashutility.CrashDialogActivity"
android:name="dev.patrickgold.florisboard.lib.crashutility.CrashDialogActivity"
android:icon="@mipmap/floris_app_icon"
android:label="@string/crash_dialog__title"
android:theme="@style/CrashDialogTheme"/>
<!-- Clipboard Image File Provider -->
<!-- Clipboard Media File Provider -->
<provider
android:name="dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardImagesProvider"
android:name="dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardMediaProvider"
android:authorities="${applicationId}.provider.clipboard"
android:grantUriPermissions="true"
android:exported="false">
@@ -147,6 +145,17 @@
android:resource="@xml/file_paths"/>
</provider>
<!-- Disable default EmojiCompat initializer -->
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.emoji2.text.EmojiCompatInitializer"
tools:node="remove"/>
</provider>
</application>
</manifest>

View File

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

View File

@@ -135,6 +135,12 @@
"authors": [ "mahmoudk1000" ],
"direction": "ltr"
},
{
"id": "german2",
"label": "German (GBoard)",
"authors": [ "M-Koushan" ],
"direction": "rtl"
},
{
"id": "greek",
"label": "Ελληνικά",
@@ -249,6 +255,20 @@
"direction": "rtl",
"modifier": "org.florisboard.layouts:persian"
},
{
"id": "persian2",
"label": "Persian2",
"authors": [ "M-Koushan" ],
"direction": "rtl",
"modifier": "org.florisboard.layouts:persian2"
},
{
"id": "persian3",
"label": "Persian3",
"authors": [ "SaeID-Rz" ],
"direction": "rtl",
"modifier": "org.florisboard.layouts:persian3"
},
{
"id": "qwerty",
"label": "QWERTY",
@@ -419,6 +439,18 @@
"label": "Persian",
"authors": [ "PHELAT" ],
"direction": "rtl"
},
{
"id": "persian2",
"label": "Persian2",
"authors": [ "M-Koushan" ],
"direction": "rtl"
},
{
"id": "persian3",
"label": "Persian3",
"authors": [ "SaeID-Rz" ],
"direction": "rtl"
}
],
"extension": [

View File

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

View File

@@ -7,7 +7,7 @@
{ "$": "auto_text_key", "code": 1386, "label": "ժ" },
{ "$": "auto_text_key", "code": 1401, "label": "չ" },
{ "$": "auto_text_key", "code": 1403, "label": "ջ" },
{ "$": "auto_text_key", "code": 1380, "label": "դ" },
{ "$": "auto_text_key", "code": 1411, "label": "փ" },
{ "$": "auto_text_key", "code": 1394, "label": "ղ" },
{ "$": "auto_text_key", "code": 1390, "label": "ծ" }
],

View File

@@ -0,0 +1,37 @@
[
[
{ "$": "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": "ü" }
],
[
{ "$": "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": "ä" }
],
[
{ "$": "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

@@ -48,7 +48,9 @@
],
[
{ "code": 1600, "label": "kashida", "popup":
{ "main": { "code": 8204, "label": "half_space" }
} },
{ "code": 1574, "label": "ﺋ", "popup": {
"main": { "code": 1569, "label": "ء" }
} },
@@ -58,9 +60,14 @@
} },
{ "code": 1688, "label": "ژ" },
{ "code": 1605, "label": "م" },
{ "code": 1567, "label": "؟" },
{ "code": 1548, "label": "،" },
{ "code": 58, "label": ":" }
{ "code": 1567, "label": "؟", "popup": {
"main": { "code": 63, "label": "?" }
} },
{ "code": 1548, "label": "،", "popup": {
"main": { "code": 1643, "label": "٫" }
} },
{ "code": 58, "label": ":", "popup": {
"main": { "code": 1563, "label": "؛" }
} }
]
]

View File

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

View File

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

View File

@@ -8,7 +8,7 @@
{ "$": "auto_text_key", "code": 1386, "label": "ժ" },
{ "$": "auto_text_key", "code": 1401, "label": "չ" },
{ "$": "auto_text_key", "code": 1403, "label": "ջ" },
{ "$": "auto_text_key", "code": 1380, "label": "դ" },
{ "$": "auto_text_key", "code": 1411, "label": "փ" },
{ "$": "auto_text_key", "code": 1394, "label": "ղ" },
{ "$": "auto_text_key", "code": 1390, "label": "ծ" }
],

View File

@@ -1,8 +1,5 @@
[
[
{ "code": 1600, "label": "kashida", "popup":
{ "main": { "code": 8204, "label": "half_space" }
} },
{ "code": 0, "type": "placeholder" },
{ "code": -7, "label": "delete", "type": "enter_editing" }
],

View File

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

View File

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

View File

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

View File

@@ -11,16 +11,18 @@
]
},
"ی": {
"main": { "code": 1610, "label": "ي" },
"relevant": [
{ "code": 1746, "label": "ے" },
{ "code": 1741, "label": "ۍ" },
{ "code": 1744, "label": "ې" },
{ "code": 1610, "label": "ي" },
{ "code": 1597, "label": "ؽ" }
{ "code": 1744, "label": "ې" }
]
},
"ێ": {
"main": { "code": 1597, "label": "ؽ" }
},
"ﺋ": {
"relevant": [
{ "code": 65163, "label": "ﺋ" },
@@ -118,19 +120,23 @@
"~right": {
"main": { "code": 1567, "label": "؟" },
"relevant": [
{ "code": 64, "label": "@" },
{ "code": 35, "label": "#" },
{ "code": 40, "label": "(" },
{ "code": 41, "label": ")" },
{ "code": 171, "label": "«" },
{ "code": 187, "label": "»" },
{ "code": 60, "label": "<" },
{ "code": 62, "label": ">" },
{ "code": 1642, "label": "٪" },
{ "code": 35, "label": "#" },
{ "code": 42, "label": "*" },
{ "code": 1563, "label": "؛" },
{ "code": 59, "label": ";" },
{ "code": 58, "label": ":" },
{ "code": 44, "label": "," },
{ "code": 1549, "label": "؍" },
{ "code": 45, "label": "-" },
{ "code": 95, "label": "_" },
{ "code": 1600, "label": "" },
{ "code": 33, "label": "!" },
{ "code": 1548, "label": "،" }
{ "code": 1563, "label": "؛" },
{ "code": 58, "label": ":" },
{ "code": 1549, "label": "؍" },
{ "code": 1643, "label": "٫" },
{ "code": 1548, "label": "،" },
{ "code": 33, "label": "!" }
]
}
},

View File

@@ -0,0 +1,99 @@
{
"all": {
"ص": {
"relevant": [
{ "code": 1590, "label": "ض" }
]
},
"half_space": {
"relevant": [
{ "code": 1622, "label": "ٖ" },
{ "code": 1648, "label": "ٰ" },
{ "code": 1619, "label": "ٓ" },
{ "code": 1615, "label": "ُ" },
{ "code": 1616, "label": "ِ" },
{ "code": 1614, "label": "َ" },
{ "code": 1600, "label": "ـ" },
{ "code": 1621, "label": "ٕ" },
{ "code": 1618, "label": "ْ" },
{ "code": 1617, "label": "ّ" },
{ "code": 1612, "label": "ٌ" },
{ "code": 1613, "label": "ٍ" },
{ "code": 1620, "label": "ٔ" }
]
},
"ی": {
"relevant": [
{ "code": 1574, "label": "ئ" },
{ "code": 1610, "label": "ي" },
{ "code": 1746, "label": "ے" }
]
},
"ا": {
"main": { "code": 1570, "label": "آ"},
"relevant": [
{ "code": 1649, "label": "ٱ" },
{ "code": 1569, "label": "ء" },
{ "code": 1571, "label": "أ" },
{ "code": 1573, "label": "إ" }
]
},
"ه": {
"relevant": [
{ "code": 1729, "label": "ہ" },
{ "code": 1728, "label": "ۀ" },
{ "code": 1726, "label": "ھ" }
]
},
"ت": {
"relevant": [
{ "code": 1579, "label": "ث" }
]
},
"ک": {
"relevant": [
{ "code": 1706, "label": "ڪ"}
]
},
"ط": {
"relevant": [
{ "code": 1592, "label": "ظ" }
]
},
"ز": {
"relevant": [
{ "code": 1688, "label": "ژ" }
]
},
"و": {
"relevant": [
{ "code": 1572, "label": "ؤ" }
]
},
"گ": {
"relevant": [
{ "code": 1662, "label": "پ" }
]
},
"~right": {
"main": { "code": 1567, "label": "؟"},
"relevant": [
{ "code": 1563, "label": "؛" },
{ "code": 58, "label": ":"},
{ "code": 33, "label": "!" }
]
}
},
"uri": {
"~right": {
"main": { "code": -255, "label": ".ir"},
"relevant": [
{ "code": -255, "label": ".gov" },
{ "code": -255, "label": ".org" },
{ "code": -255, "label": ".edu" },
{ "code": -255, "label": ".com" },
{ "code": -255, "label": ".net" }
]
}
}
}

View File

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

View File

@@ -0,0 +1,74 @@
{
"all": {
"е": {
"relevant": [
{ "$": "auto_text_key", "code": 1105, "label": "ё" }
]
},
"у": {
"relevant": [
{ "$": "auto_text_key", "code": 1118, "label": "ў" }
]
},
"г": {
"relevant": [
{ "$": "auto_text_key", "code": 1169, "label": "ґ" }
]
},
"і": {
"relevant": [
{ "$": "auto_text_key", "code": 1111, "label": "ї" },
{ "$": "auto_text_key", "code": 1099, "label": "ы" }
]
},
"є": {
"relevant": [
{ "$": "auto_text_key", "code": 8212, "label": "—" },
{ "$": "auto_text_key", "code": 1101, "label": "э" }
]
},
"ь": {
"relevant": [
{ "$": "auto_text_key", "code": 700, "label": "ʼ" },
{ "$": "auto_text_key", "code": 39, "label": "'" },
{ "$": "auto_text_key", "code": 1098, "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": "/" },
{ "$": "layout_direction_selector",
"ltr": { "code": 40, "label": "(" },
"rtl": { "code": 41, "label": "(" }
},
{ "$": "layout_direction_selector",
"ltr": { "code": 41, "label": ")" },
"rtl": { "code": 40, "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": ".org" },
{ "code": -255, "label": ".net" }
]
}
}
}

View File

@@ -1,10 +1,26 @@
{
"all": {
"г": {
"relevant": [
{ "$": "auto_text_key", "code": 1169, "label": "ґ" }
]
},
"і": {
"relevant": [
{ "$": "auto_text_key", "code": 1111, "label": "ї" }
]
},
"є": {
"relevant": [
{ "$": "auto_text_key", "code": 8212, "label": "—" }
]
},
"ь": {
"relevant": [
{ "$": "auto_text_key", "code": 700, "label": "ʼ" },
{ "$": "auto_text_key", "code": 39, "label": "'" }
]
},
"~right": {
"main": { "code": 44, "label": "," },
"relevant": [
@@ -37,7 +53,6 @@
"main": { "code": -255, "label": ".com" },
"relevant": [
{ "code": -255, "label": ".ua" },
{ "code": -255, "label": ".edu" },
{ "code": -255, "label": ".org" },
{ "code": -255, "label": ".net" }
]

View File

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

View File

@@ -1,74 +0,0 @@
{
"$type": "dev.patrickgold.florisboard.ime.theme.Theme",
"name": "floris_black",
"label": "Floris Black",
"authors": [ "serebit" ],
"isNightTheme": true,
"attributes": {
"window": {
"colorPrimary": "#388E3C",
"colorPrimaryDark": "#306D32",
"colorAccent": "#FF9800",
"navigationBarColor": "@keyboard/background",
"navigationBarLight": "false",
"semiTransparentColor": "#20FFFFFF",
"textColor": "#EEEEEE"
},
"keyboard": {
"background": "#000000"
},
"key": {
"background": "#212121",
"backgroundPressed": "#3D3D3D",
"foreground": "@window/textColor",
"foregroundPressed": "@window/textColor",
"showBorder": "true"
},
"key:enter": {
"background": "@window/colorPrimary",
"backgroundPressed": "@window/colorPrimaryDark",
"foreground": "@window/textColor",
"foregroundPressed": "@window/textColor"
},
"key:shift:capslock": {
"foreground": "@window/colorAccent",
"foregroundPressed": "@window/colorAccent"
},
"media": {
"foreground": "@window/textColor",
"foregroundAlt": "#BDBDBD"
},
"oneHanded": {
"background": "#000000",
"foreground": "@window/textColor"
},
"popup": {
"background": "#424242",
"backgroundActive": "#707070",
"foreground": "@window/textColor"
},
"privateMode": {
"background": "#7800BF",
"foreground": "@window/textColor"
},
"smartbar": {
"background": "transparent",
"foreground": "@window/textColor",
"foregroundAlt": "#73FFFFFF"
},
"smartbarButton": {
"background": "@key/background",
"foreground": "@key/foreground"
},
"extractEditLayout": {
"background": "#282828",
"foreground": "@window/textColor",
"foregroundAlt": "#73FFFFFF"
},
"extractActionButton": {
"background": "@smartbarButton/background",
"foreground": "@smartbarButton/foreground"
},
"glideTrail": {"foreground": "#20388E3C"}
}
}

View File

@@ -1,77 +0,0 @@
{
"$type": "dev.patrickgold.florisboard.ime.theme.Theme",
"name": "floris_black_borderless",
"label": "Floris Black Borderless",
"authors": [ "serebit" ],
"isNightTheme": true,
"attributes": {
"window": {
"colorPrimary": "#388E3C",
"colorPrimaryDark": "#306D32",
"colorAccent": "#FF9800",
"navigationBarColor": "@keyboard/background",
"navigationBarLight": "false",
"semiTransparentColor": "#20FFFFFF",
"textColor": "#EEEEEE"
},
"keyboard": {
"background": "#000000"
},
"key": {
"background": "transparent",
"backgroundPressed": "#7F616161",
"foreground": "@window/textColor",
"foregroundPressed": "@window/textColor",
"showBorder": "false"
},
"key:enter": {
"background": "@window/colorPrimary",
"backgroundPressed": "@window/colorPrimaryDark",
"foreground": "@window/textColor",
"foregroundPressed": "@window/textColor"
},
"key:shift:capslock": {
"foreground": "@window/colorAccent",
"foregroundPressed": "@window/colorAccent"
},
"key:space": {
"background": "#46616161"
},
"media": {
"foreground": "@window/textColor",
"foregroundAlt": "#BDBDBD"
},
"oneHanded": {
"background": "#000000",
"foreground": "@window/textColor"
},
"popup": {
"background": "#363636",
"backgroundActive": "#5F5F5F",
"foreground": "@window/textColor"
},
"privateMode": {
"background": "#7800BF",
"foreground": "@window/textColor"
},
"smartbar": {
"background": "transparent",
"foreground": "@window/textColor",
"foregroundAlt": "#73FFFFFF"
},
"smartbarButton": {
"background": "#212121",
"foreground": "@window/textColor"
},
"extractEditLayout": {
"background": "#282828",
"foreground": "@window/textColor",
"foregroundAlt": "#73FFFFFF"
},
"extractActionButton": {
"background": "@smartbarButton/background",
"foreground": "@smartbarButton/foreground"
},
"glideTrail": {"foreground": "#20388E3C"}
}
}

View File

@@ -1,74 +0,0 @@
{
"$type": "dev.patrickgold.florisboard.ime.theme.Theme",
"name": "floris_day",
"label": "Floris Day",
"authors": [ "patrickgold" ],
"isNightTheme": false,
"attributes": {
"window": {
"colorPrimary": "#4CAF50",
"colorPrimaryDark": "#388E3C",
"colorAccent": "#FF9800",
"navigationBarColor": "@keyboard/background",
"navigationBarLight": "true",
"semiTransparentColor": "#20000000",
"textColor": "#000000"
},
"keyboard": {
"background": "#E0E0E0"
},
"key": {
"background": "#FFFFFF",
"backgroundPressed": "#F5F5F5",
"foreground": "@window/textColor",
"foregroundPressed": "@window/textColor",
"showBorder": "true"
},
"key:enter": {
"background": "@window/colorPrimary",
"backgroundPressed": "@window/colorPrimaryDark",
"foreground": "#FFFFFF",
"foregroundPressed": "#FFFFFF"
},
"key:shift:capslock": {
"foreground": "@window/colorAccent",
"foregroundPressed": "@window/colorAccent"
},
"media": {
"foreground": "@window/textColor",
"foregroundAlt": "#757575"
},
"oneHanded": {
"background": "#E8F5E9",
"foreground": "#424242"
},
"popup": {
"background": "#EEEEEE",
"backgroundActive": "#BDBDBD",
"foreground": "@window/textColor"
},
"privateMode": {
"background": "#A000FF",
"foreground": "#FFFFFF"
},
"smartbar": {
"background": "transparent",
"foreground": "@window/textColor",
"foregroundAlt": "#8A8A8A"
},
"smartbarButton": {
"background": "@key/background",
"foreground": "@key/foreground"
},
"extractEditLayout": {
"background": "#E8E8E8",
"foreground": "@window/textColor",
"foregroundAlt": "#8A8A8A"
},
"extractActionButton": {
"background": "@smartbarButton/background",
"foreground": "@smartbarButton/foreground"
},
"glideTrail": {"foreground": "#204CAF50"}
}
}

View File

@@ -1,78 +0,0 @@
{
"$type": "dev.patrickgold.florisboard.ime.theme.Theme",
"name": "floris_day_borderless",
"label": "Floris Day Borderless",
"authors": [ "patrickgold" ],
"isNightTheme": false,
"attributes": {
"window": {
"colorPrimary": "#4CAF50",
"colorPrimaryDark": "#388E3C",
"colorAccent": "#FF9800",
"navigationBarColor": "@keyboard/background",
"navigationBarLight": "true",
"semiTransparentColor": "#20000000",
"textColor": "#000000"
},
"keyboard": {
"background": "#E0E0E0"
},
"key": {
"background": "transparent",
"backgroundPressed": "#7FF5F5F5",
"foreground": "@window/textColor",
"foregroundPressed": "@window/textColor",
"showBorder": "false"
},
"key:enter": {
"background": "@window/colorPrimary",
"backgroundPressed": "@window/colorPrimaryDark",
"foreground": "#FFFFFF",
"foregroundPressed": "#FFFFFF"
},
"key:shift:capslock": {
"foreground": "@window/colorAccent",
"foregroundPressed": "@window/colorAccent"
},
"key:space": {
"background": "#7FF5F5F5",
"backgroundPressed": "#FFF5F5F5"
},
"media": {
"foreground": "@window/textColor",
"foregroundAlt": "#757575"
},
"oneHanded": {
"background": "#E8F5E9",
"foreground": "#424242"
},
"popup": {
"background": "#EEEEEE",
"backgroundActive": "#BDBDBD",
"foreground": "@window/textColor"
},
"privateMode": {
"background": "#A000FF",
"foreground": "#FFFFFF"
},
"smartbar": {
"background": "transparent",
"foreground": "@window/textColor",
"foregroundAlt": "#8A8A8A"
},
"smartbarButton": {
"background": "#FFFFFF",
"foreground": "@window/textColor"
},
"extractEditLayout": {
"background": "#E8E8E8",
"foreground": "@window/textColor",
"foregroundAlt": "#8A8A8A"
},
"extractActionButton": {
"background": "@smartbarButton/background",
"foreground": "@smartbarButton/foreground"
},
"glideTrail": {"foreground": "#204CAF50"}
}
}

View File

@@ -1,74 +0,0 @@
{
"$type": "dev.patrickgold.florisboard.ime.theme.Theme",
"name": "floris_night",
"label": "Floris Night",
"authors": [ "patrickgold" ],
"isNightTheme": true,
"attributes": {
"window": {
"colorPrimary": "#4CAF50",
"colorPrimaryDark": "#388E3C",
"colorAccent": "#FF9800",
"navigationBarColor": "@keyboard/background",
"navigationBarLight": "false",
"semiTransparentColor": "#20FFFFFF",
"textColor": "#FFFFFF"
},
"keyboard": {
"background": "#212121"
},
"key": {
"background": "#424242",
"backgroundPressed": "#616161",
"foreground": "@window/textColor",
"foregroundPressed": "@window/textColor",
"showBorder": "true"
},
"key:enter": {
"background": "@window/colorPrimary",
"backgroundPressed": "@window/colorPrimaryDark",
"foreground": "#FFFFFF",
"foregroundPressed": "#FFFFFF"
},
"key:shift:capslock": {
"foreground": "@window/colorAccent",
"foregroundPressed": "@window/colorAccent"
},
"media": {
"foreground": "@window/textColor",
"foregroundAlt": "#BDBDBD"
},
"oneHanded": {
"background": "#1B5E20",
"foreground": "#EEEEEE"
},
"popup": {
"background": "#757575",
"backgroundActive": "#BDBDBD",
"foreground": "@window/textColor"
},
"privateMode": {
"background": "#A000FF",
"foreground": "#FFFFFF"
},
"smartbar": {
"background": "transparent",
"foreground": "@window/textColor",
"foregroundAlt": "#73FFFFFF"
},
"smartbarButton": {
"background": "@key/background",
"foreground": "@key/foreground"
},
"extractEditLayout": {
"background": "#282828",
"foreground": "@window/textColor",
"foregroundAlt": "#73FFFFFF"
},
"extractActionButton": {
"background": "@smartbarButton/background",
"foreground": "@smartbarButton/foreground"
},
"glideTrail": {"foreground": "#204CAF50"}
}
}

View File

@@ -1,78 +0,0 @@
{
"$type": "dev.patrickgold.florisboard.ime.theme.Theme",
"name": "floris_night_borderless",
"label": "Floris Night Borderless",
"authors": [ "patrickgold" ],
"isNightTheme": true,
"attributes": {
"window": {
"colorPrimary": "#4CAF50",
"colorPrimaryDark": "#388E3C",
"colorAccent": "#FF9800",
"navigationBarColor": "@keyboard/background",
"navigationBarLight": "false",
"semiTransparentColor": "#20FFFFFF",
"textColor": "#FFFFFF"
},
"keyboard": {
"background": "#212121"
},
"key": {
"background": "transparent",
"backgroundPressed": "#7F616161",
"foreground": "@window/textColor",
"foregroundPressed": "@window/textColor",
"showBorder": "false"
},
"key:enter": {
"background": "@window/colorPrimary",
"backgroundPressed": "@window/colorPrimaryDark",
"foreground": "#FFFFFF",
"foregroundPressed": "#FFFFFF"
},
"key:shift:capslock": {
"foreground": "@window/colorAccent",
"foregroundPressed": "@window/colorAccent"
},
"key:space": {
"background": "#2F616161",
"backgroundPressed": "#7F616161"
},
"media": {
"foreground": "@window/textColor",
"foregroundAlt": "#BDBDBD"
},
"oneHanded": {
"background": "#1B5E20",
"foreground": "#EEEEEE"
},
"popup": {
"background": "#757575",
"backgroundActive": "#BDBDBD",
"foreground": "@window/textColor"
},
"privateMode": {
"background": "#A000FF",
"foreground": "#FFFFFF"
},
"smartbar": {
"background": "transparent",
"foreground": "@window/textColor",
"foregroundAlt": "#73FFFFFF"
},
"smartbarButton": {
"background": "#424242",
"foreground": "@window/textColor"
},
"extractEditLayout": {
"background": "#282828",
"foreground": "@window/textColor",
"foregroundAlt": "#73FFFFFF"
},
"extractActionButton": {
"background": "@smartbarButton/background",
"foreground": "@smartbarButton/foreground"
},
"glideTrail": {"foreground": "#204CAF50"}
}
}

View File

@@ -17,6 +17,14 @@
"isBorderless": false,
"isMaterialYouAware": false
},
{
"id": "floris_day_borderless",
"label": "Floris Day (Borderless)",
"authors": [ "patrickgold" ],
"isNight": false,
"isBorderless": true,
"isMaterialYouAware": false
},
{
"id": "floris_night",
"label": "Floris Night",
@@ -24,6 +32,30 @@
"isNight": true,
"isBorderless": false,
"isMaterialYouAware": false
},
{
"id": "floris_night_borderless",
"label": "Floris Night (Borderless)",
"authors": [ "patrickgold" ],
"isNight": true,
"isBorderless": true,
"isMaterialYouAware": false
},
{
"id": "floris_pure_night",
"label": "Floris Pure Night",
"authors": [ "serebit" ],
"isNight": true,
"isBorderless": false,
"isMaterialYouAware": false
},
{
"id": "floris_pure_night_borderless",
"label": "Floris Pure Night (Borderless)",
"authors": [ "serebit" ],
"isNight": true,
"isBorderless": true,
"isMaterialYouAware": false
}
]
}

View File

@@ -24,8 +24,8 @@
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shadow-elevation": "2dp",
"shape": "var(--shape)"
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key:pressed": {
"background": "var(--surface-variant)",
@@ -39,7 +39,7 @@
"background": "var(--primary-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:shift}][mode={m:capslock}]": {
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
"foreground": "var(--secondary)"
},
"key[code={c:space}]": {
@@ -56,8 +56,8 @@
"background": "#eeeeee",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shadow-elevation": "2dp",
"shape": "var(--shape)"
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key-popup:focus": {
"background": "#bdbdbd",
@@ -67,8 +67,8 @@
"smartbar-primary-actions-toggle": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"shadow-elevation": "2dp",
"shape": "circle()"
"shape": "circle()",
"shadow-elevation": "2dp"
},
"smartbar-secondary-actions-toggle": {
"background": "transparent",
@@ -127,15 +127,15 @@
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shadow-elevation": "2dp",
"shape": "var(--shape-variant)"
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"clipboard-item-popup": {
"background": "var(--surface-variant)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shadow-elevation": "2dp",
"shape": "var(--shape-variant)"
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"emoji-key": {
@@ -152,8 +152,8 @@
"background": "#eeeeee",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shadow-elevation": "2dp",
"shape": "var(--shape)"
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"emoji-tab": {
"foreground": "var(--on-background)"
@@ -162,6 +162,23 @@
"foreground": "var(--primary)"
},
"extracted-landscape-input-layout": {
"background": "var(--background)"
},
"extracted-landscape-input-field": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "16sp",
"shape": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
"border-color": "var(--secondary)",
"border-width": "2dp"
},
"extracted-landscape-input-action": {
"background": "var(--primary)",
"foreground": "var(--on-surface)",
"shape": "rounded-corner(4dp, 4dp, 4dp, 4dp)"
},
"glide-trail": {
"foreground": "var(--primary)"
},

View File

@@ -0,0 +1,192 @@
{
"@defines": {
"--primary": "#4caf50",
"--primary-variant": "#388e3c",
"--secondary": "#ff9800",
"--secondary-variant": "#e65100",
"--background": "#e0e0e0",
"--surface": "#f0f0f0",
"--surface-variant": "#ffffff",
"--on-background": "#121212",
"--on-surface": "#000000",
"--on-surface-variant": "#5f5f5f",
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
},
"keyboard": {
"background": "var(--background)"
},
"key": {
"background": "transparent",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)"
},
"key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"key[code={c:enter}]": {
"background": "var(--primary)",
"foreground": "var(--on-surface)"
},
"key[code={c:enter}]:pressed": {
"background": "var(--primary-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
"foreground": "var(--secondary)"
},
"key[code={c:space}]": {
"background": "var(--surface)",
"foreground": "var(--on-surface-variant)",
"font-size": "12sp"
},
"key-hint": {
"background": "transparent",
"foreground": "var(--on-surface-variant)",
"font-size": "12sp"
},
"key-popup": {
"background": "#eeeeee",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key-popup:focus": {
"background": "#bdbdbd",
"foreground": "var(--on-surface)"
},
"smartbar-primary-actions-toggle": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"shape": "circle()"
},
"smartbar-secondary-actions-toggle": {
"background": "transparent",
"foreground": "var(--on-surface-variant)",
"shape": "circle()"
},
"smartbar-quick-action": {
"background": "transparent",
"foreground": "var(--on-background)",
"shape": "circle()"
},
"smartbar-key": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "18sp",
"shape": "var(--shape)"
},
"smartbar-key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"smartbar-key:disabled": {
"background": "transparent",
"foreground": "#12121248"
},
"smartbar-candidate-word": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "14sp",
"shape": "rectangle()"
},
"smartbar-candidate-word:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-background)"
},
"smartbar-candidate-clip": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "14sp",
"shape": "rounded-corner(8%, 8%, 8%, 8%)"
},
"smartbar-candidate-clip:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-background)"
},
"smartbar-candidate-spacer": {
"foreground": "var(--surface)"
},
"clipboard-header": {
"background": "transparent",
"foreground": "var(--on-surface)",
"font-size": "16sp"
},
"clipboard-item": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"clipboard-item-popup": {
"background": "var(--surface-variant)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"emoji-key": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "22sp",
"shape": "var(--shape)"
},
"emoji-key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"emoji-key-popup": {
"background": "#eeeeee",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"emoji-tab": {
"foreground": "var(--on-background)"
},
"emoji-tab:focus": {
"foreground": "var(--primary)"
},
"extracted-landscape-input-layout": {
"background": "var(--background)"
},
"extracted-landscape-input-field": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "16sp",
"shape": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
"border-color": "var(--secondary)",
"border-width": "2dp"
},
"extracted-landscape-input-action": {
"background": "var(--primary)",
"foreground": "var(--on-surface)",
"shape": "rounded-corner(4dp, 4dp, 4dp, 4dp)"
},
"glide-trail": {
"foreground": "var(--primary)"
},
"one-handed-panel": {
"background": "#e8f5e9",
"foreground": "#424242"
},
"system-nav-bar": {
"background": "var(--background)"
}
}

View File

@@ -24,8 +24,8 @@
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shadow-elevation": "2dp",
"shape": "var(--shape)"
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key:pressed": {
"background": "var(--surface-variant)",
@@ -39,7 +39,7 @@
"background": "var(--primary-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:shift}][mode={m:capslock}]": {
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
"foreground": "var(--secondary)"
},
"key[code={c:space}]": {
@@ -56,8 +56,8 @@
"background": "#757575",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shadow-elevation": "2dp",
"shape": "var(--shape)"
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key-popup:focus": {
"background": "#bdbdbd",
@@ -67,8 +67,8 @@
"smartbar-primary-actions-toggle": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"shadow-elevation": "2dp",
"shape": "circle()"
"shape": "circle()",
"shadow-elevation": "2dp"
},
"smartbar-secondary-actions-toggle": {
"background": "transparent",
@@ -127,15 +127,15 @@
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shadow-elevation": "2dp",
"shape": "var(--shape-variant)"
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"clipboard-item-popup": {
"background": "var(--surface-variant)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shadow-elevation": "2dp",
"shape": "var(--shape-variant)"
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"emoji-key": {
@@ -152,8 +152,8 @@
"background": "#757575",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shadow-elevation": "2dp",
"shape": "var(--shape)"
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"emoji-tab": {
"foreground": "var(--on-background)"
@@ -162,6 +162,23 @@
"foreground": "var(--primary)"
},
"extracted-landscape-input-layout": {
"background": "var(--background)"
},
"extracted-landscape-input-field": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "16sp",
"shape": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
"border-color": "var(--secondary)",
"border-width": "2dp"
},
"extracted-landscape-input-action": {
"background": "var(--primary)",
"foreground": "var(--on-surface)",
"shape": "rounded-corner(4dp, 4dp, 4dp, 4dp)"
},
"glide-trail": {
"foreground": "var(--primary)"
},

View File

@@ -0,0 +1,192 @@
{
"@defines": {
"--primary": "#4caf50",
"--primary-variant": "#388e3c",
"--secondary": "#f57c00",
"--secondary-variant": "#e65100",
"--background": "#212121",
"--surface": "#424242",
"--surface-variant": "#616161",
"--on-background": "#dcdcdc",
"--on-surface": "#ffffff",
"--on-surface-variant": "#a0a0a0",
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
},
"keyboard": {
"background": "var(--background)"
},
"key": {
"background": "transparent",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)"
},
"key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"key[code={c:enter}]": {
"background": "var(--primary)",
"foreground": "var(--on-surface)"
},
"key[code={c:enter}]:pressed": {
"background": "var(--primary-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
"foreground": "var(--secondary)"
},
"key[code={c:space}]": {
"background": "var(--surface)",
"foreground": "var(--on-surface-variant)",
"font-size": "12sp"
},
"key-hint": {
"background": "transparent",
"foreground": "var(--on-surface-variant)",
"font-size": "12sp"
},
"key-popup": {
"background": "#757575",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key-popup:focus": {
"background": "#bdbdbd",
"foreground": "var(--on-surface)"
},
"smartbar-primary-actions-toggle": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"shape": "circle()"
},
"smartbar-secondary-actions-toggle": {
"background": "transparent",
"foreground": "var(--on-surface-variant)",
"shape": "circle()"
},
"smartbar-quick-action": {
"background": "transparent",
"foreground": "var(--on-background)",
"shape": "circle()"
},
"smartbar-key": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "18sp",
"shape": "var(--shape)"
},
"smartbar-key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"smartbar-key:disabled": {
"background": "transparent",
"foreground": "#dcdcdc48"
},
"smartbar-candidate-word": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "14sp",
"shape": "rectangle()"
},
"smartbar-candidate-word:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-background)"
},
"smartbar-candidate-clip": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "14sp",
"shape": "rounded-corner(8%, 8%, 8%, 8%)"
},
"smartbar-candidate-clip:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-background)"
},
"smartbar-candidate-spacer": {
"foreground": "var(--surface)"
},
"clipboard-header": {
"background": "transparent",
"foreground": "var(--on-surface)",
"font-size": "16sp"
},
"clipboard-item": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"clipboard-item-popup": {
"background": "var(--surface-variant)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"emoji-key": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "22sp",
"shape": "var(--shape)"
},
"emoji-key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"emoji-key-popup": {
"background": "#757575",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"emoji-tab": {
"foreground": "var(--on-background)"
},
"emoji-tab:focus": {
"foreground": "var(--primary)"
},
"extracted-landscape-input-layout": {
"background": "var(--background)"
},
"extracted-landscape-input-field": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "16sp",
"shape": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
"border-color": "var(--secondary)",
"border-width": "2dp"
},
"extracted-landscape-input-action": {
"background": "var(--primary)",
"foreground": "var(--on-surface)",
"shape": "rounded-corner(4dp, 4dp, 4dp, 4dp)"
},
"glide-trail": {
"foreground": "var(--primary)"
},
"one-handed-panel": {
"background": "#1b5e20",
"foreground": "#eeeeee"
},
"system-nav-bar": {
"background": "var(--background)"
}
}

View File

@@ -0,0 +1,194 @@
{
"@defines": {
"--primary": "#388e3c",
"--primary-variant": "#306d32",
"--secondary": "#ff9800",
"--secondary-variant": "#804c00",
"--background": "#000000",
"--surface": "#212121",
"--surface-variant": "#3d3d3d",
"--on-background": "#eeeeee",
"--on-surface": "#eeeeee",
"--on-surface-variant": "#ffffff73",
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
},
"keyboard": {
"background": "var(--background)"
},
"key": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key:pressed": {
"background": "var(--surface-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:enter}]": {
"background": "var(--primary)",
"foreground": "var(--on-surface)"
},
"key[code={c:enter}]:pressed": {
"background": "var(--primary-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
"foreground": "var(--secondary)"
},
"key[code={c:space}]": {
"background": "var(--surface)",
"foreground": "var(--on-surface-variant)",
"font-size": "12sp"
},
"key-hint": {
"background": "transparent",
"foreground": "var(--on-surface-variant)",
"font-size": "12sp"
},
"key-popup": {
"background": "#424242",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key-popup:focus": {
"background": "#707070",
"foreground": "var(--on-surface)"
},
"smartbar-primary-actions-toggle": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"shape": "circle()",
"shadow-elevation": "2dp"
},
"smartbar-secondary-actions-toggle": {
"background": "transparent",
"foreground": "var(--on-surface-variant)",
"shape": "circle()"
},
"smartbar-quick-action": {
"background": "transparent",
"foreground": "var(--on-background)",
"shape": "circle()"
},
"smartbar-key": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "18sp",
"shape": "var(--shape)"
},
"smartbar-key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"smartbar-key:disabled": {
"background": "transparent",
"foreground": "#dcdcdc48"
},
"smartbar-candidate-word": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "14sp",
"shape": "rectangle()"
},
"smartbar-candidate-word:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-background)"
},
"smartbar-candidate-clip": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "14sp",
"shape": "rounded-corner(8%, 8%, 8%, 8%)"
},
"smartbar-candidate-clip:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-background)"
},
"smartbar-candidate-spacer": {
"foreground": "var(--surface)"
},
"clipboard-header": {
"background": "transparent",
"foreground": "var(--on-surface)",
"font-size": "16sp"
},
"clipboard-item": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"clipboard-item-popup": {
"background": "var(--surface-variant)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"emoji-key": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "22sp",
"shape": "var(--shape)"
},
"emoji-key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"emoji-key-popup": {
"background": "#757575",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"emoji-tab": {
"foreground": "var(--on-background)"
},
"emoji-tab:focus": {
"foreground": "var(--primary)"
},
"extracted-landscape-input-layout": {
"background": "var(--background)"
},
"extracted-landscape-input-field": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "16sp",
"shape": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
"border-color": "var(--secondary-variant)",
"border-width": "1dp"
},
"extracted-landscape-input-action": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"shape": "rounded-corner(4dp, 4dp, 4dp, 4dp)"
},
"glide-trail": {
"foreground": "var(--primary-variant)"
},
"one-handed-panel": {
"background": "#000000",
"foreground": "#eeeeee"
},
"system-nav-bar": {
"background": "var(--background)"
}
}

View File

@@ -0,0 +1,193 @@
{
"@defines": {
"--primary": "#388e3c",
"--primary-variant": "#306d32",
"--secondary": "#ff9800",
"--secondary-variant": "#804c00",
"--background": "#000000",
"--surface": "#212121",
"--surface-variant": "#3d3d3d",
"--on-background": "#eeeeee",
"--on-surface": "#eeeeee",
"--on-surface-variant": "#ffffff73",
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
},
"keyboard": {
"background": "var(--background)"
},
"key": {
"background": "transparent",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)"
},
"key:pressed": {
"background": "#6161617f",
"foreground": "var(--on-surface)"
},
"key[code={c:enter}]": {
"background": "var(--primary)",
"foreground": "var(--on-surface)"
},
"key[code={c:enter}]:pressed": {
"background": "var(--primary-variant)",
"foreground": "var(--on-surface)"
},
"key[code={c:shift}][shiftstate={sh:caps_lock}]": {
"foreground": "var(--secondary)"
},
"key[code={c:space}]": {
"background": "#61616146",
"foreground": "var(--on-surface-variant)",
"font-size": "12sp"
},
"key-hint": {
"background": "transparent",
"foreground": "var(--on-surface-variant)",
"font-size": "12sp"
},
"key-popup": {
"background": "#363636",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"key-popup:focus": {
"background": "#5F5F5F",
"foreground": "var(--on-surface)"
},
"smartbar-primary-actions-toggle": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"shape": "circle()",
"shadow-elevation": "2dp"
},
"smartbar-secondary-actions-toggle": {
"background": "transparent",
"foreground": "var(--on-surface-variant)",
"shape": "circle()"
},
"smartbar-quick-action": {
"background": "transparent",
"foreground": "var(--on-background)",
"shape": "circle()"
},
"smartbar-key": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "18sp",
"shape": "var(--shape)"
},
"smartbar-key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"smartbar-key:disabled": {
"background": "transparent",
"foreground": "#dcdcdc48"
},
"smartbar-candidate-word": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "14sp",
"shape": "rectangle()"
},
"smartbar-candidate-word:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-background)"
},
"smartbar-candidate-clip": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "14sp",
"shape": "rounded-corner(8%, 8%, 8%, 8%)"
},
"smartbar-candidate-clip:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-background)"
},
"smartbar-candidate-spacer": {
"foreground": "var(--surface)"
},
"clipboard-header": {
"background": "transparent",
"foreground": "var(--on-surface)",
"font-size": "16sp"
},
"clipboard-item": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"clipboard-item-popup": {
"background": "var(--surface-variant)",
"foreground": "var(--on-surface)",
"font-size": "14sp",
"shape": "var(--shape-variant)",
"shadow-elevation": "2dp"
},
"emoji-key": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "22sp",
"shape": "var(--shape)"
},
"emoji-key:pressed": {
"background": "var(--surface)",
"foreground": "var(--on-surface)"
},
"emoji-key-popup": {
"background": "#757575",
"foreground": "var(--on-surface)",
"font-size": "22sp",
"shape": "var(--shape)",
"shadow-elevation": "2dp"
},
"emoji-tab": {
"foreground": "var(--on-background)"
},
"emoji-tab:focus": {
"foreground": "var(--primary)"
},
"extracted-landscape-input-layout": {
"background": "var(--background)"
},
"extracted-landscape-input-field": {
"background": "transparent",
"foreground": "var(--on-background)",
"font-size": "16sp",
"shape": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
"border-color": "var(--secondary-variant)",
"border-width": "1dp"
},
"extracted-landscape-input-action": {
"background": "var(--surface)",
"foreground": "var(--on-surface)",
"shape": "rounded-corner(4dp, 4dp, 4dp, 4dp)"
},
"glide-trail": {
"foreground": "var(--primary-variant)"
},
"one-handed-panel": {
"background": "#000000",
"foreground": "#eeeeee"
},
"system-nav-bar": {
"background": "var(--background)"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -1,348 +0,0 @@
/*
* Copyright (C) 2022 Patrick Goldinger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.theme
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.unit.dp
import com.google.accompanist.flowlayout.FlowRow
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisChip
import dev.patrickgold.florisboard.app.ui.components.FlorisDropdownMenu
import dev.patrickgold.florisboard.app.ui.components.FlorisHyperlinkText
import dev.patrickgold.florisboard.app.ui.components.FlorisIconButton
import dev.patrickgold.florisboard.app.ui.components.FlorisOutlinedTextField
import dev.patrickgold.florisboard.app.ui.components.florisHorizontalScroll
import dev.patrickgold.florisboard.common.kotlin.curlyFormat
import dev.patrickgold.florisboard.ime.nlp.NATIVE_NULLPTR
import dev.patrickgold.florisboard.ime.text.key.InputMode
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.theme.FlorisImeUiSpec
import dev.patrickgold.florisboard.snygg.SnyggLevel
import dev.patrickgold.florisboard.snygg.SnyggRule
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
internal val SnyggEmptyRuleForAdding = SnyggRule(element = "- select -")
@Composable
internal fun EditRuleDialog(
initRule: SnyggRule,
level: SnyggLevel,
onConfirmRule: (oldRule: SnyggRule, newRule: SnyggRule) -> Boolean,
onDeleteRule: (rule: SnyggRule) -> Unit,
onDismiss: () -> Unit,
) {
val isAddRuleDialog = initRule == SnyggEmptyRuleForAdding
var showSelectAsError by rememberSaveable { mutableStateOf(false) }
var showAlreadyExistsError by rememberSaveable { mutableStateOf(false) }
val possibleElementNames = remember {
listOf(SnyggEmptyRuleForAdding.element) + FlorisImeUiSpec.elements.keys
}
val possibleElementLabels = possibleElementNames.map { translateElementName(it, level) ?: it }
var elementsExpanded by remember { mutableStateOf(false) }
var elementsSelectedIndex by rememberSaveable {
val index = possibleElementNames.indexOf(initRule.element).coerceIn(possibleElementNames.indices)
mutableStateOf(index)
}
val codes = rememberSaveable(saver = IntListSaver) { initRule.codes.toMutableStateList() }
var editCodeDialogValue by rememberSaveable { mutableStateOf<Int?>(null) }
val groups = rememberSaveable(saver = IntListSaver) { initRule.groups.toMutableStateList() }
var modeNormal by rememberSaveable { mutableStateOf(initRule.modes.contains(InputMode.NORMAL.value)) }
var modeShiftLock by rememberSaveable { mutableStateOf(initRule.modes.contains(InputMode.SHIFT_LOCK.value)) }
var modeCapsLock by rememberSaveable { mutableStateOf(initRule.modes.contains(InputMode.CAPS_LOCK.value)) }
var pressedSelector by rememberSaveable { mutableStateOf(initRule.pressedSelector) }
var focusSelector by rememberSaveable { mutableStateOf(initRule.focusSelector) }
var disabledSelector by rememberSaveable { mutableStateOf(initRule.disabledSelector) }
JetPrefAlertDialog(
title = stringRes(if (isAddRuleDialog) {
R.string.settings__theme_editor__add_rule
} else {
R.string.settings__theme_editor__edit_rule
}),
confirmLabel = stringRes(if (isAddRuleDialog) {
R.string.action__add
} else {
R.string.action__apply
}),
onConfirm = {
if (isAddRuleDialog && elementsSelectedIndex == 0) {
showSelectAsError = true
} else {
val newRule = SnyggRule(
element = possibleElementNames[elementsSelectedIndex],
codes = codes.toList(),
groups = groups.toList(),
modes = buildList {
if (modeNormal) { add(InputMode.NORMAL.value) }
if (modeShiftLock) { add(InputMode.SHIFT_LOCK.value) }
if (modeCapsLock) { add(InputMode.CAPS_LOCK.value) }
},
pressedSelector = pressedSelector,
focusSelector = focusSelector,
disabledSelector = disabledSelector,
)
if (!onConfirmRule(initRule, newRule)) {
showAlreadyExistsError = true
}
}
},
dismissLabel = stringRes(R.string.action__cancel),
onDismiss = onDismiss,
neutralLabel = if (!isAddRuleDialog) {
stringRes(R.string.action__delete)
} else {
null
},
neutralColors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colors.error),
onNeutral = { onDeleteRule(initRule) },
) {
Column {
AnimatedVisibility(visible = showAlreadyExistsError) {
Text(
modifier = Modifier.padding(bottom = 16.dp),
text = stringRes(R.string.settings__theme_editor__rule_already_exists),
color = MaterialTheme.colors.error,
)
}
DialogProperty(text = stringRes(R.string.settings__theme_editor__rule_element)) {
FlorisDropdownMenu(
items = possibleElementLabels,
expanded = elementsExpanded,
enabled = isAddRuleDialog,
selectedIndex = elementsSelectedIndex,
isError = showSelectAsError && elementsSelectedIndex == 0,
onSelectItem = { elementsSelectedIndex = it },
onExpandRequest = { elementsExpanded = true },
onDismissRequest = { elementsExpanded = false },
)
}
DialogProperty(text = stringRes(R.string.settings__theme_editor__rule_selectors)) {
Row(modifier = Modifier.florisHorizontalScroll()) {
FlorisChip(
onClick = { pressedSelector = !pressedSelector },
modifier = Modifier.padding(end = 4.dp),
text = when (level) {
SnyggLevel.DEVELOPER -> SnyggRule.PRESSED_SELECTOR
else -> stringRes(R.string.snygg__rule_selector__pressed)
},
color = if (pressedSelector) MaterialTheme.colors.primaryVariant else Color.Unspecified,
)
FlorisChip(
onClick = { focusSelector = !focusSelector },
modifier = Modifier.padding( end = 4.dp),
text = when (level) {
SnyggLevel.DEVELOPER -> SnyggRule.FOCUS_SELECTOR
else -> stringRes(R.string.snygg__rule_selector__focus)
},
color = if (focusSelector) MaterialTheme.colors.primaryVariant else Color.Unspecified,
)
FlorisChip(
onClick = { disabledSelector = !disabledSelector },
text = when (level) {
SnyggLevel.DEVELOPER -> SnyggRule.DISABLED_SELECTOR
else -> stringRes(R.string.snygg__rule_selector__disabled)
},
color = if (disabledSelector) MaterialTheme.colors.primaryVariant else Color.Unspecified,
)
}
}
DialogProperty(
text = stringRes(R.string.settings__theme_editor__rule_codes),
trailingIconTitle = {
FlorisIconButton(
onClick = { editCodeDialogValue = NATIVE_NULLPTR },
modifier = Modifier.offset(x = 12.dp),
icon = painterResource(R.drawable.ic_add),
)
},
) {
if (codes.isEmpty()) {
Text(
modifier = Modifier.padding(vertical = 4.dp),
text = stringRes(R.string.settings__theme_editor__no_codes_defined),
fontStyle = FontStyle.Italic,
)
}
FlowRow {
for (code in codes) {
FlorisChip(
onClick = { editCodeDialogValue = code },
text = code.toString(),
shape = MaterialTheme.shapes.medium,
)
}
}
}
DialogProperty(text = stringRes(R.string.settings__theme_editor__rule_modes)) {
Row(modifier = Modifier.florisHorizontalScroll()) {
FlorisChip(
onClick = { modeNormal = !modeNormal },
modifier = Modifier.padding(end = 4.dp),
text = when (level) {
SnyggLevel.DEVELOPER -> remember { "m:${InputMode.NORMAL.toString().lowercase()}" }
else -> stringRes(R.string.enum__input_mode__normal)
},
color = if (modeNormal) MaterialTheme.colors.primaryVariant else Color.Unspecified,
)
FlorisChip(
onClick = { modeShiftLock = !modeShiftLock },
modifier = Modifier.padding(end = 4.dp),
text = when (level) {
SnyggLevel.DEVELOPER -> remember { "m:${InputMode.SHIFT_LOCK.toString().lowercase()}" }
else -> stringRes(R.string.enum__input_mode__shift_lock)
},
color = if (modeShiftLock) MaterialTheme.colors.primaryVariant else Color.Unspecified,
)
FlorisChip(
onClick = { modeCapsLock = !modeCapsLock },
text = when (level) {
SnyggLevel.DEVELOPER -> remember { "m:${InputMode.CAPS_LOCK.toString().lowercase()}" }
else -> stringRes(R.string.enum__input_mode__caps_lock)
},
color = if (modeCapsLock) MaterialTheme.colors.primaryVariant else Color.Unspecified,
)
}
}
}
}
val initCodeValue = editCodeDialogValue
if (initCodeValue != null) {
var inputCodeString by rememberSaveable(initCodeValue) { mutableStateOf(initCodeValue.toString()) }
var showKeyCodesHelp by rememberSaveable(initCodeValue) { mutableStateOf(false) }
var showError by rememberSaveable(initCodeValue) { mutableStateOf(false) }
var errorId by rememberSaveable(initCodeValue) { mutableStateOf(NATIVE_NULLPTR) }
JetPrefAlertDialog(
title = stringRes(if (initCodeValue == NATIVE_NULLPTR) {
R.string.settings__theme_editor__add_code
} else {
R.string.settings__theme_editor__edit_code
}),
confirmLabel = stringRes(if (initCodeValue == NATIVE_NULLPTR) {
R.string.action__add
} else {
R.string.action__apply
}),
onConfirm = {
val code = inputCodeString.trim().toIntOrNull(radix = 10)
when {
code == null || (code !in KeyCode.Spec.CHARACTERS && code !in KeyCode.Spec.INTERNAL) -> {
errorId = R.string.settings__theme_editor__code_invalid
showError = true
}
code == initCodeValue -> {
editCodeDialogValue = null
}
codes.contains(code) -> {
errorId = R.string.settings__theme_editor__code_already_exists
showError = true
}
else -> {
if (initCodeValue != NATIVE_NULLPTR) {
codes.remove(initCodeValue)
}
codes.add(code)
editCodeDialogValue = null
}
}
},
dismissLabel = stringRes(R.string.action__cancel),
onDismiss = {
editCodeDialogValue = null
},
neutralLabel = if (initCodeValue != NATIVE_NULLPTR) {
stringRes(R.string.action__delete)
} else {
null
},
neutralColors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colors.error),
onNeutral = {
codes.remove(initCodeValue)
editCodeDialogValue = null
},
trailingIconTitle = {
FlorisIconButton(
onClick = { showKeyCodesHelp = !showKeyCodesHelp },
modifier = Modifier.offset(x = 12.dp),
icon = painterResource(R.drawable.ic_help_outline),
)
},
) {
Column {
AnimatedVisibility(visible = showKeyCodesHelp) {
Column(modifier = Modifier.padding(bottom = 16.dp)) {
Text(text = stringRes(R.string.settings__theme_editor__code_help_text))
FlorisHyperlinkText(
text = "Characters (unicode-table.com)",
url = stringRes(R.string.florisboard__character_key_codes_url),
)
FlorisHyperlinkText(
text = "Internal (github.com)",
url = stringRes(R.string.florisboard__internal_key_codes_url),
)
}
}
FlorisOutlinedTextField(
value = inputCodeString,
onValueChange = { v ->
inputCodeString = v
showError = false
},
isError = showError,
singleLine = true,
)
AnimatedVisibility(visible = showError) {
Text(
modifier = Modifier.padding(top = 4.dp),
text = stringRes(errorId).curlyFormat(
"c_min" to KeyCode.Spec.CHARACTERS_MIN,
"c_max" to KeyCode.Spec.CHARACTERS_MAX,
"i_min" to KeyCode.Spec.INTERNAL_MIN,
"i_max" to KeyCode.Spec.INTERNAL_MAX,
),
color = MaterialTheme.colors.error,
)
}
}
}
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright (C) 2020 Patrick Goldinger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.patrickgold.florisboard.common
import android.content.Context
import android.util.AttributeSet
import android.widget.ViewFlipper
/**
* Custom ViewFlipper class used to prevent an unnecessary exception to be thrown when it is
* detached from a window.
*
* Based on the solution of this SO answer: https://stackoverflow.com/a/8208874/6801193
*/
class FlorisViewFlipper : ViewFlipper {
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
override fun onDetachedFromWindow() {
try {
super.onDetachedFromWindow()
} catch (e: IllegalArgumentException) {
stopFlipping()
}
}
}

View File

@@ -1,368 +0,0 @@
/*
* 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.core
import android.os.SystemClock
import androidx.collection.SparseArrayCompat
import androidx.collection.forEach
import androidx.collection.set
import dev.patrickgold.florisboard.debug.LogTopic
import dev.patrickgold.florisboard.debug.flogDebug
import dev.patrickgold.florisboard.ime.keyboard.KeyData
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
/**
* The main logic point of processing input events and delegating them to the registered event receivers. Currently,
* only [InputKeyEvent]s are supported, but in the future this class is thought to be the single point where input
* events can be dispatched.
*/
class InputEventDispatcher private constructor(
channelCapacity: Int,
private val mainDispatcher: CoroutineDispatcher,
private val defaultDispatcher: CoroutineDispatcher,
private val repeatableKeyCodes: IntArray
) : InputKeyEventSender {
private val channel: Channel<InputKeyEvent> = Channel(channelCapacity)
private val mainScope: CoroutineScope = CoroutineScope(mainDispatcher + SupervisorJob())
private val defaultScope: CoroutineScope = CoroutineScope(defaultDispatcher + SupervisorJob())
private val pressedKeys: SparseArrayCompat<PressedKeyInfo> = SparseArrayCompat()
var lastKeyEventDown: InputKeyEvent? = null
private set
var lastKeyEventUp: InputKeyEvent? = null
private set
/**
* The input key event register. If null, the dispatcher will still process input, but won't dispatch them to an
* event receiver.
*/
var keyEventReceiver: InputKeyEventReceiver? = null
companion object {
/**
* The default input event channel capacity to be used in [new].
*/
private const val DEFAULT_CHANNEL_CAPACITY: Int = 64
/**
* Creates a new [InputEventDispatcher] instance from given arguments and returns it.
*
* @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].
* @param defaultDispatcher The default dispatcher used to switch the context to call the receiver callbacks.
* Defaults to [Dispatchers.Default].
* @param repeatableKeyCodes An int array of all key codes which are repeatable while being pressed down.
*
* @return A new [InputEventDispatcher] instance initialized with given arguments.
*/
fun new(
channelCapacity: Int = DEFAULT_CHANNEL_CAPACITY,
mainDispatcher: CoroutineDispatcher = Dispatchers.Main.immediate,
defaultDispatcher: CoroutineDispatcher = Dispatchers.Default,
repeatableKeyCodes: IntArray = intArrayOf()
): InputEventDispatcher = InputEventDispatcher(
channelCapacity, mainDispatcher, defaultDispatcher, repeatableKeyCodes.clone()
)
private fun <T> SparseArrayCompat<T>.removeAndReturn(key: Int): T? {
val elem = get(key)
return if (elem == null) {
null
} else {
remove(key)
elem
}
}
}
init {
defaultScope.launch {
for (ev in channel) {
if (!isActive) break
val startTime = System.nanoTime()
flogDebug(LogTopic.KEY_EVENTS) { ev.toString() }
when (ev.action) {
InputKeyEvent.Action.DOWN -> {
if (pressedKeys.indexOfKey(ev.data.code) >= 0) continue
pressedKeys[ev.data.code] = PressedKeyInfo(
eventTimeDown = ev.eventTime,
repeatKeyPressJob = if (!repeatableKeyCodes.contains(ev.data.code)) { null } else {
defaultScope.launch {
delay(600)
while (isActive) {
channel.send(InputKeyEvent.repeat(ev.data))
delay(50)
}
}
}
)
withContext(mainDispatcher) {
keyEventReceiver?.onInputKeyDown(ev)
}
if (ev.data.code != KeyCode.INTERNAL_BATCH_EDIT) {
lastKeyEventDown = ev
}
}
InputKeyEvent.Action.DOWN_UP -> {
pressedKeys.removeAndReturn(ev.data.code)?.repeatKeyPressJob?.cancel()
withContext(mainDispatcher) {
keyEventReceiver?.onInputKeyDown(ev)
keyEventReceiver?.onInputKeyUp(ev)
}
if (ev.data.code != KeyCode.INTERNAL_BATCH_EDIT) {
lastKeyEventDown = ev
lastKeyEventUp = ev
}
}
InputKeyEvent.Action.UP -> {
pressedKeys.removeAndReturn(ev.data.code)?.repeatKeyPressJob?.cancel()
withContext(mainDispatcher) {
keyEventReceiver?.onInputKeyUp(ev)
}
if (ev.data.code != KeyCode.INTERNAL_BATCH_EDIT) {
lastKeyEventUp = ev
}
}
InputKeyEvent.Action.REPEAT -> {
if (pressedKeys.indexOfKey(ev.data.code) >= 0) {
withContext(mainDispatcher) {
keyEventReceiver?.onInputKeyRepeat(ev)
}
}
}
InputKeyEvent.Action.CANCEL -> {
pressedKeys.removeAndReturn(ev.data.code)?.repeatKeyPressJob?.cancel()
withContext(mainDispatcher) {
keyEventReceiver?.onInputKeyCancel(ev)
}
}
}
flogDebug(LogTopic.KEY_EVENTS) { "Time elapsed: ${(System.nanoTime() - startTime) / 1_000_000}" }
}
pressedKeys.forEach { _, value -> value.repeatKeyPressJob?.cancel() }
pressedKeys.clear()
}
}
override fun send(ev: InputKeyEvent) {
channel.trySend(ev)
}
/**
* Checks if there's currently a key down with given [code].
*
* @param code The key code to check for.
*
* @return True if the given [code] is currently down, false otherwise.
*/
fun isPressed(code: Int): Boolean {
return pressedKeys.indexOfKey(code) >= 0
}
/**
* Closes this dispatcher and cancels the local coroutine scope.
*/
fun close() {
keyEventReceiver = null
mainScope.cancel()
defaultScope.cancel()
}
data class PressedKeyInfo(
val eventTimeDown: Long,
val repeatKeyPressJob: Job?
)
}
/**
* Data class representing a single input key event.
*
* @property eventTime The exact event time when this event occurred, measured in milliseconds since a static point in
* the past. The exact point is irrelevant, but while this input dispatcher is active, the point must not change in
* order for difference time calculation to succeed.
* @property action The action of this event.
* @property data The data of this event.
* @property count The count how often this event occurred. Is only respected by other methods if the [action] of this
* event is [Action.DOWN_UP] or [Action.REPEAT], else always 1 is assumed.
*/
data class InputKeyEvent(
val eventTime: Long,
val action: Action,
val data: KeyData,
val count: Int
) {
companion object {
/**
* Creates a new input key event with given [keyData] and sets the action to [Action.DOWN].
*
* @param keyData The key data of the input key event event to create.
*
* @return The created input key event.
*/
fun down(keyData: KeyData): InputKeyEvent {
return InputKeyEvent(
eventTime = SystemClock.uptimeMillis(),
action = Action.DOWN,
data = keyData,
count = 1
)
}
/**
* Creates a new input key event with given [keyData] and sets the action to [Action.DOWN_UP].
*
* @param keyData The key data of the input key event event to create.
* @param count How often this event occurred. Must be grater or equal to 1, defaults to 1.
*
* @return The created input key event.
*/
fun downUp(keyData: KeyData, count: Int = 1): InputKeyEvent {
return InputKeyEvent(
eventTime = SystemClock.uptimeMillis(),
action = Action.DOWN_UP,
data = keyData,
count = count
)
}
/**
* Creates a new input key event with given [keyData] and sets the action to [Action.UP].
*
* @param keyData The key data of the input key event event to create.
*
* @return The created input key event.
*/
fun up(keyData: KeyData): InputKeyEvent {
return InputKeyEvent(
eventTime = SystemClock.uptimeMillis(),
action = Action.UP,
data = keyData,
count = 1
)
}
/**
* Creates a new input key event with given [keyData] and sets the action to [Action.REPEAT].
*
* @param keyData The key data of the input key event event to create.
* @param count How often this event occurred. Must be grater or equal to 1, defaults to 1.
*
* @return The created input key event.
*/
fun repeat(keyData: KeyData, count: Int = 1): InputKeyEvent {
return InputKeyEvent(
eventTime = SystemClock.uptimeMillis(),
action = Action.REPEAT,
data = keyData,
count = count
)
}
/**
* Creates a new input key event with given [keyData] and sets the action to [Action.CANCEL].
*
* @param keyData The key data of the input key event event to create.
*
* @return The created input key event.
*/
fun cancel(keyData: KeyData): InputKeyEvent {
return InputKeyEvent(
eventTime = SystemClock.uptimeMillis(),
action = Action.CANCEL,
data = keyData,
count = 1
)
}
}
/**
* Checks if the [other] input key event is a consecutive event while respecting [maxEventTimeDiff].
*
* @param other The other input key event to compare with this one.
* @param maxEventTimeDiff The maximum event time diff between this event and [other], in milliseconds.
*
* @return True if this event is a consecutive event of [other], false otherwise.
*/
fun isConsecutiveEventOf(other: InputKeyEvent?, maxEventTimeDiff: Long): Boolean {
return other != null && data.code == other.data.code && eventTime - other.eventTime <= maxEventTimeDiff
}
/**
* Returns a string representation of this input key event.
*/
override fun toString(): String {
return "FlorisKeyEvent { eventTime=${eventTime}ms, action=$action, data=$data, count=$count }"
}
/**
* The action of an input key event.
*/
enum class Action {
DOWN,
DOWN_UP,
UP,
REPEAT,
CANCEL,
}
}
/**
* Interface which represents an input key event sender.
*/
interface InputKeyEventSender {
/**
* Sends given input key event [ev] to the underlying input channel, awaiting to be processed.
*
* @param ev The input key event to send.
*/
fun send(ev: InputKeyEvent)
}
/**
* Interface which represents an input key event receiver.
*/
interface InputKeyEventReceiver {
/**
* Event method which gets called when a key went down.
*
* @param ev The associated input key event.
*/
fun onInputKeyDown(ev: InputKeyEvent)
/**
* Event method which gets called when a key went up.
*
* @param ev The associated input key event.
*/
fun onInputKeyUp(ev: InputKeyEvent)
/**
* Event method which gets called when a key is called repeatedly while being pressed down.
*
* @param ev The associated input key event.
*/
fun onInputKeyRepeat(ev: InputKeyEvent)
/**
* Event method which gets called when a key press is cancelled.
*
* @param ev The associated input key event.
*/
fun onInputKeyCancel(ev: InputKeyEvent)
}

View File

@@ -1,108 +0,0 @@
/*
* 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:Suppress("MemberVisibilityCanBePrivate")
package dev.patrickgold.florisboard.ime.keyboard
import android.view.inputmethod.EditorInfo
import dev.patrickgold.florisboard.common.android.AndroidVersion
/**
* Class which holds the same information as an [EditorInfo.imeOptions] int but more accessible and
* readable.
*/
@JvmInline
value class ImeOptions(val state: KeyboardState) {
companion object {
const val M_IME_OPTIONS: ULong = 0x0F_FFu
const val O_IME_OPTIONS: Int = 32
const val M_ENTER_ACTION: ULong = 0x0Fu
const val O_ENTER_ACTION: Int = 32
const val F_FORCE_ASCII: ULong = 0x00_00_00_10_00_00_00_00u
const val F_NAVIGATE_NEXT: ULong = 0x00_00_00_20_00_00_00_00u
const val F_NAVIGATE_PREVIOUS: ULong = 0x00_00_00_40_00_00_00_00u
const val F_NO_ACCESSORY_ACTION: ULong = 0x00_00_00_80_00_00_00_00u
const val F_NO_ENTER_ACTION: ULong = 0x00_00_01_00_00_00_00_00u
const val F_NO_EXTRACT_UI: ULong = 0x00_00_02_00_00_00_00_00u
const val F_NO_FULLSCREEN: ULong = 0x00_00_04_00_00_00_00_00u
const val F_NO_PERSONALIZED_LEARNING: ULong = 0x00_00_08_00_00_00_00_00u
}
var enterAction: EnterAction
get() = EnterAction.fromInt(state.getRegion(M_ENTER_ACTION, O_ENTER_ACTION))
private set(v) = state.setRegion(M_ENTER_ACTION, O_ENTER_ACTION, v.toInt())
var flagForceAscii: Boolean
get() = state.getFlag(F_FORCE_ASCII)
private set(v) = state.setFlag(F_FORCE_ASCII, v)
var flagNavigateNext: Boolean
get() = state.getFlag(F_NAVIGATE_NEXT)
private set(v) = state.setFlag(F_NAVIGATE_NEXT, v)
var flagNavigatePrevious: Boolean
get() = state.getFlag(F_NAVIGATE_PREVIOUS)
private set(v) = state.setFlag(F_NAVIGATE_PREVIOUS, v)
var flagNoAccessoryAction: Boolean
get() = state.getFlag(F_NO_ACCESSORY_ACTION)
private set(v) = state.setFlag(F_NO_ACCESSORY_ACTION, v)
var flagNoEnterAction: Boolean
get() = state.getFlag(F_NO_ENTER_ACTION)
private set(v) = state.setFlag(F_NO_ENTER_ACTION, v)
var flagNoExtractUi: Boolean
get() = state.getFlag(F_NO_EXTRACT_UI)
private set(v) = state.setFlag(F_NO_EXTRACT_UI, v)
var flagNoFullscreen: Boolean
get() = state.getFlag(F_NO_FULLSCREEN)
private set(v) = state.setFlag(F_NO_FULLSCREEN, v)
var flagNoPersonalizedLearning: Boolean
get() = state.getFlag(F_NO_PERSONALIZED_LEARNING)
private set(v) = state.setFlag(F_NO_PERSONALIZED_LEARNING, v)
fun update(editorInfo: EditorInfo) {
val imeOptionsRaw = editorInfo.imeOptions
state.setRegion(M_IME_OPTIONS, O_IME_OPTIONS, 0) // reset imeOptions region
enterAction = EnterAction.fromInt(imeOptionsRaw and EditorInfo.IME_MASK_ACTION)
flagForceAscii = imeOptionsRaw and EditorInfo.IME_FLAG_FORCE_ASCII != 0
flagNavigateNext = imeOptionsRaw and EditorInfo.IME_FLAG_NAVIGATE_NEXT != 0
flagNavigatePrevious = imeOptionsRaw and EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS != 0
flagNoAccessoryAction = imeOptionsRaw and EditorInfo.IME_FLAG_NO_ACCESSORY_ACTION != 0
flagNoEnterAction = imeOptionsRaw and EditorInfo.IME_FLAG_NO_ENTER_ACTION != 0
flagNoExtractUi = imeOptionsRaw and EditorInfo.IME_FLAG_NO_EXTRACT_UI != 0
flagNoFullscreen = imeOptionsRaw and EditorInfo.IME_FLAG_NO_FULLSCREEN != 0
if (AndroidVersion.ATLEAST_API26_O) {
flagNoPersonalizedLearning = imeOptionsRaw and EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING != 0
}
}
enum class EnterAction(val value: Int) {
UNSPECIFIED(EditorInfo.IME_ACTION_UNSPECIFIED),
DONE(EditorInfo.IME_ACTION_DONE),
GO(EditorInfo.IME_ACTION_GO),
NEXT(EditorInfo.IME_ACTION_NEXT),
NONE(EditorInfo.IME_ACTION_NONE),
PREVIOUS(EditorInfo.IME_ACTION_PREVIOUS),
SEARCH(EditorInfo.IME_ACTION_SEARCH),
SEND(EditorInfo.IME_ACTION_SEND);
companion object {
fun fromInt(int: Int) = values().firstOrNull { it.value == int } ?: NONE
}
fun toInt() = value
}
}

View File

@@ -1,224 +0,0 @@
/*
* 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:Suppress("MemberVisibilityCanBePrivate")
package dev.patrickgold.florisboard.ime.keyboard
import android.text.InputType
import android.view.inputmethod.EditorInfo
/**
* Class which holds the same information as an [EditorInfo.inputType] int but more accessible and
* readable.
*/
@JvmInline
value class InputAttributes(val state: KeyboardState) {
companion object {
const val M_INPUT_ATTRIBUTES: ULong = 0x0F_FF_FFu
const val O_INPUT_ATTRIBUTES: Int = 44
const val M_TYPE: ULong = 0x07u
const val O_TYPE: Int = 44
const val M_VARIATION: ULong = 0x1Fu
const val O_VARIATION: Int = 47
const val M_CAPS_MODE: ULong = 0x03u
const val O_CAPS_MODE: Int = 52
const val F_NUMBER_DECIMAL: ULong = 0x00_40_00_00_00_00_00_00u
const val F_NUMBER_SIGNED: ULong = 0x00_80_00_00_00_00_00_00u
const val F_TEXT_AUTO_COMPLETE: ULong = 0x01_00_00_00_00_00_00_00u
const val F_TEXT_AUTO_CORRECT: ULong = 0x02_00_00_00_00_00_00_00u
const val F_TEXT_IME_MULTILINE: ULong = 0x04_00_00_00_00_00_00_00u
const val F_TEXT_MULTILINE: ULong = 0x08_00_00_00_00_00_00_00u
const val F_TEXT_NO_SUGGESTIONS: ULong = 0x10_00_00_00_00_00_00_00u
}
var type: Type
get() = Type.fromInt(state.getRegion(M_TYPE, O_TYPE))
private set(v) = state.setRegion(M_TYPE, O_TYPE, v.toInt())
var variation: Variation
get() = Variation.fromInt(state.getRegion(M_VARIATION, O_VARIATION))
private set(v) = state.setRegion(M_VARIATION, O_VARIATION, v.toInt())
var capsMode: CapsMode
get() = CapsMode.fromInt(state.getRegion(M_CAPS_MODE, O_CAPS_MODE))
private set(v) = state.setRegion(M_CAPS_MODE, O_CAPS_MODE, v.toInt())
var flagNumberDecimal: Boolean
get() = state.getFlag(F_NUMBER_DECIMAL)
private set(v) = state.setFlag(F_NUMBER_DECIMAL, v)
var flagNumberSigned: Boolean
get() = state.getFlag(F_NUMBER_SIGNED)
private set(v) = state.setFlag(F_NUMBER_SIGNED, v)
var flagTextAutoComplete: Boolean
get() = state.getFlag(F_TEXT_AUTO_COMPLETE)
private set(v) = state.setFlag(F_TEXT_AUTO_COMPLETE, v)
var flagTextAutoCorrect: Boolean
get() = state.getFlag(F_TEXT_AUTO_CORRECT)
private set(v) = state.setFlag(F_TEXT_AUTO_CORRECT, v)
var flagTextImeMultiLine: Boolean
get() = state.getFlag(F_TEXT_IME_MULTILINE)
private set(v) = state.setFlag(F_TEXT_IME_MULTILINE, v)
var flagTextMultiLine: Boolean
get() = state.getFlag(F_TEXT_MULTILINE)
private set(v) = state.setFlag(F_TEXT_MULTILINE, v)
var flagTextNoSuggestions: Boolean
get() = state.getFlag(F_TEXT_NO_SUGGESTIONS)
private set(v) = state.setFlag(F_TEXT_NO_SUGGESTIONS, v)
fun update(editorInfo: EditorInfo) {
val inputAttrsRaw = editorInfo.inputType
state.setRegion(M_INPUT_ATTRIBUTES, O_INPUT_ATTRIBUTES, 0) // reset inputAttributes region
when (inputAttrsRaw and InputType.TYPE_MASK_CLASS) {
InputType.TYPE_NULL -> {
type = Type.NULL
variation = Variation.NORMAL
capsMode = CapsMode.NONE
}
InputType.TYPE_CLASS_DATETIME -> {
type = Type.DATETIME
variation = when (inputAttrsRaw and InputType.TYPE_MASK_VARIATION) {
InputType.TYPE_DATETIME_VARIATION_DATE -> Variation.DATE
InputType.TYPE_DATETIME_VARIATION_NORMAL -> Variation.NORMAL
InputType.TYPE_DATETIME_VARIATION_TIME -> Variation.TIME
else -> Variation.NORMAL
}
capsMode = CapsMode.NONE
}
InputType.TYPE_CLASS_NUMBER -> {
type = Type.NUMBER
variation = when (inputAttrsRaw and InputType.TYPE_MASK_VARIATION) {
InputType.TYPE_NUMBER_VARIATION_NORMAL -> Variation.NORMAL
InputType.TYPE_NUMBER_VARIATION_PASSWORD -> Variation.PASSWORD
else -> Variation.NORMAL
}
capsMode = CapsMode.NONE
flagNumberDecimal = inputAttrsRaw and InputType.TYPE_NUMBER_FLAG_DECIMAL != 0
flagNumberSigned = inputAttrsRaw and InputType.TYPE_NUMBER_FLAG_SIGNED != 0
}
InputType.TYPE_CLASS_PHONE -> {
type = Type.PHONE
variation = Variation.NORMAL
capsMode = CapsMode.NONE
}
InputType.TYPE_CLASS_TEXT -> {
type = Type.TEXT
variation = when (inputAttrsRaw and InputType.TYPE_MASK_VARIATION) {
InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS -> Variation.EMAIL_ADDRESS
InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT -> Variation.EMAIL_SUBJECT
InputType.TYPE_TEXT_VARIATION_FILTER -> Variation.FILTER
InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE -> Variation.LONG_MESSAGE
InputType.TYPE_TEXT_VARIATION_NORMAL -> Variation.NORMAL
InputType.TYPE_TEXT_VARIATION_PASSWORD -> Variation.PASSWORD
InputType.TYPE_TEXT_VARIATION_PERSON_NAME -> Variation.PERSON_NAME
InputType.TYPE_TEXT_VARIATION_PHONETIC -> Variation.PHONETIC
InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS -> Variation.POSTAL_ADDRESS
InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE -> Variation.SHORT_MESSAGE
InputType.TYPE_TEXT_VARIATION_URI -> Variation.URI
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD -> Variation.VISIBLE_PASSWORD
InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT -> Variation.WEB_EDIT_TEXT
InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS -> Variation.WEB_EMAIL_ADDRESS
InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD -> Variation.WEB_PASSWORD
else -> Variation.NORMAL
}
capsMode = CapsMode.fromFlags(inputAttrsRaw)
flagTextAutoComplete = inputAttrsRaw and InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE != 0
flagTextAutoCorrect = inputAttrsRaw and InputType.TYPE_TEXT_FLAG_AUTO_CORRECT != 0
flagTextImeMultiLine = inputAttrsRaw and InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE != 0
flagTextMultiLine = inputAttrsRaw and InputType.TYPE_TEXT_FLAG_MULTI_LINE != 0
flagTextNoSuggestions = inputAttrsRaw and InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS != 0
}
else -> {
type = Type.TEXT
variation = Variation.NORMAL
capsMode = CapsMode.NONE
}
}
}
enum class Type(val value: Int) {
NULL(EditorInfo.TYPE_NULL),
DATETIME(EditorInfo.TYPE_CLASS_DATETIME),
NUMBER(EditorInfo.TYPE_CLASS_NUMBER),
PHONE(EditorInfo.TYPE_CLASS_PHONE),
TEXT(EditorInfo.TYPE_CLASS_TEXT);
companion object {
fun fromInt(int: Int) = values().firstOrNull { it.value == int } ?: NULL
}
fun toInt() = value
}
enum class Variation(val value: Int) {
NORMAL(0),
DATE(1),
EMAIL_ADDRESS(2),
EMAIL_SUBJECT(3),
FILTER(4),
LONG_MESSAGE(5),
PASSWORD(6),
PERSON_NAME(7),
PHONETIC(8),
POSTAL_ADDRESS(9),
SHORT_MESSAGE(10),
TIME(11),
URI(12),
VISIBLE_PASSWORD(13),
WEB_EDIT_TEXT(14),
WEB_EMAIL_ADDRESS(15),
WEB_PASSWORD(16);
companion object {
fun fromInt(int: Int) = values().firstOrNull { it.value == int } ?: NORMAL
}
fun toInt() = value
}
enum class CapsMode(val value: Int) {
NONE(0),
ALL(1),
SENTENCES(2),
WORDS(3);
companion object {
fun fromFlags(flags: Int): CapsMode {
return when {
flags and InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS != 0 -> ALL
flags and InputType.TYPE_TEXT_FLAG_CAP_SENTENCES != 0 -> SENTENCES
flags and InputType.TYPE_TEXT_FLAG_CAP_WORDS != 0 -> WORDS
else -> NONE
}
}
fun fromInt(int: Int) = values().firstOrNull { it.value == int } ?: NONE
}
fun toFlags(): Int {
return when (this) {
ALL -> InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS
SENTENCES -> InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
WORDS -> InputType.TYPE_TEXT_FLAG_CAP_WORDS
else -> 0
}
}
fun toInt() = value
}
}

View File

@@ -1,47 +0,0 @@
/*
* 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.nlp
import dev.patrickgold.florisboard.common.FlorisLocale
@Suppress("RegExpRedundantEscape")
object TextProcessor {
private val LATIN_BASIC_WORD_REGEX = """[_]*(([\p{L}\d\']+[_-]*[\p{L}\d\']+)|[\p{L}\d\']+)[_]*""".toRegex()
private fun wordRegexFor(locale: FlorisLocale): Regex {
return when (locale) {
else -> LATIN_BASIC_WORD_REGEX
}
}
fun detectWords(text: CharSequence, locale: FlorisLocale): Sequence<IntRange> {
val regex = wordRegexFor(locale)
return regex.findAll(text).map { it.range }
}
fun detectWords(text: CharSequence, start: Int, end: Int, locale: FlorisLocale): Sequence<IntRange> {
val regex = wordRegexFor(locale)
val tStart = start.coerceAtLeast(0)
val tEnd = end.coerceAtMost(text.length)
return regex.findAll(text.slice(tStart..tEnd)).map { it.range }
}
fun isWord(text: CharSequence, locale: FlorisLocale): Boolean {
val regex = wordRegexFor(locale)
return regex.matches(text)
}
}

View File

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

View File

@@ -22,28 +22,29 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.core.os.UserManagerCompat
import dev.patrickgold.florisboard.app.prefs.florisPreferenceModel
import dev.patrickgold.florisboard.common.NativeStr
import dev.patrickgold.florisboard.common.android.AndroidVersion
import dev.patrickgold.florisboard.common.toNativeStr
import dev.patrickgold.florisboard.crashutility.CrashUtility
import dev.patrickgold.florisboard.debug.Flog
import dev.patrickgold.florisboard.debug.LogTopic
import dev.patrickgold.florisboard.debug.flogError
import dev.patrickgold.florisboard.debug.flogInfo
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.clipboard.ClipboardManager
import dev.patrickgold.florisboard.ime.core.SubtypeManager
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
import dev.patrickgold.florisboard.ime.editor.EditorInstance
import dev.patrickgold.florisboard.ime.keyboard.KeyboardManager
import dev.patrickgold.florisboard.ime.media.emoji.FlorisEmojiCompat
import dev.patrickgold.florisboard.ime.nlp.NlpManager
import dev.patrickgold.florisboard.ime.spelling.SpellingManager
import dev.patrickgold.florisboard.ime.spelling.SpellingService
import dev.patrickgold.florisboard.ime.text.gestures.GlideTypingManager
import dev.patrickgold.florisboard.ime.theme.ThemeManager
import dev.patrickgold.florisboard.res.AssetManager
import dev.patrickgold.florisboard.res.cache.CacheManager
import dev.patrickgold.florisboard.res.ext.ExtensionManager
import dev.patrickgold.florisboard.res.io.deleteContentsRecursively
import dev.patrickgold.florisboard.lib.NativeStr
import dev.patrickgold.florisboard.lib.cache.CacheManager
import dev.patrickgold.florisboard.lib.crashutility.CrashUtility
import dev.patrickgold.florisboard.lib.devtools.Flog
import dev.patrickgold.florisboard.lib.devtools.LogTopic
import dev.patrickgold.florisboard.lib.devtools.flogError
import dev.patrickgold.florisboard.lib.devtools.flogInfo
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
import dev.patrickgold.florisboard.lib.io.AssetManager
import dev.patrickgold.florisboard.lib.io.deleteContentsRecursively
import dev.patrickgold.florisboard.lib.toNativeStr
import dev.patrickgold.jetpref.datastore.JetPref
import java.io.File
@@ -67,6 +68,7 @@ class FlorisApplication : Application() {
val assetManager = lazy { AssetManager(this) }
val cacheManager = lazy { CacheManager(this) }
val clipboardManager = lazy { ClipboardManager(this) }
val editorInstance = lazy { EditorInstance(this) }
val extensionManager = lazy { ExtensionManager(this) }
val glideTypingManager = lazy { GlideTypingManager(this) }
val keyboardManager = lazy { KeyboardManager(this) }
@@ -88,8 +90,9 @@ class FlorisApplication : Application() {
flogOutputs = Flog.OUTPUT_CONSOLE,
)
CrashUtility.install(this)
FlorisEmojiCompat.init(this)
if (AndroidVersion.ATLEAST_API24_N && !UserManagerCompat.isUserUnlocked(this)) {
if (!UserManagerCompat.isUserUnlocked(this)) {
val context = createDeviceProtectedStorageContext()
initICU(context)
prefs.initializeBlocking(context)
@@ -162,6 +165,8 @@ fun Context.cacheManager() = lazy { this.florisApplication().cacheManager.value
fun Context.clipboardManager() = lazy { this.florisApplication().clipboardManager.value }
fun Context.editorInstance() = lazy { this.florisApplication().editorInstance.value }
fun Context.extensionManager() = lazy { this.florisApplication().extensionManager.value }
fun Context.glideTypingManager() = lazy { this.florisApplication().glideTypingManager.value }

View File

@@ -19,70 +19,68 @@ package dev.patrickgold.florisboard
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.inputmethodservice.ExtractEditText
import android.os.Build
import android.os.Bundle
import android.util.Size
import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.ExtractedText
import android.view.inputmethod.InlineSuggestionsRequest
import android.view.inputmethod.InlineSuggestionsResponse
import android.view.inputmethod.InputConnection
import android.view.inputmethod.InputMethodManager
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.inline.InlinePresentationSpec
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material.ButtonDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.input.pointer.pointerInteropFilter
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.AbstractComposeView
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.WindowCompat
import dev.patrickgold.florisboard.app.FlorisAppActivity
import dev.patrickgold.florisboard.app.prefs.florisPreferenceModel
import dev.patrickgold.florisboard.app.res.ProvideLocalizedResources
import dev.patrickgold.florisboard.app.ui.components.SystemUiIme
import dev.patrickgold.florisboard.app.ui.devtools.DevtoolsOverlay
import dev.patrickgold.florisboard.common.ViewUtils
import dev.patrickgold.florisboard.common.android.AndroidVersion
import dev.patrickgold.florisboard.common.android.isOrientationLandscape
import dev.patrickgold.florisboard.common.android.isOrientationPortrait
import dev.patrickgold.florisboard.common.android.launchActivity
import dev.patrickgold.florisboard.common.android.setLocale
import dev.patrickgold.florisboard.common.android.systemServiceOrNull
import dev.patrickgold.florisboard.common.observeAsTransformingState
import dev.patrickgold.florisboard.debug.LogTopic
import dev.patrickgold.florisboard.debug.flogError
import dev.patrickgold.florisboard.debug.flogInfo
import dev.patrickgold.florisboard.debug.flogWarning
import dev.patrickgold.florisboard.app.devtools.DevtoolsOverlay
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.ImeUiMode
import dev.patrickgold.florisboard.ime.clipboard.ClipboardInputLayout
import dev.patrickgold.florisboard.ime.core.EditorInstance
import dev.patrickgold.florisboard.ime.editor.EditorRange
import dev.patrickgold.florisboard.ime.editor.FlorisEditorInfo
import dev.patrickgold.florisboard.ime.input.InputFeedbackController
import dev.patrickgold.florisboard.ime.input.LocalInputFeedbackController
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
import dev.patrickgold.florisboard.ime.keyboard.InputFeedbackController
import dev.patrickgold.florisboard.ime.keyboard.LocalInputFeedbackController
import dev.patrickgold.florisboard.ime.keyboard.ProvideKeyboardRowBaseHeight
import dev.patrickgold.florisboard.ime.landscapeinput.LandscapeInputUiMode
import dev.patrickgold.florisboard.ime.lifecycle.LifecycleInputMethodService
import dev.patrickgold.florisboard.ime.media.MediaInputLayout
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
@@ -91,7 +89,30 @@ import dev.patrickgold.florisboard.ime.text.TextInputLayout
import dev.patrickgold.florisboard.ime.text.smartbar.SecondaryRowPlacement
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.snygg.ui.SnyggSurface
import dev.patrickgold.florisboard.lib.android.AndroidInternalR
import dev.patrickgold.florisboard.lib.android.AndroidVersion
import dev.patrickgold.florisboard.lib.android.isOrientationLandscape
import dev.patrickgold.florisboard.lib.android.isOrientationPortrait
import dev.patrickgold.florisboard.lib.android.launchActivity
import dev.patrickgold.florisboard.lib.android.setLocale
import dev.patrickgold.florisboard.lib.android.systemServiceOrNull
import dev.patrickgold.florisboard.lib.compose.FlorisButton
import dev.patrickgold.florisboard.lib.compose.ProvideLocalizedResources
import dev.patrickgold.florisboard.lib.compose.SystemUiIme
import dev.patrickgold.florisboard.lib.devtools.LogTopic
import dev.patrickgold.florisboard.lib.devtools.flogError
import dev.patrickgold.florisboard.lib.devtools.flogInfo
import dev.patrickgold.florisboard.lib.devtools.flogWarning
import dev.patrickgold.florisboard.lib.observeAsTransformingState
import dev.patrickgold.florisboard.lib.snygg.ui.SnyggSurface
import dev.patrickgold.florisboard.lib.snygg.ui.shape
import dev.patrickgold.florisboard.lib.snygg.ui.snyggBackground
import dev.patrickgold.florisboard.lib.snygg.ui.snyggBorder
import dev.patrickgold.florisboard.lib.snygg.ui.snyggShadow
import dev.patrickgold.florisboard.lib.snygg.ui.solidColor
import dev.patrickgold.florisboard.lib.snygg.ui.spSize
import dev.patrickgold.florisboard.lib.util.ViewUtils
import dev.patrickgold.florisboard.lib.util.debugSummarize
import dev.patrickgold.jetpref.datastore.model.observeAsState
import java.lang.ref.WeakReference
@@ -107,18 +128,14 @@ private var FlorisImeServiceReference = WeakReference<FlorisImeService?>(null)
/**
* Core class responsible for linking together all managers and UI compose-ables to provide an IME service. Sets
* up the window and context to be lifecycle-aware, so LiveData and Jetpack Compose can be used without issues.
*
* This is a new implementation for the keyboard service class and is replacing the old core class bit by bit.
* The main objective for the new class is to hold as few state as possible and delegate tasks to context-bound
* manager classes.
*/
class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHistoryChangedListener {
class FlorisImeService : LifecycleInputMethodService() {
companion object {
private val InlineSuggestionUiSmallestSize = Size(0, 0)
private val InlineSuggestionUiBiggestSize = Size(Int.MAX_VALUE, Int.MAX_VALUE)
fun activeEditorInstance(): EditorInstance? {
return FlorisImeServiceReference.get()?.activeEditorInstance
fun currentInputConnection(): InputConnection? {
return FlorisImeServiceReference.get()?.currentInputConnection
}
fun inputFeedbackController(): InputFeedbackController? {
@@ -201,17 +218,19 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
}
private val prefs by florisPreferenceModel()
private val editorInstance by editorInstance()
private val keyboardManager by keyboardManager()
private val nlpManager by nlpManager()
private val subtypeManager by subtypeManager()
private val themeManager by themeManager()
private val activeEditorInstance by lazy { EditorInstance(this) }
private val activeState get() = keyboardManager.activeState
private var inputWindowView by mutableStateOf<View?>(null)
private var inputViewSize by mutableStateOf(IntSize.Zero)
private val inputFeedbackController by lazy { InputFeedbackController.new(this) }
private var isWindowShown: Boolean = false
private var isFullscreenUiMode by mutableStateOf(false)
private var isExtractUiShown by mutableStateOf(false)
private var resourcesContext by mutableStateOf(this as Context)
init {
@@ -221,7 +240,6 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
override fun onCreate() {
super.onCreate()
FlorisImeServiceReference = WeakReference(this)
activeEditorInstance.wordHistoryChangedListener = this
subtypeManager.activeSubtype.observe(this) { subtype ->
val config = Configuration(resources.configuration)
config.setLocale(subtype.primaryLocale)
@@ -230,7 +248,7 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
}
override fun onCreateInputView(): View {
super.onCreateInputView()
super.installViewTreeOwners()
val composeView = ComposeInputView()
inputWindowView = composeView
return composeView
@@ -241,32 +259,47 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
return null
}
override fun onCreateExtractTextView(): View {
super.installViewTreeOwners()
// Consider adding a fallback to the default extract edit layout if user reports come
// that this causes a crash, especially if the device manufacturer of the user device
// is a known one to break AOSP standards...
val defaultExtractView = super.onCreateExtractTextView()
if (defaultExtractView == null || defaultExtractView !is ViewGroup) {
return ComposeExtractedLandscapeInputView(null)
}
val extractEditText = defaultExtractView.findViewById<ExtractEditText>(android.R.id.inputExtractEditText)
(extractEditText?.parent as? ViewGroup)?.removeView(extractEditText)
defaultExtractView.apply {
removeAllViews()
addView(ComposeExtractedLandscapeInputView(extractEditText))
}
return defaultExtractView
}
override fun onDestroy() {
super.onDestroy()
FlorisImeServiceReference = WeakReference(null)
activeEditorInstance.wordHistoryChangedListener = null
inputWindowView = null
}
override fun onBindInput() {
super.onBindInput()
activeEditorInstance.bindInput()
}
override fun onStartInput(attribute: EditorInfo?, restarting: Boolean) {
super.onStartInput(attribute, restarting)
if (attribute == null) return
activeEditorInstance.startInput(attribute)
override fun onStartInput(info: EditorInfo?, restarting: Boolean) {
flogInfo { "restarting=$restarting info=${info?.debugSummarize()}" }
super.onStartInput(info, restarting)
if (info == null) return
val editorInfo = FlorisEditorInfo.wrap(info)
editorInstance.handleStartInput(editorInfo)
}
override fun onStartInputView(info: EditorInfo?, restarting: Boolean) {
flogInfo { "restarting=$restarting info=${info?.debugSummarize()}" }
super.onStartInputView(info, restarting)
if (info == null) return
val editorInfo = FlorisEditorInfo.wrap(info)
activeState.batchEdit {
activeState.update(info)
activeState.imeUiMode = ImeUiMode.TEXT
activeState.isSelectionMode = (info.initialSelEnd - info.initialSelStart) != 0
activeEditorInstance.startInputView(info)
activeState.isSelectionMode = editorInfo.initialSelection.isSelectionMode
editorInstance.handleStartInputView(editorInfo)
keyboardManager.updateCapsState()
}
}
@@ -277,51 +310,34 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
newSelStart: Int,
newSelEnd: Int,
candidatesStart: Int,
candidatesEnd: Int
candidatesEnd: Int,
) {
flogInfo { "old={start=$oldSelStart,end=$oldSelEnd} new={start=$newSelStart,end=$newSelEnd} composing={start=$candidatesStart,end=$candidatesEnd}" }
super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, candidatesStart, candidatesEnd)
activeState.batchEdit {
activeState.isSelectionMode = (newSelEnd - newSelStart) != 0
activeEditorInstance.updateSelection(
oldSelStart, oldSelEnd,
newSelStart, newSelEnd,
candidatesStart, candidatesEnd,
editorInstance.handleSelectionUpdate(
oldSelection = EditorRange.normalized(oldSelStart, oldSelEnd),
newSelection = EditorRange.normalized(newSelStart, newSelEnd),
composing = EditorRange.normalized(candidatesStart, candidatesEnd),
)
keyboardManager.updateCapsState()
}
}
override fun onFinishInputView(finishingInput: Boolean) {
flogInfo { "finishing=$finishingInput" }
super.onFinishInputView(finishingInput)
// TODO: evaluate which parts to reset. Resetting everything is too much,
// resetting nothing could be problematic too.
//activeState.reset()
activeEditorInstance.finishInputView()
editorInstance.handleFinishInputView()
}
override fun onFinishInput() {
flogInfo { "(no args)" }
super.onFinishInput()
activeEditorInstance.finishInput()
editorInstance.handleFinishInput()
nlpManager.clearInlineSuggestions()
}
override fun onUnbindInput() {
super.onUnbindInput()
activeEditorInstance.unbindInput()
}
override fun onWordHistoryChanged(
currentWord: EditorInstance.Region?,
wordsBeforeCurrent: List<EditorInstance.Region>,
wordsAfterCurrent: List<EditorInstance.Region>,
) {
if (currentWord == null || !currentWord.isValid || !activeState.isComposingEnabled) {
nlpManager.clearSuggestions()
return
}
nlpManager.suggest(currentWord.text, listOf())
}
override fun onWindowShown() {
super.onWindowShown()
if (isWindowShown) {
@@ -345,14 +361,34 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
isWindowShown = false
}
override fun onEvaluateFullscreenMode(): Boolean {
return when (prefs.keyboard.landscapeInputUiMode.get()) {
LandscapeInputUiMode.DYNAMICALLY_SHOW -> super.onEvaluateFullscreenMode()
LandscapeInputUiMode.NEVER_SHOW -> false
LandscapeInputUiMode.ALWAYS_SHOW -> true
}
}
override fun updateFullscreenMode() {
super.updateFullscreenMode()
isFullscreenUiMode = isFullscreenMode
updateSoftInputWindowLayoutParameters()
}
override fun onUpdateExtractedText(token: Int, text: ExtractedText?) {
super.onUpdateExtractedText(token, text)
activeEditorInstance.updateText(token, text)
override fun onUpdateExtractingVisibility(info: EditorInfo?) {
if (info != null) {
editorInstance.handleStartInputView(FlorisEditorInfo.wrap(info))
}
when (prefs.keyboard.landscapeInputUiMode.get()) {
LandscapeInputUiMode.DYNAMICALLY_SHOW -> super.onUpdateExtractingVisibility(info)
LandscapeInputUiMode.NEVER_SHOW -> isExtractViewShown = false
LandscapeInputUiMode.ALWAYS_SHOW -> isExtractViewShown = true
}
}
override fun setExtractViewShown(shown: Boolean) {
super.setExtractViewShown(shown)
isExtractUiShown = shown
}
@RequiresApi(Build.VERSION_CODES.R)
@@ -424,7 +460,7 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
val w = window?.window ?: return
WindowCompat.setDecorFitsSystemWindows(w, true)
ViewUtils.updateLayoutHeightOf(w, WindowManager.LayoutParams.MATCH_PARENT)
val layoutHeight = if (isFullscreenMode) {
val layoutHeight = if (isFullscreenUiMode) {
WindowManager.LayoutParams.WRAP_CONTENT
} else {
WindowManager.LayoutParams.MATCH_PARENT
@@ -436,15 +472,39 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
ViewUtils.updateLayoutHeightOf(inputWindowView, layoutHeight)
}
override fun getTextForImeAction(imeOptions: Int): String? {
return try {
when (imeOptions and EditorInfo.IME_MASK_ACTION) {
EditorInfo.IME_ACTION_NONE -> null
EditorInfo.IME_ACTION_GO -> resourcesContext.getString(AndroidInternalR.string.ime_action_go)
EditorInfo.IME_ACTION_SEARCH -> resourcesContext.getString(AndroidInternalR.string.ime_action_search)
EditorInfo.IME_ACTION_SEND -> resourcesContext.getString(AndroidInternalR.string.ime_action_send)
EditorInfo.IME_ACTION_NEXT -> resourcesContext.getString(AndroidInternalR.string.ime_action_next)
EditorInfo.IME_ACTION_DONE -> resourcesContext.getString(AndroidInternalR.string.ime_action_done)
EditorInfo.IME_ACTION_PREVIOUS -> resourcesContext.getString(AndroidInternalR.string.ime_action_previous)
else -> resourcesContext.getString(AndroidInternalR.string.ime_action_default)
}
} catch (_: Throwable) {
super.getTextForImeAction(imeOptions)?.toString()
}
}
@Composable
private fun ImeUiWrapper() {
ProvideLocalizedResources(resourcesContext) {
ProvideKeyboardRowBaseHeight {
CompositionLocalProvider(LocalInputFeedbackController provides inputFeedbackController) {
FlorisImeTheme {
// Outer box is necessary as an "outer window"
Box(modifier = Modifier.fillMaxSize()) {
DevtoolsUi()
Column(modifier = Modifier.fillMaxWidth()) {
if (!(isFullscreenUiMode && isExtractUiShown)) {
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
) {
DevtoolsUi()
}
}
ImeUi()
}
SystemUiIme()
@@ -456,11 +516,11 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun BoxScope.ImeUi() {
private fun ImeUi() {
val activeState by keyboardManager.observeActiveState()
val keyboardStyle = FlorisImeTheme.style.get(
element = FlorisImeUi.Keyboard,
mode = activeState.inputMode.value,
mode = activeState.inputShiftState.value,
)
val layoutDirection = LocalLayoutDirection.current
SideEffect {
@@ -471,7 +531,6 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.align(Alignment.BottomStart)
.onGloballyPositioned { coords -> inputViewSize = coords.size }
// Do not remove below line or touch input may get stuck
.pointerInteropFilter { false },
@@ -525,23 +584,13 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
}
}
}
}
@Composable
private fun BoxScope.DevtoolsUi() = with(LocalDensity.current) {
private fun DevtoolsUi() {
val devtoolsEnabled by prefs.devtools.enabled.observeAsState()
if (devtoolsEnabled) {
val maxHeight = inputWindowView?.measuredHeight?.let { windowHeight ->
windowHeight - inputViewSize.height - FlorisImeSizing.Static.smartbarHeightPx
} ?: inputViewSize.height
DevtoolsOverlay(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.heightIn(max = maxHeight.toDp())
.align(Alignment.TopStart),
)
DevtoolsOverlay(modifier = Modifier.fillMaxSize())
}
}
@@ -565,4 +614,108 @@ class FlorisImeService : LifecycleInputMethodService(), EditorInstance.WordHisto
updateSoftInputWindowLayoutParameters()
}
}
private inner class ComposeExtractedLandscapeInputView(eet: ExtractEditText?) : FrameLayout(this) {
val composeView: ComposeView
val extractEditText: ExtractEditText
init {
isHapticFeedbackEnabled = true
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
extractEditText = (eet ?: ExtractEditText(context)).also {
it.id = android.R.id.inputExtractEditText
it.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
it.background = null
it.gravity = Gravity.TOP
it.isVerticalScrollBarEnabled = true
}
addView(extractEditText)
composeView = ComposeView(context).also { it.setContent { Content() } }
addView(composeView)
}
@Composable
fun Content() {
ProvideLocalizedResources(resourcesContext, forceLayoutDirection = LayoutDirection.Ltr) {
FlorisImeTheme {
val layoutStyle = FlorisImeTheme.style.get(FlorisImeUi.ExtractedLandscapeInputLayout)
val fieldStyle = FlorisImeTheme.style.get(FlorisImeUi.ExtractedLandscapeInputField)
val actionStyle = FlorisImeTheme.style.get(FlorisImeUi.ExtractedLandscapeInputAction)
val activeEditorInfo by editorInstance.activeInfoFlow.collectAsState()
Box(
modifier = Modifier
.snyggBackground(layoutStyle, FlorisImeTheme.fallbackSurfaceColor()),
) {
Row(
modifier = Modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically,
) {
val fieldColor = fieldStyle.foreground.solidColor(FlorisImeTheme.fallbackContentColor())
AndroidView(
modifier = Modifier
.padding(8.dp)
.fillMaxHeight()
.weight(1f)
.snyggShadow(fieldStyle)
.snyggBorder(fieldStyle)
.snyggBackground(fieldStyle),
factory = { extractEditText },
update = { view ->
view.background = null
view.backgroundTintList = null
view.foregroundTintList = null
view.setTextColor(fieldColor.toArgb())
view.setHintTextColor(fieldColor.copy(fieldColor.alpha * 0.6f).toArgb())
view.setTextSize(
TypedValue.COMPLEX_UNIT_SP,
fieldStyle.fontSize.spSize(default = 16.sp).value,
)
},
)
FlorisButton(
onClick = {
if (activeEditorInfo.extractedActionId != 0) {
currentInputConnection?.performEditorAction(activeEditorInfo.extractedActionId)
} else {
editorInstance.performEnterAction(activeEditorInfo.imeOptions.action)
}
},
modifier = Modifier.padding(horizontal = 8.dp),
text = activeEditorInfo.extractedActionLabel
?: getTextForImeAction(activeEditorInfo.imeOptions.action.toInt())
?: "ACTION",
shape = actionStyle.shape.shape(),
colors = ButtonDefaults.buttonColors(
backgroundColor = actionStyle.background.solidColor(FlorisImeTheme.fallbackContentColor()),
contentColor = actionStyle.foreground.solidColor(FlorisImeTheme.fallbackSurfaceColor()),
),
)
}
}
}
}
}
override fun getAccessibilityClassName(): CharSequence {
return javaClass.name
}
override fun onAttachedToWindow() {
removeView(extractEditText)
super.onAttachedToWindow()
try {
(parent as LinearLayout).let { extractEditLayout ->
extractEditLayout.layoutParams = LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT,
).also { it.setMargins(0, 0, 0, 0) }
extractEditLayout.setPadding(0, 0, 0, 0)
}
} catch (e: Throwable) {
flogError { e.message.toString() }
}
}
}
}

View File

@@ -20,13 +20,13 @@ import android.service.textservice.SpellCheckerService
import android.view.textservice.SentenceSuggestionsInfo
import android.view.textservice.SuggestionsInfo
import android.view.textservice.TextInfo
import dev.patrickgold.florisboard.app.prefs.florisPreferenceModel
import dev.patrickgold.florisboard.common.FlorisLocale
import dev.patrickgold.florisboard.debug.LogTopic
import dev.patrickgold.florisboard.debug.flogInfo
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
import dev.patrickgold.florisboard.ime.spelling.SpellingLanguageMode
import dev.patrickgold.florisboard.ime.spelling.SpellingService
import dev.patrickgold.florisboard.lib.FlorisLocale
import dev.patrickgold.florisboard.lib.devtools.LogTopic
import dev.patrickgold.florisboard.lib.devtools.flogInfo
import kotlinx.coroutines.runBlocking
class FlorisSpellCheckerService : SpellCheckerService() {

View File

@@ -14,11 +14,13 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.prefs
package dev.patrickgold.florisboard.app
import dev.patrickgold.florisboard.app.AppTheme
import dev.patrickgold.florisboard.app.ui.settings.theme.DisplayColorsAs
import dev.patrickgold.florisboard.app.ui.settings.theme.DisplayKbdAfterDialogs
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalConfiguration
import dev.patrickgold.florisboard.app.settings.theme.DisplayColorsAs
import dev.patrickgold.florisboard.app.settings.theme.DisplayKbdAfterDialogs
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
import dev.patrickgold.florisboard.ime.core.Subtype
import dev.patrickgold.florisboard.ime.landscapeinput.LandscapeInputUiMode
@@ -36,11 +38,14 @@ import dev.patrickgold.florisboard.ime.text.smartbar.SecondaryRowPlacement
import dev.patrickgold.florisboard.ime.text.smartbar.SmartbarRowType
import dev.patrickgold.florisboard.ime.theme.ThemeMode
import dev.patrickgold.florisboard.ime.theme.extCoreTheme
import dev.patrickgold.florisboard.res.ext.ExtensionComponentName
import dev.patrickgold.florisboard.snygg.SnyggLevel
import dev.patrickgold.florisboard.util.VersionName
import dev.patrickgold.florisboard.lib.android.isOrientationPortrait
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
import dev.patrickgold.florisboard.lib.observeAsTransformingState
import dev.patrickgold.florisboard.lib.snygg.SnyggLevel
import dev.patrickgold.florisboard.lib.util.VersionName
import dev.patrickgold.jetpref.datastore.JetPref
import dev.patrickgold.jetpref.datastore.model.PreferenceModel
import dev.patrickgold.jetpref.datastore.model.observeAsState
fun florisPreferenceModel() = JetPref.getOrCreatePreferenceModel(AppPrefs::class, ::AppPrefs)
@@ -135,12 +140,16 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
key = "devtools__show_primary_clip",
default = false,
)
val showInputStateOverlay = boolean(
key = "devtools__show_input_state_overlay",
default = false,
)
val showSpellingOverlay = boolean(
key = "devtools__show_spelling_overlay",
default = false,
)
val overrideWordSuggestionsMinHeapRestriction = boolean(
key = "devtools__override_word_suggestions_min_heap_restriction",
val showKeyTouchBoundaries = boolean(
key = "devtools__show_touch_boundaries",
default = false,
)
}
@@ -177,7 +186,7 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
)
val spaceBarSwipeUp = enum(
key = "gestures__space_bar_swipe_up",
default = SwipeAction.SWITCH_TO_CLIPBOARD_CONTEXT,
default = SwipeAction.NO_ACTION,
)
val spaceBarSwipeLeft = enum(
key = "gestures__space_bar_swipe_left",
@@ -195,9 +204,13 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
key = "gestures__delete_key_swipe_left",
default = SwipeAction.DELETE_CHARACTERS_PRECISELY,
)
val deleteKeyLongPress = enum(
key = "gestures__delete_key_long_press",
default = SwipeAction.DELETE_CHARACTER,
)
val swipeDistanceThreshold = int(
key = "gestures__swipe_distance_threshold",
default = 36,
default = 32,
)
val swipeVelocityThreshold = int(
key = "gestures__swipe_velocity_threshold",
@@ -227,6 +240,10 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
key = "glide__preview_refresh_delay",
default = 150,
)
val immediateBackspaceDeletesWord = boolean(
key = "glide__immediate_backspace_deletes_word",
default = true,
)
}
val inputFeedback = InputFeedback()
@@ -309,7 +326,7 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
val internal = Internal()
inner class Internal {
val homeIsBetaToolboxCollapsed = boolean(
key = "internal__home_is_beta_toolbox_collapsed_0314release",
key = "internal__home_is_beta_toolbox_collapsed_0316beta01",
default = false,
)
val isImeSetUp = boolean(
@@ -438,6 +455,24 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
mergeHintPopups = mergeHintPopupsEnabled.get(),
)
}
@Composable
fun fontSizeMultiplier(): Float {
val configuration = LocalConfiguration.current
val oneHandedMode by oneHandedMode.observeAsState()
val oneHandedModeFactor by oneHandedModeScaleFactor.observeAsTransformingState { it / 100.0f }
val fontSizeMultiplierBase by if (configuration.isOrientationPortrait()) {
fontSizeMultiplierPortrait
} else {
fontSizeMultiplierLandscape
}.observeAsTransformingState { it / 100.0f }
val fontSizeMultiplier = fontSizeMultiplierBase * if (oneHandedMode != OneHandedMode.OFF && configuration.isOrientationPortrait()) {
oneHandedModeFactor
} else {
1.0f
}
return fontSizeMultiplier
}
}
val localization = Localization()

View File

@@ -41,21 +41,19 @@ import com.google.accompanist.insets.ProvideWindowInsets
import com.google.accompanist.insets.navigationBarsWithImePadding
import com.google.accompanist.insets.statusBarsPadding
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.prefs.florisPreferenceModel
import dev.patrickgold.florisboard.app.res.ProvideLocalizedResources
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.Routes
import dev.patrickgold.florisboard.app.ui.components.LocalPreviewFieldController
import dev.patrickgold.florisboard.app.ui.components.PreviewKeyboardField
import dev.patrickgold.florisboard.app.ui.components.SystemUiApp
import dev.patrickgold.florisboard.app.ui.components.rememberPreviewFieldController
import dev.patrickgold.florisboard.app.ui.theme.FlorisAppTheme
import dev.patrickgold.florisboard.common.FlorisLocale
import dev.patrickgold.florisboard.common.android.AndroidVersion
import dev.patrickgold.florisboard.common.android.hideAppIcon
import dev.patrickgold.florisboard.common.android.setLocale
import dev.patrickgold.florisboard.common.android.showAppIcon
import dev.patrickgold.florisboard.util.AppVersionUtils
import dev.patrickgold.florisboard.app.apptheme.FlorisAppTheme
import dev.patrickgold.florisboard.lib.FlorisLocale
import dev.patrickgold.florisboard.lib.android.AndroidVersion
import dev.patrickgold.florisboard.lib.android.hideAppIcon
import dev.patrickgold.florisboard.lib.android.setLocale
import dev.patrickgold.florisboard.lib.android.showAppIcon
import dev.patrickgold.florisboard.lib.compose.LocalPreviewFieldController
import dev.patrickgold.florisboard.lib.compose.PreviewKeyboardField
import dev.patrickgold.florisboard.lib.compose.ProvideLocalizedResources
import dev.patrickgold.florisboard.lib.compose.SystemUiApp
import dev.patrickgold.florisboard.lib.compose.rememberPreviewFieldController
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.util.AppVersionUtils
import dev.patrickgold.jetpref.datastore.ui.ProvideDefaultDialogPrefStrings
enum class AppTheme(val id: String) {

View File

@@ -14,51 +14,51 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui
package dev.patrickgold.florisboard.app
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import dev.patrickgold.florisboard.app.ui.devtools.AndroidLocalesScreen
import dev.patrickgold.florisboard.app.ui.devtools.AndroidSettingsScreen
import dev.patrickgold.florisboard.app.ui.devtools.DevtoolsScreen
import dev.patrickgold.florisboard.app.ui.ext.ExtensionEditScreen
import dev.patrickgold.florisboard.app.ui.ext.ExtensionExportScreen
import dev.patrickgold.florisboard.app.ui.ext.ExtensionImportScreen
import dev.patrickgold.florisboard.app.ui.ext.ExtensionImportScreenType
import dev.patrickgold.florisboard.app.ui.ext.ExtensionViewScreen
import dev.patrickgold.florisboard.app.ui.settings.HomeScreen
import dev.patrickgold.florisboard.app.ui.settings.about.AboutScreen
import dev.patrickgold.florisboard.app.ui.settings.about.ProjectLicenseScreen
import dev.patrickgold.florisboard.app.ui.settings.about.ThirdPartyLicensesScreen
import dev.patrickgold.florisboard.app.ui.settings.advanced.AdvancedScreen
import dev.patrickgold.florisboard.app.ui.settings.advanced.BackupScreen
import dev.patrickgold.florisboard.app.ui.settings.advanced.RestoreScreen
import dev.patrickgold.florisboard.app.ui.settings.clipboard.ClipboardScreen
import dev.patrickgold.florisboard.app.ui.settings.dictionary.DictionaryScreen
import dev.patrickgold.florisboard.app.ui.settings.dictionary.UserDictionaryScreen
import dev.patrickgold.florisboard.app.ui.settings.dictionary.UserDictionaryType
import dev.patrickgold.florisboard.app.ui.settings.gestures.GesturesScreen
import dev.patrickgold.florisboard.app.ui.settings.keyboard.InputFeedbackScreen
import dev.patrickgold.florisboard.app.ui.settings.keyboard.KeyboardScreen
import dev.patrickgold.florisboard.app.ui.settings.localization.LocalizationScreen
import dev.patrickgold.florisboard.app.ui.settings.localization.SelectLocaleScreen
import dev.patrickgold.florisboard.app.ui.settings.localization.SubtypeEditorScreen
import dev.patrickgold.florisboard.app.ui.settings.media.MediaScreen
import dev.patrickgold.florisboard.app.ui.settings.smartbar.SmartbarScreen
import dev.patrickgold.florisboard.app.ui.settings.spelling.ImportSpellingArchiveScreen
import dev.patrickgold.florisboard.app.ui.settings.spelling.ManageSpellingDictsScreen
import dev.patrickgold.florisboard.app.ui.settings.spelling.SpellingInfoScreen
import dev.patrickgold.florisboard.app.ui.settings.spelling.SpellingScreen
import dev.patrickgold.florisboard.app.ui.settings.theme.ThemeScreen
import dev.patrickgold.florisboard.app.ui.settings.theme.ThemeManagerScreen
import dev.patrickgold.florisboard.app.ui.settings.theme.ThemeManagerScreenAction
import dev.patrickgold.florisboard.app.ui.settings.typing.TypingScreen
import dev.patrickgold.florisboard.app.ui.setup.SetupScreen
import dev.patrickgold.florisboard.app.ui.splash.SplashScreen
import dev.patrickgold.florisboard.common.kotlin.curlyFormat
import dev.patrickgold.florisboard.app.devtools.AndroidLocalesScreen
import dev.patrickgold.florisboard.app.devtools.AndroidSettingsScreen
import dev.patrickgold.florisboard.app.devtools.DevtoolsScreen
import dev.patrickgold.florisboard.app.ext.ExtensionEditScreen
import dev.patrickgold.florisboard.app.ext.ExtensionExportScreen
import dev.patrickgold.florisboard.app.ext.ExtensionImportScreen
import dev.patrickgold.florisboard.app.ext.ExtensionImportScreenType
import dev.patrickgold.florisboard.app.ext.ExtensionViewScreen
import dev.patrickgold.florisboard.app.settings.HomeScreen
import dev.patrickgold.florisboard.app.settings.about.AboutScreen
import dev.patrickgold.florisboard.app.settings.about.ProjectLicenseScreen
import dev.patrickgold.florisboard.app.settings.about.ThirdPartyLicensesScreen
import dev.patrickgold.florisboard.app.settings.advanced.AdvancedScreen
import dev.patrickgold.florisboard.app.settings.advanced.BackupScreen
import dev.patrickgold.florisboard.app.settings.advanced.RestoreScreen
import dev.patrickgold.florisboard.app.settings.clipboard.ClipboardScreen
import dev.patrickgold.florisboard.app.settings.dictionary.DictionaryScreen
import dev.patrickgold.florisboard.app.settings.dictionary.UserDictionaryScreen
import dev.patrickgold.florisboard.app.settings.dictionary.UserDictionaryType
import dev.patrickgold.florisboard.app.settings.gestures.GesturesScreen
import dev.patrickgold.florisboard.app.settings.keyboard.InputFeedbackScreen
import dev.patrickgold.florisboard.app.settings.keyboard.KeyboardScreen
import dev.patrickgold.florisboard.app.settings.localization.LocalizationScreen
import dev.patrickgold.florisboard.app.settings.localization.SelectLocaleScreen
import dev.patrickgold.florisboard.app.settings.localization.SubtypeEditorScreen
import dev.patrickgold.florisboard.app.settings.media.MediaScreen
import dev.patrickgold.florisboard.app.settings.smartbar.SmartbarScreen
import dev.patrickgold.florisboard.app.settings.spelling.ImportSpellingArchiveScreen
import dev.patrickgold.florisboard.app.settings.spelling.ManageSpellingDictsScreen
import dev.patrickgold.florisboard.app.settings.spelling.SpellingInfoScreen
import dev.patrickgold.florisboard.app.settings.spelling.SpellingScreen
import dev.patrickgold.florisboard.app.settings.theme.ThemeManagerScreen
import dev.patrickgold.florisboard.app.settings.theme.ThemeManagerScreenAction
import dev.patrickgold.florisboard.app.settings.theme.ThemeScreen
import dev.patrickgold.florisboard.app.settings.typing.TypingScreen
import dev.patrickgold.florisboard.app.setup.SetupScreen
import dev.patrickgold.florisboard.app.splash.SplashScreen
import dev.patrickgold.florisboard.lib.kotlin.curlyFormat
@Suppress("FunctionName")
object Routes {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.theme
package dev.patrickgold.florisboard.app.apptheme
import androidx.compose.ui.graphics.Color

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.theme
package dev.patrickgold.florisboard.app.apptheme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Shapes

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.theme
package dev.patrickgold.florisboard.app.apptheme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.ButtonDefaults

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.theme
package dev.patrickgold.florisboard.app.apptheme
import androidx.compose.material.Typography
import androidx.compose.ui.text.TextStyle

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.devtools
package dev.patrickgold.florisboard.app.devtools
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
@@ -23,12 +23,15 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontFamily
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.model.observeAsState
import java.util.*
@Composable
@@ -39,17 +42,22 @@ fun AndroidLocalesScreen() = FlorisScreen {
val availableLocales = remember { Locale.getAvailableLocales().sortedBy { it.toLanguageTag() } }
content {
val displayLanguageNamesIn by prefs.localization.displayLanguageNamesIn.observeAsState()
SelectionContainer(modifier = Modifier.fillMaxWidth()) {
LazyColumn {
items(availableLocales) {
items(availableLocales) { locale ->
Row {
Text(
text = it.toLanguageTag().padEnd(12),
text = locale.toLanguageTag().padEnd(12),
fontFamily = FontFamily.Monospace,
)
Text(
modifier = Modifier.weight(1.0f),
text = it.getDisplayName(it),
text = when (displayLanguageNamesIn) {
DisplayLanguageNamesIn.SYSTEM_LOCALE -> locale.displayName
DisplayLanguageNamesIn.NATIVE_LOCALE -> locale.getDisplayName(locale)
},
fontFamily = FontFamily.Monospace,
)
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.devtools
package dev.patrickgold.florisboard.app.devtools
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
@@ -27,9 +27,9 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.common.android.AndroidSettings
import dev.patrickgold.florisboard.lib.android.AndroidSettings
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.Preference
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog

View File

@@ -14,17 +14,19 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.devtools
package dev.patrickgold.florisboard.app.devtools
import android.view.textservice.SuggestionsInfo
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.LocalContentColor
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
@@ -37,10 +39,11 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import dev.patrickgold.florisboard.app.prefs.florisPreferenceModel
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.clipboardManager
import dev.patrickgold.florisboard.common.FlorisLocale
import dev.patrickgold.florisboard.common.observeAsNonNullState
import dev.patrickgold.florisboard.editorInstance
import dev.patrickgold.florisboard.lib.FlorisLocale
import dev.patrickgold.florisboard.lib.observeAsNonNullState
import dev.patrickgold.florisboard.spellingManager
import dev.patrickgold.jetpref.datastore.model.observeAsState
import java.text.SimpleDateFormat
@@ -50,14 +53,11 @@ private val CardBackground = Color.Black.copy(0.6f)
private val DateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss", FlorisLocale.default().base)
@Composable
fun DevtoolsOverlay(
modifier: Modifier = Modifier,
) {
fun DevtoolsOverlay(modifier: Modifier = Modifier) {
val prefs by florisPreferenceModel()
val context = LocalContext.current
val clipboardManager by context.clipboardManager()
val showPrimaryClip by prefs.devtools.showPrimaryClip.observeAsState()
val showInputStateOverlay by prefs.devtools.showInputStateOverlay.observeAsState()
val showSpellingOverlay by prefs.devtools.showSpellingOverlay.observeAsState()
CompositionLocalProvider(
@@ -66,11 +66,10 @@ fun DevtoolsOverlay(
) {
Column(modifier = modifier) {
if (showPrimaryClip) {
val primaryClip by clipboardManager.primaryClip.observeAsState()
Text(
text = primaryClip.toString(),
color = Color.White,
)
DevtoolsClipboardOverlay()
}
if (showInputStateOverlay) {
DevtoolsInputStateOverlay()
}
if (showSpellingOverlay) {
DevtoolsSpellingOverlay()
@@ -79,6 +78,46 @@ fun DevtoolsOverlay(
}
}
@Composable
private fun DevtoolsClipboardOverlay() {
val context = LocalContext.current
val clipboardManager by context.clipboardManager()
DevtoolsOverlayBox(title = "Clipboard overlay") {
val primaryClip by clipboardManager.primaryClip.observeAsState()
Text(
modifier = Modifier.padding(bottom = 8.dp, start = 8.dp, end = 8.dp),
text = primaryClip.toString(),
color = Color.White,
)
}
}
@Composable
private fun DevtoolsInputStateOverlay() {
val context = LocalContext.current
val editorInstance by context.editorInstance()
val info by editorInstance.activeInfoFlow.collectAsState()
val content by editorInstance.activeContentFlow.collectAsState()
val selection = content.selection
DevtoolsOverlayBox(title = "Input state overlay") {
DevtoolsSubGroup(title = "EditorInfo") {
DevtoolsText(text = "Type=${info.inputAttributes.type} Variation=${info.inputAttributes.variation} IsRich=${info.isRichInputEditor}")
DevtoolsText(text = "InitialSelection: ${info.initialSelection}")
}
DevtoolsSubGroup(title = "EditorContent") {
DevtoolsText(text = "Selection: { start=${selection.start}, end=${selection.end} }")
DevtoolsText(text = "Before: \"${content.textBeforeSelection}\"")
DevtoolsText(text = "Selected: \"${content.selectedText}\"")
DevtoolsText(text = "After: \"${content.textAfterSelection}\"")
DevtoolsText(text = "ComposingWord: ${content.composing}")
}
}
}
@Composable
private fun DevtoolsSpellingOverlay() {
val context = LocalContext.current
@@ -87,18 +126,8 @@ private fun DevtoolsSpellingOverlay() {
val debugOverlayVersion by spellingManager.debugOverlayVersion.observeAsNonNullState()
val suggestionsInfos = remember(debugOverlayVersion) { spellingManager.debugOverlaySuggestionsInfos.snapshot() }
Column(
modifier = Modifier
.padding(all = 8.dp)
.fillMaxWidth()
.background(CardBackground),
) {
val sortedEntries = suggestionsInfos.entries.sortedByDescending { it.key }
Text(
modifier = Modifier.padding(all = 8.dp),
text = "Spelling overlay (${sortedEntries.size})",
fontSize = 14.sp,
)
val sortedEntries = suggestionsInfos.entries.sortedByDescending { it.key }
DevtoolsOverlayBox(title = "Spelling overlay (${sortedEntries.size})") {
for ((timestamp, wordInfoPair) in sortedEntries) {
val (word, info) = wordInfoPair
val isTypo = (info.suggestionsAttributes and SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) > 0
@@ -131,3 +160,47 @@ private fun DevtoolsSpellingOverlay() {
}
}
}
@Composable
private fun DevtoolsOverlayBox(
title: String,
content: @Composable ColumnScope.() -> Unit,
) {
Column(
modifier = Modifier
.padding(all = 8.dp)
.fillMaxWidth()
.background(CardBackground),
) {
Text(
modifier = Modifier.padding(all = 8.dp),
text = title,
fontSize = 14.sp,
)
content()
}
}
@Composable
private fun DevtoolsSubGroup(
title: String,
content: @Composable ColumnScope.() -> Unit,
) {
Text(
modifier = Modifier.padding(start = 8.dp),
text = title,
fontFamily = FontFamily.Monospace,
fontWeight = FontWeight.Bold,
fontSize = 12.sp,
)
Column(modifier = Modifier.padding(start = 12.dp, bottom = 8.dp), content = content)
}
@Composable
private fun DevtoolsText(text: String) {
Text(
text = text,
fontFamily = FontFamily.Monospace,
fontSize = 12.sp,
)
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.devtools
package dev.patrickgold.florisboard.app.devtools
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -22,13 +22,13 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.Routes
import dev.patrickgold.florisboard.app.ui.components.FlorisConfirmDeleteDialog
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.common.android.AndroidSettings
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
import dev.patrickgold.florisboard.ime.dictionary.FlorisUserDictionaryDatabase
import dev.patrickgold.florisboard.lib.android.AndroidSettings
import dev.patrickgold.florisboard.lib.compose.FlorisConfirmDeleteDialog
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.model.observeAsState
import dev.patrickgold.jetpref.datastore.ui.Preference
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
@@ -66,17 +66,22 @@ fun DevtoolsScreen() = FlorisScreen {
summary = stringRes(R.string.devtools__show_primary_clip__summary),
enabledIf = { prefs.devtools.enabled isEqualTo true },
)
SwitchPreference(
prefs.devtools.showInputStateOverlay,
title = stringRes(R.string.devtools__show_input_state_overlay__label),
summary = stringRes(R.string.devtools__show_input_state_overlay__summary),
enabledIf = { prefs.devtools.enabled isEqualTo true },
)
SwitchPreference(
prefs.devtools.showSpellingOverlay,
title = stringRes(R.string.devtools__show_spelling_overlay__label),
summary = stringRes(R.string.devtools__show_spelling_overlay__summary),
enabledIf = { prefs.devtools.enabled isEqualTo true },
)
// TODO: remove this preference once word suggestions are re-implemented in 0.3.15/16
SwitchPreference(
prefs.devtools.overrideWordSuggestionsMinHeapRestriction,
title = "Override min heap size restriction for word suggestions",
summary = "This allows you to use word suggestions even if your heap size is not intended for it and can break FlorisBoard",
prefs.devtools.showKeyTouchBoundaries,
title = stringRes(R.string.devtools__show_key_touch_boundaries__label),
summary = stringRes(R.string.devtools__show_key_touch_boundaries__summary),
enabledIf = { prefs.devtools.enabled isEqualTo true },
)
Preference(

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.ext
package dev.patrickgold.florisboard.app.ext
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -37,14 +37,14 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisIconButton
import dev.patrickgold.florisboard.app.ui.components.FlorisOutlinedBox
import dev.patrickgold.florisboard.app.ui.components.FlorisTextButton
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
import dev.patrickgold.florisboard.res.ext.ExtensionComponent
import dev.patrickgold.florisboard.res.ext.ExtensionComponentName
import dev.patrickgold.florisboard.res.ext.ExtensionMeta
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.FlorisTextButton
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.ExtensionComponent
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
import dev.patrickgold.florisboard.lib.ext.ExtensionMeta
@Composable
fun ExtensionComponentNoneFoundView() {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.ext
package dev.patrickgold.florisboard.app.ext
import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
@@ -44,24 +44,11 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisButtonBar
import dev.patrickgold.florisboard.app.ui.components.FlorisIconButton
import dev.patrickgold.florisboard.app.ui.components.FlorisInfoCard
import dev.patrickgold.florisboard.app.ui.components.FlorisOutlinedBox
import dev.patrickgold.florisboard.app.ui.components.FlorisOutlinedTextField
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.app.ui.components.FlorisUnsavedChangesDialog
import dev.patrickgold.florisboard.app.ui.components.autoMirrorForRtl
import dev.patrickgold.florisboard.app.ui.components.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.app.ui.settings.advanced.RadioListItem
import dev.patrickgold.florisboard.app.ui.settings.theme.DialogProperty
import dev.patrickgold.florisboard.app.ui.settings.theme.ThemeEditorScreen
import dev.patrickgold.florisboard.app.ui.theme.outline
import dev.patrickgold.florisboard.app.apptheme.outline
import dev.patrickgold.florisboard.app.settings.advanced.RadioListItem
import dev.patrickgold.florisboard.app.settings.theme.DialogProperty
import dev.patrickgold.florisboard.app.settings.theme.ThemeEditorScreen
import dev.patrickgold.florisboard.cacheManager
import dev.patrickgold.florisboard.common.ValidationResult
import dev.patrickgold.florisboard.common.android.showLongToast
import dev.patrickgold.florisboard.common.rememberValidationResult
import dev.patrickgold.florisboard.extensionManager
import dev.patrickgold.florisboard.ime.keyboard.KeyboardExtension
import dev.patrickgold.florisboard.ime.spelling.SpellingExtension
@@ -70,23 +57,36 @@ import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponentEditor
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponentImpl
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionEditor
import dev.patrickgold.florisboard.res.FlorisRef
import dev.patrickgold.florisboard.res.ZipUtils
import dev.patrickgold.florisboard.res.cache.CacheManager
import dev.patrickgold.florisboard.res.ext.Extension
import dev.patrickgold.florisboard.res.ext.ExtensionComponent
import dev.patrickgold.florisboard.res.ext.ExtensionComponentName
import dev.patrickgold.florisboard.res.ext.ExtensionDefaults
import dev.patrickgold.florisboard.res.ext.ExtensionEditor
import dev.patrickgold.florisboard.res.ext.ExtensionJsonConfig
import dev.patrickgold.florisboard.res.ext.ExtensionMaintainer
import dev.patrickgold.florisboard.res.ext.ExtensionManager
import dev.patrickgold.florisboard.res.ext.ExtensionMeta
import dev.patrickgold.florisboard.res.ext.ExtensionValidation
import dev.patrickgold.florisboard.res.ext.validate
import dev.patrickgold.florisboard.res.io.subFile
import dev.patrickgold.florisboard.res.io.writeJson
import dev.patrickgold.florisboard.snygg.SnyggStylesheetJsonConfig
import dev.patrickgold.florisboard.lib.ValidationResult
import dev.patrickgold.florisboard.lib.android.showLongToast
import dev.patrickgold.florisboard.lib.cache.CacheManager
import dev.patrickgold.florisboard.lib.compose.FlorisButtonBar
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
import dev.patrickgold.florisboard.lib.compose.FlorisInfoCard
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedTextField
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.FlorisUnsavedChangesDialog
import dev.patrickgold.florisboard.lib.compose.autoMirrorForRtl
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.Extension
import dev.patrickgold.florisboard.lib.ext.ExtensionComponent
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
import dev.patrickgold.florisboard.lib.ext.ExtensionDefaults
import dev.patrickgold.florisboard.lib.ext.ExtensionEditor
import dev.patrickgold.florisboard.lib.ext.ExtensionJsonConfig
import dev.patrickgold.florisboard.lib.ext.ExtensionMaintainer
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
import dev.patrickgold.florisboard.lib.ext.ExtensionMeta
import dev.patrickgold.florisboard.lib.ext.ExtensionValidation
import dev.patrickgold.florisboard.lib.ext.validate
import dev.patrickgold.florisboard.lib.io.FlorisRef
import dev.patrickgold.florisboard.lib.io.ZipUtils
import dev.patrickgold.florisboard.lib.io.subFile
import dev.patrickgold.florisboard.lib.io.writeJson
import dev.patrickgold.florisboard.lib.rememberValidationResult
import dev.patrickgold.florisboard.lib.snygg.SnyggStylesheetJsonConfig
import dev.patrickgold.florisboard.themeManager
import dev.patrickgold.jetpref.datastore.ui.Preference
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.ext
package dev.patrickgold.florisboard.app.ext
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
@@ -22,11 +22,11 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.common.android.showLongToast
import dev.patrickgold.florisboard.extensionManager
import dev.patrickgold.florisboard.res.ext.Extension
import dev.patrickgold.florisboard.res.ext.ExtensionDefaults
import dev.patrickgold.florisboard.lib.android.showLongToast
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.ext.Extension
import dev.patrickgold.florisboard.lib.ext.ExtensionDefaults
@Composable
fun ExtensionExportScreen(id: String) {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.ext
package dev.patrickgold.florisboard.app.ext
import android.text.format.Formatter
import androidx.activity.compose.rememberLauncherForActivityResult
@@ -45,24 +45,24 @@ import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisBulletSpacer
import dev.patrickgold.florisboard.app.ui.components.FlorisButtonBar
import dev.patrickgold.florisboard.app.ui.components.FlorisOutlinedBox
import dev.patrickgold.florisboard.app.ui.components.FlorisOutlinedButton
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.app.ui.components.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.app.ui.components.florisHorizontalScroll
import dev.patrickgold.florisboard.cacheManager
import dev.patrickgold.florisboard.common.android.showLongToast
import dev.patrickgold.florisboard.common.kotlin.resultOk
import dev.patrickgold.florisboard.extensionManager
import dev.patrickgold.florisboard.ime.keyboard.KeyboardExtension
import dev.patrickgold.florisboard.ime.nlp.NATIVE_NULLPTR
import dev.patrickgold.florisboard.ime.spelling.SpellingExtension
import dev.patrickgold.florisboard.ime.theme.ThemeExtension
import dev.patrickgold.florisboard.res.FileRegistry
import dev.patrickgold.florisboard.res.cache.CacheManager
import dev.patrickgold.florisboard.lib.android.showLongToast
import dev.patrickgold.florisboard.lib.cache.CacheManager
import dev.patrickgold.florisboard.lib.compose.FlorisBulletSpacer
import dev.patrickgold.florisboard.lib.compose.FlorisButtonBar
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedButton
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.io.FileRegistry
import dev.patrickgold.florisboard.lib.kotlin.resultOk
enum class ExtensionImportScreenType(
val id: String,

View File

@@ -14,14 +14,14 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.ext
package dev.patrickgold.florisboard.app.ext
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.app.ui.components.FlorisChip
import dev.patrickgold.florisboard.lib.compose.FlorisChip
@OptIn(ExperimentalMaterialApi::class)
@Composable

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.ext
package dev.patrickgold.florisboard.app.ext
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
@@ -22,8 +22,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.ui.Routes
import dev.patrickgold.florisboard.res.ext.Extension
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.lib.ext.Extension
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
@Composable

View File

@@ -14,12 +14,12 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.ext
package dev.patrickgold.florisboard.app.ext
import androidx.compose.runtime.Composable
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
@Composable
fun ExtensionListScreen() = FlorisScreen {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.ext
package dev.patrickgold.florisboard.app.ext
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -22,16 +22,16 @@ import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.ui.components.FlorisChip
import dev.patrickgold.florisboard.common.android.launchUrl
import dev.patrickgold.florisboard.res.ext.ExtensionMaintainer
import dev.patrickgold.florisboard.lib.android.launchUrl
import dev.patrickgold.florisboard.lib.compose.FlorisChip
import dev.patrickgold.florisboard.lib.ext.ExtensionMaintainer
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
@OptIn(ExperimentalMaterialApi::class)

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.ext
package dev.patrickgold.florisboard.app.ext
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
@@ -23,8 +23,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
@Composable
internal fun ExtensionNotFoundScreen(id: String) = FlorisScreen {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.ext
package dev.patrickgold.florisboard.app.ext
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
@@ -44,21 +44,21 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.Routes
import dev.patrickgold.florisboard.app.ui.components.FlorisConfirmDeleteDialog
import dev.patrickgold.florisboard.app.ui.components.FlorisHyperlinkText
import dev.patrickgold.florisboard.app.ui.components.FlorisOutlinedButton
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.app.ui.components.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.common.android.showLongToast
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.extensionManager
import dev.patrickgold.florisboard.ime.theme.ThemeExtension
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponentImpl
import dev.patrickgold.florisboard.res.FlorisRef
import dev.patrickgold.florisboard.res.ext.Extension
import dev.patrickgold.florisboard.res.ext.ExtensionMaintainer
import dev.patrickgold.florisboard.res.ext.ExtensionMeta
import dev.patrickgold.florisboard.lib.android.showLongToast
import dev.patrickgold.florisboard.lib.compose.FlorisConfirmDeleteDialog
import dev.patrickgold.florisboard.lib.compose.FlorisHyperlinkText
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedButton
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.Extension
import dev.patrickgold.florisboard.lib.ext.ExtensionMaintainer
import dev.patrickgold.florisboard.lib.ext.ExtensionMeta
import dev.patrickgold.florisboard.lib.io.FlorisRef
@Composable
fun ExtensionViewScreen(id: String) {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.ext
package dev.patrickgold.florisboard.app.ext
import android.os.Bundle
import androidx.activity.ComponentActivity

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings
package dev.patrickgold.florisboard.app.settings
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -38,12 +38,14 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.Routes
import dev.patrickgold.florisboard.app.ui.components.FlorisErrorCard
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.app.ui.components.FlorisWarningCard
import dev.patrickgold.florisboard.common.InputMethodUtils
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.lib.android.launchUrl
import dev.patrickgold.florisboard.lib.compose.FlorisButton
import dev.patrickgold.florisboard.lib.compose.FlorisErrorCard
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.FlorisWarningCard
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.util.InputMethodUtils
import dev.patrickgold.jetpref.datastore.model.observeAsState
import dev.patrickgold.jetpref.datastore.ui.Preference
@@ -81,7 +83,7 @@ fun HomeScreen() = FlorisScreen {
Column(modifier = Modifier.padding(horizontal = 16.dp)) {
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Text(
text = "Note on the new Settings UI",
text = "Welcome to the 0.3.16 beta series!",
style = MaterialTheme.typography.subtitle1,
fontWeight = FontWeight.Bold,
)
@@ -98,8 +100,9 @@ fun HomeScreen() = FlorisScreen {
}
}
if (!isCollapsed) {
Text("Welcome to the new Settings of FlorisBoard!\n")
Text("It has been quite a long time since 0.3.13, but since then a lot has changed. FlorisBoard has undergone a major overhaul and now uses a completely new UI library, backend logic and also the Settings have been completely revamped. A big thanks goes to all my beta testers who continuously provided feedback and tested things out, this made the development much more interactive and better!\n")
Text("The 0.3.16 beta series focuses on preparing the keyboard for word suggestions in 0.4.0, getting rid of the input lag and improve input connection handling. Most work is already done, now I am focusing on fixing introduced bugs and generally fixing a lot of bugs to improve the stability of this keyboard.\n")
Text("If you have general feedback on this rework or want to report a newly broken/buggy input for a specific app (or all apps), please make sure to post your feedback in this thread:\n")
FlorisButton(onClick = { context.launchUrl("https://github.com/florisboard/florisboard/discussions/1827") }, text = "Open feedback thread")
Spacer(modifier = Modifier.height(16.dp))
Text("Note that this release does not contain support for word suggestions (will show the current word plus numbers as a placeholder).", color = Color.Red)
Text("Please DO NOT file an issue for this. It is already more than known and a major goal for implementation in 0.4.0. Thank you!\n")

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.about
package dev.patrickgold.florisboard.app.settings.about
import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement
@@ -33,13 +33,13 @@ import androidx.compose.ui.unit.sp
import dev.patrickgold.florisboard.BuildConfig
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.Routes
import dev.patrickgold.florisboard.app.ui.components.FlorisCanvasIcon
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.clipboardManager
import dev.patrickgold.florisboard.common.android.launchUrl
import dev.patrickgold.florisboard.common.android.stringRes
import dev.patrickgold.florisboard.lib.android.launchUrl
import dev.patrickgold.florisboard.lib.android.stringRes
import dev.patrickgold.florisboard.lib.compose.FlorisCanvasIcon
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.Preference
@Composable

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.about
package dev.patrickgold.florisboard.app.settings.about
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.text.selection.SelectionContainer
@@ -28,12 +28,12 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.sp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.app.ui.components.florisHorizontalScroll
import dev.patrickgold.florisboard.app.ui.components.florisVerticalScroll
import dev.patrickgold.florisboard.assetManager
import dev.patrickgold.florisboard.res.FlorisRef
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
import dev.patrickgold.florisboard.lib.compose.florisVerticalScroll
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.io.FlorisRef
@Composable
fun ProjectLicenseScreen() = FlorisScreen {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.about
package dev.patrickgold.florisboard.app.settings.about
import android.webkit.URLUtil
import androidx.compose.foundation.layout.fillMaxSize
@@ -31,10 +31,10 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.app.ui.components.florisScrollbar
import dev.patrickgold.florisboard.common.android.launchUrl
import dev.patrickgold.florisboard.lib.android.launchUrl
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.Preference
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog

View File

@@ -14,19 +14,19 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.advanced
package dev.patrickgold.florisboard.app.settings.advanced
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.AppTheme
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.Routes
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.common.FlorisLocale
import dev.patrickgold.florisboard.common.android.AndroidVersion
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
import dev.patrickgold.florisboard.lib.FlorisLocale
import dev.patrickgold.florisboard.lib.android.AndroidVersion
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.model.observeAsState
import dev.patrickgold.jetpref.datastore.ui.ListPreference
import dev.patrickgold.jetpref.datastore.ui.Preference
@@ -115,6 +115,7 @@ fun AdvancedScreen() = FlorisScreen {
"tr",
"uk",
"zgh",
"zh-CN",
).map { languageTag ->
if (languageTag == "auto") {
entry(

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.advanced
package dev.patrickgold.florisboard.app.settings.advanced
import android.content.Intent
import androidx.activity.compose.rememberLauncherForActivityResult
@@ -33,22 +33,22 @@ import androidx.core.content.FileProvider
import dev.patrickgold.florisboard.BuildConfig
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisButtonBar
import dev.patrickgold.florisboard.app.ui.components.FlorisOutlinedBox
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.app.ui.components.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.app.ui.components.rippleClickable
import dev.patrickgold.florisboard.cacheManager
import dev.patrickgold.florisboard.common.android.showLongToast
import dev.patrickgold.florisboard.common.android.writeFromFile
import dev.patrickgold.florisboard.res.FileRegistry
import dev.patrickgold.florisboard.res.ZipUtils
import dev.patrickgold.florisboard.res.cache.CacheManager
import dev.patrickgold.florisboard.res.ext.ExtensionManager
import dev.patrickgold.florisboard.res.io.subDir
import dev.patrickgold.florisboard.res.io.subFile
import dev.patrickgold.florisboard.res.io.writeJson
import dev.patrickgold.florisboard.lib.android.showLongToast
import dev.patrickgold.florisboard.lib.android.writeFromFile
import dev.patrickgold.florisboard.lib.cache.CacheManager
import dev.patrickgold.florisboard.lib.compose.FlorisButtonBar
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.rippleClickable
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
import dev.patrickgold.florisboard.lib.io.FileRegistry
import dev.patrickgold.florisboard.lib.io.ZipUtils
import dev.patrickgold.florisboard.lib.io.subDir
import dev.patrickgold.florisboard.lib.io.subFile
import dev.patrickgold.florisboard.lib.io.writeJson
import dev.patrickgold.jetpref.datastore.jetprefDatastoreDir
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
import kotlinx.serialization.SerialName

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.advanced
package dev.patrickgold.florisboard.app.settings.advanced
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
@@ -41,30 +41,30 @@ import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.BuildConfig
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.prefs.florisPreferenceModel
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.CardDefaults
import dev.patrickgold.florisboard.app.ui.components.FlorisButtonBar
import dev.patrickgold.florisboard.app.ui.components.FlorisOutlinedBox
import dev.patrickgold.florisboard.app.ui.components.FlorisOutlinedButton
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.app.ui.components.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.cacheManager
import dev.patrickgold.florisboard.common.android.readToFile
import dev.patrickgold.florisboard.common.android.showLongToast
import dev.patrickgold.florisboard.res.FileRegistry
import dev.patrickgold.florisboard.res.ZipUtils
import dev.patrickgold.florisboard.res.cache.CacheManager
import dev.patrickgold.florisboard.res.ext.ExtensionManager
import dev.patrickgold.florisboard.res.io.deleteContentsRecursively
import dev.patrickgold.florisboard.res.io.readJson
import dev.patrickgold.florisboard.res.io.subDir
import dev.patrickgold.florisboard.res.io.subFile
import dev.patrickgold.florisboard.lib.android.readToFile
import dev.patrickgold.florisboard.lib.android.showLongToast
import dev.patrickgold.florisboard.lib.cache.CacheManager
import dev.patrickgold.florisboard.lib.compose.CardDefaults
import dev.patrickgold.florisboard.lib.compose.FlorisButtonBar
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedButton
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
import dev.patrickgold.florisboard.lib.io.ZipUtils
import dev.patrickgold.florisboard.lib.io.deleteContentsRecursively
import dev.patrickgold.florisboard.lib.io.readJson
import dev.patrickgold.florisboard.lib.io.subDir
import dev.patrickgold.florisboard.lib.io.subFile
import dev.patrickgold.jetpref.datastore.JetPref
import dev.patrickgold.jetpref.datastore.ui.Preference
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.FileNotFoundException
import java.text.DateFormat
import java.util.*
@@ -84,6 +84,7 @@ fun RestoreScreen() = FlorisScreen {
title = stringRes(R.string.backup_and_restore__restore__title)
previewFieldVisible = false
val prefs by florisPreferenceModel()
val navController = LocalNavController.current
val context = LocalContext.current
val cacheManager by context.cacheManager()
@@ -108,7 +109,11 @@ fun RestoreScreen() = FlorisScreen {
workspace.zipFile = workspace.inputDir.subFile(Restore.BACKUP_ARCHIVE_FILE_NAME)
context.contentResolver.readToFile(uri, workspace.zipFile)
ZipUtils.unzip(workspace.zipFile, workspace.outputDir)
workspace.metadata = workspace.outputDir.subFile(Backup.METADATA_JSON_NAME).readJson()
workspace.metadata = try {
workspace.outputDir.subFile(Backup.METADATA_JSON_NAME).readJson()
} catch (e: FileNotFoundException) {
error("Invalid archive: either backup_metadata.json is missing or file is not a ZIP archive.")
}
workspace.restoreWarningId = when {
workspace.metadata.versionCode != BuildConfig.VERSION_CODE -> {
R.string.backup_and_restore__restore__metadata_warn_different_version
@@ -132,7 +137,6 @@ fun RestoreScreen() = FlorisScreen {
)
suspend fun performRestore() {
val prefs by florisPreferenceModel()
val workspace = restoreWorkspace!!
val shouldReset = restoreMode == Restore.Mode.ERASE_AND_OVERWRITE
if (restoreFilesSelector.jetprefDatastore) {
@@ -228,9 +232,7 @@ fun RestoreScreen() = FlorisScreen {
FlorisOutlinedButton(
onClick = {
runCatching {
restoreDataFromFileSystemLauncher.launch(
FileRegistry.BackupArchive.mediaType
)
restoreDataFromFileSystemLauncher.launch("*/*")
}.onFailure { error ->
context.showLongToast(R.string.backup_and_restore__restore__failure, "error_message" to error.localizedMessage)
}

View File

@@ -14,13 +14,13 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.clipboard
package dev.patrickgold.florisboard.app.settings.clipboard
import androidx.compose.runtime.Composable
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.res.pluralsRes
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.pluralsRes
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup

View File

@@ -14,14 +14,14 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.dictionary
package dev.patrickgold.florisboard.app.settings.dictionary
import androidx.compose.runtime.Composable
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.Routes
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.Preference
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.dictionary
package dev.patrickgold.florisboard.app.settings.dictionary
import androidx.activity.compose.BackHandler
import androidx.activity.compose.rememberLauncherForActivityResult
@@ -43,23 +43,23 @@ import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisIconButton
import dev.patrickgold.florisboard.app.ui.components.FlorisOutlinedTextField
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.app.ui.components.rippleClickable
import dev.patrickgold.florisboard.app.ui.settings.theme.DialogProperty
import dev.patrickgold.florisboard.common.FlorisLocale
import dev.patrickgold.florisboard.common.android.launchActivity
import dev.patrickgold.florisboard.common.android.showLongToast
import dev.patrickgold.florisboard.common.android.stringRes
import dev.patrickgold.florisboard.common.rememberValidationResult
import dev.patrickgold.florisboard.app.settings.theme.DialogProperty
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
import dev.patrickgold.florisboard.ime.dictionary.FREQUENCY_MAX
import dev.patrickgold.florisboard.ime.dictionary.FREQUENCY_MIN
import dev.patrickgold.florisboard.ime.dictionary.UserDictionaryDao
import dev.patrickgold.florisboard.ime.dictionary.UserDictionaryEntry
import dev.patrickgold.florisboard.ime.dictionary.UserDictionaryValidation
import dev.patrickgold.florisboard.lib.FlorisLocale
import dev.patrickgold.florisboard.lib.android.launchActivity
import dev.patrickgold.florisboard.lib.android.showLongToast
import dev.patrickgold.florisboard.lib.android.stringRes
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedTextField
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.rippleClickable
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.rememberValidationResult
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
import kotlinx.coroutines.delay

View File

@@ -14,17 +14,17 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.gestures
package dev.patrickgold.florisboard.app.settings.gestures
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisInfoCard
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
import dev.patrickgold.florisboard.lib.compose.FlorisInfoCard
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
import dev.patrickgold.jetpref.datastore.ui.ListPreference
@@ -81,6 +81,12 @@ fun GesturesScreen() = FlorisScreen {
stepIncrement = 25,
enabledIf = { prefs.glide.enabled isEqualTo true && prefs.glide.showPreview isEqualTo true },
)
SwitchPreference(
prefs.glide.immediateBackspaceDeletesWord,
title = stringRes(R.string.pref__glide__immediate_backspace_deletes_word__label),
summary = stringRes(R.string.pref__glide__immediate_backspace_deletes_word__summary),
enabledIf = { prefs.glide.enabled isEqualTo true },
)
}
PreferenceGroup(title = stringRes(R.string.pref__gestures__general_title)) {
@@ -139,6 +145,11 @@ fun GesturesScreen() = FlorisScreen {
title = stringRes(R.string.pref__gestures__delete_key_swipe_left__label),
entries = SwipeAction.deleteSwipeListEntries(),
)
ListPreference(
prefs.gestures.deleteKeyLongPress,
title = stringRes(R.string.pref__gestures__delete_key_long_press__label),
entries = SwipeAction.deleteLongPressListEntries(),
)
DialogSliderPreference(
prefs.gestures.swipeVelocityThreshold,
title = stringRes(R.string.pref__gestures__swipe_velocity_threshold__label),

View File

@@ -14,13 +14,13 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.keyboard
package dev.patrickgold.florisboard.app.settings.keyboard
import androidx.compose.runtime.Composable
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.ime.keyboard.InputFeedbackController
import dev.patrickgold.florisboard.ime.input.InputFeedbackController
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup

View File

@@ -14,18 +14,18 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.keyboard
package dev.patrickgold.florisboard.app.settings.keyboard
import androidx.compose.runtime.Composable
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.Routes
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.ime.landscapeinput.LandscapeInputUiMode
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
import dev.patrickgold.florisboard.ime.text.key.KeyHintMode
import dev.patrickgold.florisboard.ime.text.key.UtilityKeyAction
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
import dev.patrickgold.jetpref.datastore.ui.ListPreference

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.localization
package dev.patrickgold.florisboard.app.settings.localization
import androidx.compose.foundation.layout.padding
import androidx.compose.material.ExtendedFloatingActionButton
@@ -28,14 +28,14 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.Routes
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.app.ui.components.FlorisWarningCard
import dev.patrickgold.florisboard.common.observeAsNonNullState
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
import dev.patrickgold.florisboard.ime.keyboard.LayoutType
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.FlorisWarningCard
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.observeAsNonNullState
import dev.patrickgold.florisboard.subtypeManager
import dev.patrickgold.jetpref.datastore.model.observeAsState
import dev.patrickgold.jetpref.datastore.ui.ListPreference

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.localization
package dev.patrickgold.florisboard.app.settings.localization
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
@@ -43,12 +43,12 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.prefs.florisPreferenceModel
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.app.ui.components.florisScrollbar
import dev.patrickgold.florisboard.common.FlorisLocale
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
import dev.patrickgold.florisboard.lib.FlorisLocale
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.model.observeAsState
import dev.patrickgold.jetpref.material.ui.JetPrefListItem

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.localization
package dev.patrickgold.florisboard.app.settings.localization
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
@@ -52,16 +52,8 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.Observer
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.prefs.florisPreferenceModel
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.Routes
import dev.patrickgold.florisboard.app.ui.components.FlorisButtonBar
import dev.patrickgold.florisboard.app.ui.components.FlorisDropdownLikeButton
import dev.patrickgold.florisboard.app.ui.components.FlorisDropdownMenu
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.common.FlorisLocale
import dev.patrickgold.florisboard.common.android.AndroidVersion
import dev.patrickgold.florisboard.common.observeAsNonNullState
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
import dev.patrickgold.florisboard.ime.core.Subtype
import dev.patrickgold.florisboard.ime.core.SubtypeJsonConfig
@@ -71,7 +63,14 @@ import dev.patrickgold.florisboard.ime.keyboard.LayoutArrangementComponent
import dev.patrickgold.florisboard.ime.keyboard.LayoutType
import dev.patrickgold.florisboard.ime.keyboard.extCorePopupMapping
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.res.ext.ExtensionComponentName
import dev.patrickgold.florisboard.lib.FlorisLocale
import dev.patrickgold.florisboard.lib.compose.FlorisButtonBar
import dev.patrickgold.florisboard.lib.compose.FlorisDropdownLikeButton
import dev.patrickgold.florisboard.lib.compose.FlorisDropdownMenu
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
import dev.patrickgold.florisboard.lib.observeAsNonNullState
import dev.patrickgold.florisboard.subtypeManager
import dev.patrickgold.jetpref.datastore.model.observeAsState
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
@@ -103,6 +102,7 @@ private class SubtypeEditorState(init: Subtype?) {
secondaryLocales = editor.secondaryLocales.value,
composer = editor.composer.value,
currencySet = editor.currencySet.value,
punctuationRule = editor.punctuationRule.value,
popupMapping = editor.popupMapping.value,
layoutMap = editor.layoutMap.value,
)
@@ -120,6 +120,7 @@ private class SubtypeEditorState(init: Subtype?) {
val secondaryLocales: MutableState<List<FlorisLocale>> = mutableStateOf(init?.secondaryLocales ?: listOf())
val composer: MutableState<ExtensionComponentName> = mutableStateOf(init?.composer ?: SelectComponentName)
val currencySet: MutableState<ExtensionComponentName> = mutableStateOf(init?.currencySet ?: SelectComponentName)
val punctuationRule: MutableState<ExtensionComponentName> = mutableStateOf(init?.punctuationRule ?: SelectComponentName)
val popupMapping: MutableState<ExtensionComponentName> = mutableStateOf(init?.popupMapping ?: SelectComponentName)
val layoutMap: MutableState<SubtypeLayoutMap> = mutableStateOf(init?.layoutMap ?: SelectLayoutMap)
@@ -129,6 +130,7 @@ private class SubtypeEditorState(init: Subtype?) {
secondaryLocales.value = subtype.secondaryLocales
composer.value = subtype.composer
currencySet.value = subtype.currencySet
punctuationRule.value = subtype.punctuationRule
popupMapping.value = subtype.popupMapping
layoutMap.value = subtype.layoutMap
}
@@ -148,7 +150,7 @@ private class SubtypeEditorState(init: Subtype?) {
check(layoutMap.value.phone2 != SelectComponentName)
Subtype(
id.value, primaryLocale.value, secondaryLocales.value, composer.value,
currencySet.value, popupMapping.value, layoutMap.value,
currencySet.value, punctuationRule.value, popupMapping.value, layoutMap.value,
)
}
}
@@ -267,17 +269,12 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
overflow = TextOverflow.Ellipsis,
)
val systemLocales = remember {
if (AndroidVersion.ATLEAST_API24_N) {
val list = mutableListOf<FlorisLocale>()
val localeList = configuration.locales
for (n in 0 until localeList.size()) {
list.add(FlorisLocale.from(localeList.get(n)))
}
list
} else {
@Suppress("DEPRECATION")
listOf(FlorisLocale.from(configuration.locale))
val list = mutableListOf<FlorisLocale>()
val localeList = configuration.locales
for (n in 0 until localeList.size()) {
list.add(FlorisLocale.from(localeList.get(n)))
}
list
}
val suggestedPresets = remember(subtypePresets) {
val presets = mutableListOf<SubtypePreset>()

View File

@@ -14,15 +14,15 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.media
package dev.patrickgold.florisboard.app.settings.media
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.res.pluralsRes
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSkinTone
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.pluralsRes
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.model.observeAsState
import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi

View File

@@ -14,14 +14,14 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.smartbar
package dev.patrickgold.florisboard.app.settings.smartbar
import androidx.compose.runtime.Composable
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.ime.text.smartbar.SecondaryRowPlacement
import dev.patrickgold.florisboard.ime.text.smartbar.SmartbarRowType
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.ListPreference
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package dev.patrickgold.florisboard.app.ui.settings.spelling
package dev.patrickgold.florisboard.app.settings.spelling
import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult
@@ -42,15 +42,15 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.res.stringRes
import dev.patrickgold.florisboard.app.ui.components.FlorisDropdownMenu
import dev.patrickgold.florisboard.app.ui.components.FlorisScreen
import dev.patrickgold.florisboard.app.ui.components.FlorisStep
import dev.patrickgold.florisboard.app.ui.components.FlorisStepLayout
import dev.patrickgold.florisboard.app.ui.components.FlorisStepState
import dev.patrickgold.florisboard.extensionManager
import dev.patrickgold.florisboard.ime.spelling.SpellingExtensionEditor
import dev.patrickgold.florisboard.ime.spelling.SpellingManager
import dev.patrickgold.florisboard.lib.compose.FlorisDropdownMenu
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.FlorisStep
import dev.patrickgold.florisboard.lib.compose.FlorisStepLayout
import dev.patrickgold.florisboard.lib.compose.FlorisStepState
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.spellingManager
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog

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