Compare commits
151 Commits
v0.5.0-alp
...
release/0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab49fc5f29 | ||
|
|
b8947f96e6 | ||
|
|
96e3af9c16 | ||
|
|
fa79fa3849 | ||
|
|
f646825e9c | ||
|
|
0a299e1b04 | ||
|
|
6417cf5958 | ||
|
|
234580fd48 | ||
|
|
fe015c549c | ||
|
|
253ee969eb | ||
|
|
8fa986ca76 | ||
|
|
640a1c56cc | ||
|
|
0799adc193 | ||
|
|
059d2fd4bf | ||
|
|
1275650ca5 | ||
|
|
8359a6cd6c | ||
|
|
31730348b9 | ||
|
|
f12170543f | ||
|
|
37f9266ba3 | ||
|
|
ad47e51f0d | ||
|
|
6ccfb579b0 | ||
|
|
59e32a3a28 | ||
|
|
cee3da97f0 | ||
|
|
afad229273 | ||
|
|
b23496afa4 | ||
|
|
2dd37439ca | ||
|
|
75f774d5e2 | ||
|
|
38d91718ac | ||
|
|
8d9bacbefd | ||
|
|
a1031acd8e | ||
|
|
7a3af3eb54 | ||
|
|
7a433e9860 | ||
|
|
f687873cc5 | ||
|
|
c6a84fd324 | ||
|
|
dcc490cc9d | ||
|
|
d7b52cd489 | ||
|
|
fc7700395c | ||
|
|
9f07702852 | ||
|
|
f111544d81 | ||
|
|
fc09bb74c0 | ||
|
|
674bdccc40 | ||
|
|
3e69dddaa9 | ||
|
|
3c175bc60d | ||
|
|
83150e17ab | ||
|
|
2d6646e674 | ||
|
|
cc9511edca | ||
|
|
f25df31147 | ||
|
|
0ccb0c33b4 | ||
|
|
7ea4e8c827 | ||
|
|
93c76640f4 | ||
|
|
c2c874a9e0 | ||
|
|
b14877da8c | ||
|
|
2fb1fdb281 | ||
|
|
52646ecba1 | ||
|
|
7905dae807 | ||
|
|
e73ac5f96f | ||
|
|
15039fb020 | ||
|
|
c035e19cfc | ||
|
|
8d30403676 | ||
|
|
2886a24fcc | ||
|
|
352c98205c | ||
|
|
18c76b7195 | ||
|
|
ea0712225a | ||
|
|
1132eaf59b | ||
|
|
cba85f1c0e | ||
|
|
15dba9f33a | ||
|
|
3859528120 | ||
|
|
046d253382 | ||
|
|
c55a87862f | ||
|
|
ee08c58db2 | ||
|
|
05d4a5cf62 | ||
|
|
c5c8f7a4c3 | ||
|
|
ce3aee93d6 | ||
|
|
96340f7277 | ||
|
|
d0dcf5be38 | ||
|
|
15450a760e | ||
|
|
de0027d87e | ||
|
|
c7e83fca21 | ||
|
|
115dc5c42a | ||
|
|
c78cf84d6c | ||
|
|
5e59f144dd | ||
|
|
564c075763 | ||
|
|
86365d393b | ||
|
|
45a0c9ef63 | ||
|
|
25da6be1a4 | ||
|
|
976afc2e51 | ||
|
|
d994da8c97 | ||
|
|
40a22a762a | ||
|
|
264b9ff98b | ||
|
|
f1b7ddedb8 | ||
|
|
b1665f61e5 | ||
|
|
0fa663f658 | ||
|
|
b7f5bf267d | ||
|
|
8563f37c98 | ||
|
|
d3a2e50dda | ||
|
|
29562c9980 | ||
|
|
5bbb5cfbce | ||
|
|
4ada583418 | ||
|
|
200a195ae6 | ||
|
|
fa910692bc | ||
|
|
c3545d1625 | ||
|
|
300ca755ec | ||
|
|
3efb760787 | ||
|
|
647f7c106b | ||
|
|
d998f8fe0c | ||
|
|
b7020d7cc9 | ||
|
|
5b874fc22a | ||
|
|
4a6fad851c | ||
|
|
6de6b37f6f | ||
|
|
d2eb6fd1c4 | ||
|
|
4b84a1ed40 | ||
|
|
a1d4079ef9 | ||
|
|
ba6d401447 | ||
|
|
43168fc31f | ||
|
|
d234d30154 | ||
|
|
e010a22863 | ||
|
|
da3b99fda8 | ||
|
|
23f58496e3 | ||
|
|
40ecbe1428 | ||
|
|
62a2131fa8 | ||
|
|
df8f96396f | ||
|
|
bace0259ec | ||
|
|
64f062307d | ||
|
|
2ca69f8fa9 | ||
|
|
f8537a2245 | ||
|
|
cdb6707850 | ||
|
|
35db9be1bf | ||
|
|
8551bb6842 | ||
|
|
9ad5bc68d3 | ||
|
|
ebf5c0f898 | ||
|
|
f913b4f7db | ||
|
|
4f966385ec | ||
|
|
e2ce63a2e9 | ||
|
|
64603c4692 | ||
|
|
90d3fbfdf1 | ||
|
|
1f01c56c68 | ||
|
|
c0a5486619 | ||
|
|
511802cb25 | ||
|
|
eb057ecaf5 | ||
|
|
f93938b43a | ||
|
|
2b7f8212c7 | ||
|
|
44c2c6e9c1 | ||
|
|
a2cfe091a6 | ||
|
|
4d9b4d5522 | ||
|
|
defe45fec6 | ||
|
|
c6721067bf | ||
|
|
a7f55d4589 | ||
|
|
eb2e260924 | ||
|
|
4950533409 | ||
|
|
813047fb40 | ||
|
|
96f3dccfc2 |
11
.dockerignore
Normal file
11
.dockerignore
Normal file
@@ -0,0 +1,11 @@
|
||||
**/.*/
|
||||
!.git/
|
||||
**/build/
|
||||
**/dist/
|
||||
**/out/
|
||||
**/target/
|
||||
utils/
|
||||
!utils/repr_build/scripts/
|
||||
.env
|
||||
gradlew.bat
|
||||
local.properties
|
||||
16
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
16
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Bug Report
|
||||
name: 🐛 Bug Report
|
||||
description: Create a report to help FlorisBoard improve
|
||||
labels:
|
||||
- "bug"
|
||||
@@ -44,7 +44,8 @@ body:
|
||||
label: Install Source
|
||||
options:
|
||||
- Google PlayStore
|
||||
- F-Droid
|
||||
- F-Droid (F-Droid Main)
|
||||
- F-Droid (IzzyOnDroid)
|
||||
- GitHub
|
||||
validations:
|
||||
required: true
|
||||
@@ -62,3 +63,14 @@ body:
|
||||
placeholder: e.g. 10, Stock
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: "Checklist"
|
||||
options:
|
||||
- label: "I made sure that there are *no existing issues* - [open](https://github.com/florisboard/florisboard/issues) or [closed](https://github.com/florisboard/florisboard/issues?q=is%3Aissue+is%3Aclosed) - which I could contribute my information to."
|
||||
required: true
|
||||
- label: "I have read and understood the [contribution guidelines](https://github.com/florisboard/florisboard/blob/main/CONTRIBUTING.md)."
|
||||
required: true
|
||||
- label: "I have taken the time to fill in all the required details. I understand that the bug report will be dismissed otherwise."
|
||||
required: true
|
||||
|
||||
12
.github/ISSUE_TEMPLATE/config.yml
vendored
12
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,8 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Ask a question
|
||||
url: https://github.com/florisboard/florisboard/discussions/new?category=q-a
|
||||
about: Ask here if you have a question about FlorisBoard or need assistance
|
||||
- name: General feedback
|
||||
url: https://github.com/florisboard/florisboard/discussions/new?category=feedback
|
||||
about: Give general feedback about this project
|
||||
- name: 💬 Matrix
|
||||
url: https://matrix.to/#/#florisboard:matrix.org
|
||||
about: General chat about FlorisBoard and quick Q/A
|
||||
- name: ❓ Questions / Feedback
|
||||
url: https://github.com/florisboard/florisboard/discussions/new/choose
|
||||
about: Post your questions or feedback in the discussions panel
|
||||
|
||||
13
.github/ISSUE_TEMPLATE/crash_report.yml
vendored
13
.github/ISSUE_TEMPLATE/crash_report.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Crash report
|
||||
name: 💥 Crash report
|
||||
description: Create a report with a generated crash log attached to help FlorisBoard improve
|
||||
labels:
|
||||
- "bug"
|
||||
@@ -36,3 +36,14 @@ body:
|
||||
description: Paste the generated crash log below
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: "Checklist"
|
||||
options:
|
||||
- label: "I made sure that there are *no existing issues* - [open](https://github.com/florisboard/florisboard/issues) or [closed](https://github.com/florisboard/florisboard/issues?q=is%3Aissue+is%3Aclosed) - which I could contribute my information to."
|
||||
required: true
|
||||
- label: "I have read and understood the [contribution guidelines](https://github.com/florisboard/florisboard/blob/main/CONTRIBUTING.md)."
|
||||
required: true
|
||||
- label: "I have taken the time to fill in all the required details. I understand that the crash report will be dismissed otherwise."
|
||||
required: true
|
||||
|
||||
13
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
13
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Feature request / Suggestion
|
||||
name: ✨ Feature request
|
||||
description: Suggest an idea for this project
|
||||
labels:
|
||||
- "proposal"
|
||||
@@ -20,3 +20,14 @@ body:
|
||||
description: Please explain your idea in a precise way.
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: "Checklist"
|
||||
options:
|
||||
- label: "I made sure that there are *no existing issues* - [open](https://github.com/florisboard/florisboard/issues) or [closed](https://github.com/florisboard/florisboard/issues?q=is%3Aissue+is%3Aclosed) - which I could contribute my information to."
|
||||
required: true
|
||||
- label: "I have read and understood the [contribution guidelines](https://github.com/florisboard/florisboard/blob/main/CONTRIBUTING.md)."
|
||||
required: true
|
||||
- label: "I have taken the time to fill in all the required details. I understand that the feature request will be dismissed otherwise."
|
||||
required: true
|
||||
|
||||
4
.github/workflows/android.yml
vendored
4
.github/workflows/android.yml
vendored
@@ -25,14 +25,14 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: gradle/actions/wrapper-validation@v3
|
||||
- uses: gradle/actions/wrapper-validation@v4
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
- name: Set up CMake and Ninja
|
||||
uses: lukka/get-cmake@latest
|
||||
uses: lukka/get-cmake@v4.0.2
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew clean assembleDebug
|
||||
- uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -4,32 +4,34 @@ Thanks for considering contributing to FlorisBoard!
|
||||
|
||||
There are several ways to contribute to FlorisBoard. This document provides some general guidelines for each type of contribution.
|
||||
|
||||
## General contributions
|
||||
The FlorisBoard community is international, as such we require all contributions, including issues, pull requests, and participation in the Matrix chat to be in English and follow the [code of conduct](https://github.com/florisboard/florisboard/blob/main/CODE_OF_CONDUCT.md). Contributions not adhering to these requirements will be dismissed. Thanks for making the FlorisBoard community an inclusive and safe space for everyone!
|
||||
|
||||
### Translation
|
||||
## Non-code contributions
|
||||
|
||||
To make FlorisBoard accessible in as many languages as possible, the platform [Crowdin](https://crowdin.florisboard.patrickgold.dev) is used to crowdsource and manage translations. The list of languages in Crowdin covers a good range of languages, but feel free to send an email to [florisboard@patrickgold.dev](mailto:florisboard@patrickgold.dev) to request a new language.
|
||||
### Translations
|
||||
|
||||
To make FlorisBoard accessible in as many languages as possible, the platform [Crowdin](https://crowdin.florisboard.org) is used to crowdsource and manage translations. The list of languages in Crowdin covers a good range of languages, but feel free to email [florisboard@patrickgold.dev](mailto:florisboard@patrickgold.dev) to request a new language.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> This is the only source of translations - **PRs that add/update translations are not accepted.**
|
||||
|
||||
### Feedback
|
||||
|
||||
You can [give general feedback](https://github.com/florisboard/florisboard/discussions/new?category=feedback) directly here on GitHub. This is the preferred way to give feedback, as it allows not only for me to read and respond to feedback, but for everyone in this community.
|
||||
|
||||
### Bug reporting
|
||||
|
||||
This kind of contribution is the most important, as it tells where FlorisBoard has flaws and thus should be improved to maximize stability and user experience. To make this process as smooth as possible, please use the pre-made [issue template](.github/ISSUE_TEMPLATE/bug_report.md) for bug reporting. This makes it easy for us to understand what the bug is and how to solve it.
|
||||
Allows us to see where FlorisBoard has flaws and should be improved to maximize stability and user experience. To make this process as smooth as possible, please use the pre-made [bug report template](https://github.com/florisboard/florisboard/issues/new?template=bug_report.yml). This makes it easy for us to understand what the bug is and how to solve it.
|
||||
|
||||
#### Capturing error logs
|
||||
|
||||
Logs are captured by FlorisBoard's crash handler, which gives you the ability to copy it to the clipboard and paste it in GitHub. This is the preferred way to capture logs.
|
||||
Logs are captured by FlorisBoard's crash handler, which gives you the ability to copy it to the clipboard and paste it in the crash report [issue template](https://github.com/florisboard/florisboard/issues/new?template=crash_report.yml). This is the preferred way to capture logs.
|
||||
|
||||
Alternatively, you can also use ADB (Android Debug Bridge) to capture the error log. This is recommended for experienced users only.
|
||||
|
||||
### Feature proposals
|
||||
|
||||
Use the feature proposal issue template to suggest a new idea or improvement for this project.
|
||||
Use the feature proposal [issue template](https://github.com/florisboard/florisboard/issues/new?template=feature_request.yml) to suggest a new idea or improvement for this project.
|
||||
|
||||
### Feedback
|
||||
|
||||
You can [give general feedback](https://github.com/florisboard/florisboard/discussions/new?category=feedback) directly here on GitHub. This is the preferred way to give feedback, as it allows not only for me to read and respond to feedback, but for everyone in this community.
|
||||
|
||||
## Code contributions
|
||||
|
||||
@@ -38,15 +40,15 @@ You are always welcome to contribute new features or work on existing issues, th
|
||||
> [!NOTE]
|
||||
> If you intend to implement a bigger feature please coordinate with us so we can prevent that there's a major difference in expected implementation.
|
||||
|
||||
If you are overwhelmed by the code don't hesistate to ask for help in the [dev chat](https://matrix.to/#/#florisboard-dev:matrix.org) or the discussions tab! Some issues are also marked as good first issue, which are easy to do tasks.
|
||||
If you are overwhelmed by the code don't hesitate to ask for help in the [dev chat](https://matrix.to/#/#florisboard-dev:matrix.org) or the discussions tab! Some issues are also marked as good first issue, which are easy to do tasks.
|
||||
|
||||
### System requirements for development
|
||||
|
||||
- Desktop PC with Linux or WSL2 (Windows)
|
||||
- MacOS and Windows without WSL2 probably works too however there's no official support
|
||||
- At least 16GB of RAM (because of Android Studio)
|
||||
- At least 16GB of RAM (because of Android Studio / IntelliJ)
|
||||
- The following tools must be installed:
|
||||
- Android Studio (bundles SDK and NDK)
|
||||
- Android Studio (bundles SDK and NDK) or IntelliJ with Android and Compose plugin
|
||||
- Java 17
|
||||
- CMake 3.22+
|
||||
- Clang 15+
|
||||
@@ -64,34 +66,7 @@ If you want to manually build the project without Android Studio you must ensure
|
||||
|
||||
and Gradle should take care of every build task.
|
||||
|
||||
## Joining the team
|
||||
|
||||
If you want to join the core maintainer/moderator team on a volunteer basis and be part of this project's journey that's great to hear!
|
||||
|
||||
### Basic Requirements
|
||||
|
||||
- A passion for seeing FlorisBoard flourish
|
||||
- Good English skills for team and public communication
|
||||
- A GitHub account and a Matrix handle
|
||||
|
||||
### Why Join
|
||||
|
||||
You'll have the chance to work directly with me and other team members. While the general idea is for us to work on all kinds of different aspects of the project as a team, if you're particularly interested in a specific area (e.g., UI, extensions, text processing), that's totally okay too!
|
||||
|
||||
### Available Roles
|
||||
|
||||
Currently the following roles are available and need help:
|
||||
|
||||
Role Description | Required Dev Experience
|
||||
---|---
|
||||
Software Developer (Kotlin) for Core App | Java/Kotlin development experience (on Android)
|
||||
Software Developer (Rust) for Native Core | Some Rust development experience
|
||||
GitHub Issues/Discussions Moderator | None
|
||||
Crowdin Translation Verifier | Language proficiency for the language you want to verify
|
||||
|
||||
Interested? Feel free to dm me ([@patrickgold](https://matrix.to/#/@patrickgold:matrix.org)) on Matrix or send an email to [florisboard@patrickgold.dev](mailto:florisboard@patrickgold.dev).
|
||||
|
||||
## Donating
|
||||
|
||||
Alternatively you can also show your support by buying me a coffee, so I can stay up all night and chase away bugs or add new cool stuff :)
|
||||
You can also show your support by buying me a coffee, so I can stay up all night and chase away bugs or add new cool stuff :)
|
||||
See the `Sponsors` button for available options!
|
||||
|
||||
35
README.md
35
README.md
@@ -1,11 +1,11 @@
|
||||
<img align="left" width="80" height="80"
|
||||
src=".github/repo_icon.png" alt="App icon">
|
||||
|
||||
# FlorisBoard [](https://crowdin.florisboard.patrickgold.dev) [](https://matrix.to/#/#florisboard:matrix.org) [](CODE_OF_CONDUCT.md) [](https://github.com/florisboard/florisboard/actions/workflows/android.yml)
|
||||
# FlorisBoard [](https://crowdin.florisboard.org) [](https://matrix.to/#/#florisboard:matrix.org) [](CODE_OF_CONDUCT.md) [](https://github.com/florisboard/florisboard/actions/workflows/android.yml)
|
||||
|
||||
**FlorisBoard** is a free and open-source keyboard for Android 7.0+
|
||||
**FlorisBoard** is a free and open-source keyboard for Android 8.0+
|
||||
devices. It aims at being modern, user-friendly and customizable while
|
||||
fully respecting your privacy. Currently in early-beta state.
|
||||
fully respecting your privacy. Currently in beta state.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
@@ -26,10 +26,13 @@ fully respecting your privacy. Currently in early-beta state.
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<p><a href="https://f-droid.org/packages/dev.patrickgold.florisboard"><img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="64" alt="F-Droid badge"></a></p>
|
||||
<p>
|
||||
<a href="https://apt.izzysoft.de/fdroid/index/apk/dev.patrickgold.florisboard"><img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" height="64" alt="IzzySoft repo badge"></a>
|
||||
<a href="https://f-droid.org/packages/dev.patrickgold.florisboard"><img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="64" alt="F-Droid badge"></a>
|
||||
</p>
|
||||
<p>
|
||||
|
||||
**Google Play**: Join the [FlorisBoard Test Group](https://groups.google.com/g/florisboard-public-alpha-test), then visit the [testing page](https://play.google.com/apps/testing/dev.patrickgold.florisboard). Once joined and installed, updates will be delivered like for any other app. ([Store entry](https://play.google.com/store/apps/details?id=dev.patrickgold.florisboard))
|
||||
**Google Play**: Join the [FlorisBoard Test Group](https://groups.google.com/g/florisboard-closed-beta-test), then visit the [testing page](https://play.google.com/apps/testing/dev.patrickgold.florisboard). Once joined and installed, updates will be delivered like for any other app. ([Store entry](https://play.google.com/store/apps/details?id=dev.patrickgold.florisboard))
|
||||
|
||||
</p>
|
||||
<p>
|
||||
@@ -47,7 +50,7 @@ fully respecting your privacy. Currently in early-beta state.
|
||||
<p><a href="https://apt.izzysoft.de/fdroid/index/apk/dev.patrickgold.florisboard.beta"><img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" height="64" alt="IzzySoft repo badge"></a></p>
|
||||
<p>
|
||||
|
||||
**Google Play**: Join the [FlorisBoard Test Group](https://groups.google.com/g/florisboard-public-alpha-test), then visit the [preview testing page](https://play.google.com/apps/testing/dev.patrickgold.florisboard.beta). Once joined and installed, updates will be delivered like for any other app. ([Store entry](https://play.google.com/store/apps/details?id=dev.patrickgold.florisboard.beta))
|
||||
**Google Play**: Join the [FlorisBoard Test Group](https://groups.google.com/g/florisboard-closed-beta-test), then visit the [preview testing page](https://play.google.com/apps/testing/dev.patrickgold.florisboard.beta). Once joined and installed, updates will be delivered like for any other app. ([Store entry](https://play.google.com/store/apps/details?id=dev.patrickgold.florisboard.beta))
|
||||
|
||||
</p>
|
||||
<p>
|
||||
@@ -64,7 +67,7 @@ fully respecting your privacy. Currently in early-beta state.
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Beginning with v0.6.0 FlorisBoard will enter the public beta on Google Play.
|
||||
Beginning with v0.7 FlorisBoard will enter the public beta on Google Play.
|
||||
|
||||
## Highlighted features
|
||||
- Integrated clipboard manager / history
|
||||
@@ -74,7 +77,7 @@ Beginning with v0.6.0 FlorisBoard will enter the public beta on Google Play.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Word suggestions/spell checking are not included in the current releases
|
||||
> and are a major goal for the v0.5 milestone.
|
||||
> and are a major goal for the v0.6 milestone.
|
||||
|
||||
Feature roadmap: See [ROADMAP.md](ROADMAP.md)
|
||||
|
||||
@@ -96,6 +99,22 @@ Many thanks to Ali ([@4H1R](https://github.com/4H1R)) for implementing the store
|
||||
Please refer to this [page](https://github.com/florisboard/florisboard/wiki/List-of-permissions-FlorisBoard-requests)
|
||||
to get more information on this topic.
|
||||
|
||||
## APK signing certificate hashes
|
||||
|
||||
The package names and SHA-256 hashes of the signature certificate are listed below, so you can verify both FlorisBoard variants with apksigner by using `apksigner verify --print-certs florisboard-<version>-<track>.apk` when you download the APK.
|
||||
If you have [AppVerifier](https://github.com/soupslurpr/AppVerifier) installed, you can alternatively copy both the package name and the hash of the corresponding track and share them to AppVerifier.
|
||||
|
||||
##### Stable track:
|
||||
|
||||
dev.patrickgold.florisboard<br>
|
||||
0B:80:71:64:50:8E:AF:EB:1F:BB:81:5B:E7:A2:3C:77:FE:68:9D:94:B1:43:75:C9:9B:DA:A9:B6:57:7F:D6:D6
|
||||
|
||||
##### Preview track:
|
||||
|
||||
dev.patrickgold.florisboard.beta<br>
|
||||
0B:80:71:64:50:8E:AF:EB:1F:BB:81:5B:E7:A2:3C:77:FE:68:9D:94:B1:43:75:C9:9B:DA:A9:B6:57:7F:D6:D6
|
||||
|
||||
|
||||
## Used libraries, components and icons
|
||||
* [AndroidX libraries](https://github.com/androidx/androidx) by
|
||||
[Android Jetpack](https://github.com/androidx)
|
||||
|
||||
72
ROADMAP.md
72
ROADMAP.md
@@ -1,49 +1,67 @@
|
||||
# Roadmap
|
||||
|
||||
This feature roadmap intents to provide transparency to what is planned to be added to FlorisBoard in the foreseeable future. Note that there are no ETAs for any version milestones down below, experience has shown these won't hold anyways.
|
||||
This feature roadmap intents to provide transparency to what is planned to be added to FlorisBoard in the foreseeable future. Note that there are no ETAs for any version milestones down below, experience has shown these won't hold anyway.
|
||||
|
||||
Each major milestone has associated alpha/beta releases, so if you are interested in previewing features quicker, keep an eye out! Each major 0.x release has also patch releases after the initial major release, which will be published on both the stable and preview tracks.
|
||||
|
||||
## 0.5
|
||||
## 0.5 (currently in development)
|
||||
|
||||
- Implement predictive text support / spell checking
|
||||
- Consider adding proximity-based key typo detection
|
||||
- Add new extension type: Language Pack
|
||||
- Basically groups all locale-relevant data (predictive base model, emoji suggestion data, ...)
|
||||
in a dynamically importable extension file
|
||||
- New text processing logic (maybe moved back / split to 0.6)
|
||||
- Add floating keyboard mode
|
||||
- New keyboard layout engine + file syntax based on the upcoming Unicode Keyboard v3 standard
|
||||
- Add Tablet mode / Optimizations for landscape input based on new keyboard layout engine
|
||||
- Add support for any remaining new features introduced with Android 13
|
||||
> [!NOTE]
|
||||
> The milestone 0.5 was split, thus the word suggestions now come with version 0.6. The old version 0.6 has been moved down and is now 0.7. The time it takes to implement word suggestions will not change, but we can now release the new theme editor earlier, which would otherwise lie dormant.
|
||||
|
||||
- [x] Theme rework part II / Snygg v2
|
||||
- [x] See https://github.com/florisboard/florisboard/pull/2855
|
||||
- [x] Spaces in URI bug (See https://github.com/florisboard/florisboard/issues/2898)
|
||||
- [x] Re-add time based theme switching (See https://github.com/florisboard/florisboard/pull/2977)
|
||||
- [x] Add support for any remaining new features introduced with Android 13 / 14
|
||||
- [x] Raise minimum required Android version from Android 7 (SDK level 24) to Android 8 (SDK level 26)
|
||||
|
||||
## 0.6
|
||||
|
||||
- Complete rework of the Emoji panel
|
||||
- Emoji search
|
||||
- Fully scrollable emoji list (soft category borders)
|
||||
- More granular themeing options
|
||||
- Layout customization (e.g. placement of category buttons)
|
||||
- Maybe: consider upgrading to emoji2 for better unified system-wide emoji styles
|
||||
- Reimplementation of glide typing with the new layout engine and predictive text core
|
||||
- Prepare FlorisBoard repository and app store presence for public beta release on Google Play (will go live with stable 0.6)
|
||||
- Rework branding images and texts of FlorisBoard for the app stores
|
||||
- Focus on stability and experience improvements of the app and keyboard
|
||||
- Add support for new features introduced with Android 14 / 15
|
||||
- Not finalized, but planned: raise minimum required Android version from Android 7 (SDK level 24) to Android 8 (SDK level 26)
|
||||
- [ ] Implement predictive text support / spell checking
|
||||
- [ ] Add new extension type: Language Pack
|
||||
- Basically groups all locale-relevant data (predictive base model, emoji suggestion data, ...)
|
||||
in a dynamically importable extension file
|
||||
- [ ] Proper physical keyboard support (See https://github.com/florisboard/florisboard/issues/1972)
|
||||
- [ ] Rework cache manager (See https://github.com/florisboard/florisboard/issues/2870)
|
||||
|
||||
## k3lp
|
||||
|
||||
> [!NOTE]
|
||||
> The development of k3lp is not tied to a florisboard version and takes place on [codeberg.org](https://codeberg.org/k3lp/k3lp) simultaneously.
|
||||
|
||||
- [ ] New keyboard layout engine + file syntax based on the upcoming Unicode Keyboard v3 standard
|
||||
- [ ] Add Tablet mode / Optimizations for landscape input based on new keyboard layout engine
|
||||
|
||||
## 0.7+
|
||||
|
||||
> [!NOTE]
|
||||
> From 0.6 onwards we plan to have more stable 0.X releases but with at most one large feature per release, thus having a much quicker iteration of new features on the stable track, which is a benefit for everyone involved.
|
||||
|
||||
- [ ] Add floating keyboard mode
|
||||
- [ ] New text processing logic
|
||||
- [ ] Complete rework of the Emoji panel
|
||||
- [ ] Emoji search
|
||||
- [ ] Fully scrollable emoji list (soft category borders)
|
||||
- [ ] Side scrollable emoji list (swipe for next category)
|
||||
- [ ] More granular theming options
|
||||
- [ ] Layout customization (e.g. placement of category buttons)
|
||||
- [ ] Maybe: consider upgrading to emoji2 for better unified system-wide emoji styles
|
||||
- [ ] Reimplementation of glide typing with the new layout engine and predictive text core
|
||||
- [ ] Prepare FlorisBoard repository and app store presence for public beta release on Google Play (will go live with stable 0.7)
|
||||
- [ ] Rework branding images and texts of FlorisBoard for the app stores
|
||||
- [ ] Focus on stability and experience improvements of the app and keyboard
|
||||
- [ ] Add support for new features introduced with Android 15 / 16
|
||||
|
||||
## Backlog / Planned (unassigned)
|
||||
|
||||
**Features that MAY be added (even in versions mentioned above) or dismissed**
|
||||
|
||||
- Full on-board layout editor which allows users to create their own layouts without writing a JSON file
|
||||
- Theme rework part II
|
||||
- Adaptive themes v2
|
||||
- Voice-to-text with Mozilla's open-source voice service (or any other oss voice provider)
|
||||
- Text translation
|
||||
- Stickers/GIFs
|
||||
- Kaomoji panel implementation
|
||||
- FlorisBoard landing web page for presentation
|
||||
- Implementing additional layouts
|
||||
- Support for Tasker/Automate/MacroDroid plugins
|
||||
- Support for WearOS/Smartwatches
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayOutputStream
|
||||
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.agp.application)
|
||||
@@ -29,25 +28,29 @@ plugins {
|
||||
val projectMinSdk: String by project
|
||||
val projectTargetSdk: String by project
|
||||
val projectCompileSdk: String by project
|
||||
val projectBuildToolsVersion: String by project
|
||||
val projectNdkVersion: String by project
|
||||
val projectVersionCode: String by project
|
||||
val projectVersionName: String by project
|
||||
val projectVersionNameSuffix = projectVersionName.substringAfter("-", "")
|
||||
val projectVersionNameSuffix = projectVersionName.substringAfter("-", "").let { suffix ->
|
||||
if (suffix.isNotEmpty()) {
|
||||
"-$suffix"
|
||||
} else {
|
||||
suffix
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "dev.patrickgold.florisboard"
|
||||
compileSdk = projectCompileSdk.toInt()
|
||||
buildToolsVersion = projectBuildToolsVersion
|
||||
ndkVersion = projectNdkVersion
|
||||
buildToolsVersion = tools.versions.buildTools.get()
|
||||
ndkVersion = tools.versions.ndk.get()
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = "11"
|
||||
freeCompilerArgs = listOf(
|
||||
"-opt-in=kotlin.contracts.ExperimentalContracts",
|
||||
"-Xjvm-default=all-compatibility",
|
||||
@@ -149,7 +152,13 @@ android {
|
||||
}
|
||||
|
||||
aboutLibraries {
|
||||
configPath = "app/src/main/config"
|
||||
collect {
|
||||
configPath = file("src/main/config")
|
||||
}
|
||||
}
|
||||
|
||||
lint {
|
||||
baseline = file("lint.xml")
|
||||
}
|
||||
|
||||
testOptions {
|
||||
@@ -162,21 +171,21 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
composeCompiler {
|
||||
// DO NOT ENABLE STRONG SKIPPING! This project currently relies on
|
||||
// recomposition on parent state change to update the UI correctly.
|
||||
featureFlags.add(ComposeFeatureFlag.StrongSkipping.disabled())
|
||||
}
|
||||
|
||||
tasks.withType<Test> {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val composeBom = platform(libs.androidx.compose.bom)
|
||||
implementation(composeBom)
|
||||
// testImplementation(composeBom)
|
||||
// androidTestImplementation(composeBom)
|
||||
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.activity.ktx)
|
||||
implementation(libs.androidx.autofill)
|
||||
implementation(libs.androidx.collection.ktx)
|
||||
implementation(libs.androidx.compose.material.icons)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
implementation(libs.androidx.compose.runtime.livedata)
|
||||
implementation(libs.androidx.compose.ui)
|
||||
@@ -186,27 +195,30 @@ dependencies {
|
||||
implementation(libs.androidx.emoji2)
|
||||
implementation(libs.androidx.emoji2.views)
|
||||
implementation(libs.androidx.exifinterface)
|
||||
implementation(libs.androidx.material.icons)
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
implementation(libs.androidx.profileinstaller)
|
||||
ksp(libs.androidx.room.compiler)
|
||||
implementation(libs.androidx.room.runtime)
|
||||
implementation(libs.cache4k)
|
||||
implementation(libs.kotlin.reflect)
|
||||
implementation(libs.kotlinx.coroutines)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.mikepenz.aboutlibraries.core)
|
||||
implementation(libs.mikepenz.aboutlibraries.compose)
|
||||
implementation(libs.patrickgold.compose.tooltip)
|
||||
implementation(libs.patrickgold.jetpref.datastore.model)
|
||||
ksp(libs.patrickgold.jetpref.datastore.model.processor)
|
||||
implementation(libs.patrickgold.jetpref.datastore.ui)
|
||||
implementation(libs.patrickgold.jetpref.material.ui)
|
||||
|
||||
implementation(project(":lib:android"))
|
||||
implementation(project(":lib:color"))
|
||||
implementation(project(":lib:compose"))
|
||||
implementation(project(":lib:kotlin"))
|
||||
implementation(project(":lib:native"))
|
||||
implementation(project(":lib:snygg"))
|
||||
|
||||
testImplementation(libs.kotlin.test.junit5)
|
||||
androidTestImplementation(libs.androidx.test.ext)
|
||||
androidTestImplementation(libs.androidx.test.espresso.core)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<lint>
|
||||
<issue id="UsingMaterialAndMaterial3Libraries" severity="ignore" />
|
||||
<issue id="ExtraTranslation" severity="warning"/>
|
||||
</lint>
|
||||
|
||||
@@ -17,6 +17,13 @@
|
||||
"direction": "rtl",
|
||||
"modifier": "org.florisboard.layouts:arabic"
|
||||
},
|
||||
{
|
||||
"id": "armenian_alt_phonetic",
|
||||
"label": "Armenian Alt Phonetic",
|
||||
"authors": [ "MikayelB" ],
|
||||
"direction": "ltr",
|
||||
"modifier": "org.florisboard.layouts:armenian"
|
||||
},
|
||||
{
|
||||
"id": "western_armenian",
|
||||
"label": "Armenian (Western)",
|
||||
@@ -150,6 +157,13 @@
|
||||
"direction": "ltr",
|
||||
"modifier": "org.florisboard.layouts:dvorak"
|
||||
},
|
||||
{
|
||||
"id": "dvorak_se",
|
||||
"label": "Dvorak (SE)",
|
||||
"authors": [ "iceaway" ],
|
||||
"direction": "ltr",
|
||||
"modifier": "org.florisboard.layouts:dvorak_se"
|
||||
},
|
||||
{
|
||||
"id": "esperanto",
|
||||
"label": "Esperanto",
|
||||
@@ -503,6 +517,12 @@
|
||||
"authors": [ "msrd0" ],
|
||||
"direction": "ltr"
|
||||
},
|
||||
{
|
||||
"id": "dvorak_se",
|
||||
"label": "Dvorak (SE)",
|
||||
"authors": [ "iceaway" ],
|
||||
"direction": "ltr"
|
||||
},
|
||||
{
|
||||
"id": "hebrew",
|
||||
"label": "עברית",
|
||||
@@ -601,6 +621,12 @@
|
||||
"authors": [ "waelwindows" ],
|
||||
"direction": "ltr"
|
||||
},
|
||||
{
|
||||
"id": "czech",
|
||||
"label": "Czech",
|
||||
"authors": [ "bmondream" ],
|
||||
"direction": "ltr"
|
||||
},
|
||||
{
|
||||
"id": "devanagari",
|
||||
"label": "Devanagari",
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
[
|
||||
[
|
||||
{ "$": "auto_text_key", "code": 229, "label": "å" },
|
||||
{ "$": "auto_text_key", "code": 228, "label": "ä" },
|
||||
{ "$": "auto_text_key", "code": 246, "label": "ö" },
|
||||
{ "$": "auto_text_key", "code": 112, "label": "p" },
|
||||
{ "$": "auto_text_key", "code": 121, "label": "y" },
|
||||
{ "$": "auto_text_key", "code": 102, "label": "f" },
|
||||
{ "$": "auto_text_key", "code": 103, "label": "g" },
|
||||
{ "$": "auto_text_key", "code": 99, "label": "c" },
|
||||
{ "$": "auto_text_key", "code": 114, "label": "r" },
|
||||
{ "$": "auto_text_key", "code": 108, "label": "l" }
|
||||
],
|
||||
[
|
||||
{ "$": "auto_text_key", "code": 97, "label": "a" },
|
||||
{ "$": "auto_text_key", "code": 111, "label": "o" },
|
||||
{ "$": "auto_text_key", "code": 101, "label": "e" },
|
||||
{ "$": "auto_text_key", "code": 117, "label": "u" },
|
||||
{ "$": "auto_text_key", "code": 105, "label": "i" },
|
||||
{ "$": "auto_text_key", "code": 100, "label": "d" },
|
||||
{ "$": "auto_text_key", "code": 104, "label": "h" },
|
||||
{ "$": "auto_text_key", "code": 116, "label": "t" },
|
||||
{ "$": "auto_text_key", "code": 110, "label": "n" },
|
||||
{ "$": "auto_text_key", "code": 115, "label": "s" }
|
||||
],
|
||||
[
|
||||
{ "$": "auto_text_key", "code": 113, "label": "q" },
|
||||
{ "$": "auto_text_key", "code": 106, "label": "j" },
|
||||
{ "$": "auto_text_key", "code": 107, "label": "k" },
|
||||
{ "$": "auto_text_key", "code": 120, "label": "x" },
|
||||
{ "$": "auto_text_key", "code": 98, "label": "b" },
|
||||
{ "$": "auto_text_key", "code": 109, "label": "m" },
|
||||
{ "$": "auto_text_key", "code": 119, "label": "w" },
|
||||
{ "$": "auto_text_key", "code": 118, "label": "v" },
|
||||
{ "$": "auto_text_key", "code": 122, "label": "z" }
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
[
|
||||
[
|
||||
{ "code": -11, "label": "shift", "type": "modifier" },
|
||||
{ "code": 0, "type": "placeholder" },
|
||||
{ "code": -7, "label": "delete", "type": "enter_editing" }
|
||||
],
|
||||
[
|
||||
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
|
||||
{ "$": "auto_text_key", "code": 44, "label": "," },
|
||||
{ "code": -227, "label": "language_switch", "type": "system_gui" },
|
||||
{ "code": -212, "label": "ime_ui_mode_media", "type": "system_gui" },
|
||||
{ "code": 32, "label": "space" },
|
||||
{ "$": "auto_text_key", "code": 46, "label": "." },
|
||||
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
|
||||
]
|
||||
]
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 2535, "label": "১", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2536, "label": "২", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2537, "label": "৩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2538, "label": "৪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2539, "label": "৫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 2540, "label": "৬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2542, "label": "৮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2543, "label": "৯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2534, "label": "০", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
{ "code": 32902, "label": "肆" },
|
||||
{ "code": 18825, "label": "䦉" },
|
||||
{ "code": 20118, "label": "亖" },
|
||||
{ "code": 65300, "label": "3" }
|
||||
{ "code": 65300, "label": "4" }
|
||||
]
|
||||
} },
|
||||
{ "code": 20116, "label": "五", "type": "numeric", "popup": {
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
[
|
||||
[
|
||||
{ "$": "shift_state_selector",
|
||||
"default": {
|
||||
"code": 43, "label": "+", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" }
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "$": "shift_state_selector",
|
||||
"shiftedManual": {
|
||||
"code": 282, "label": "Ě", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" }
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"code": 283, "label": "ě", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" }
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "$": "shift_state_selector",
|
||||
"shiftedManual": {
|
||||
"code": 352, "label": "Š", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" }
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"code": 353, "label": "š", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" }
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "$": "shift_state_selector",
|
||||
"shiftedManual": {
|
||||
"code": 268, "label": "Č", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" }
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"code": 269, "label": "č", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" }
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "$": "shift_state_selector",
|
||||
"shiftedManual": {
|
||||
"code": 344, "label": "Ř", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" }
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"code": 345, "label": "ř", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" }
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "$": "shift_state_selector",
|
||||
"shiftedManual": {
|
||||
"code": 381, "label": "Ž", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" }
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"code": 382, "label": "ž", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" }
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "$": "shift_state_selector",
|
||||
"shiftedManual": {
|
||||
"code": 221, "label": "Ý", "type": "numeric", "popup": {
|
||||
"main": { "code": 55, "label": "7" }
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"code": 253, "label": "ý", "type": "numeric", "popup": {
|
||||
"main": { "code": 55, "label": "7" }
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "$": "shift_state_selector",
|
||||
"shiftedManual": {
|
||||
"code": 193, "label": "Á", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" }
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"code": 225, "label": "á", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" }
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "$": "shift_state_selector",
|
||||
"shiftedManual": {
|
||||
"code": 205, "label": "Í", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" }
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"code": 237, "label": "í", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" }
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "$": "shift_state_selector",
|
||||
"shiftedManual": {
|
||||
"code": 201, "label": "É", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" }
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"code": 233, "label": "é", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" }
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 2407, "label": "१", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2408, "label": "२", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2409, "label": "३", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2410, "label": "४", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2411, "label": "५", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 2412, "label": "६", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2414, "label": "८", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2415, "label": "९", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2406, "label": "०", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 1633, "label": "١", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1634, "label": "٢", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1635, "label": "٣", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1636, "label": "٤", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1637, "label": "٥", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 1638, "label": "٦", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1640, "label": "٨", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1641, "label": "٩", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1632, "label": "٠", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 2791, "label": "૧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2792, "label": "૨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2793, "label": "૩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2794, "label": "૪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2795, "label": "૫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 2796, "label": "૬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2798, "label": "૮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2799, "label": "૯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2790, "label": "૦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 2663, "label": "੧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2664, "label": "੨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2665, "label": "੩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2666, "label": "੪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2667, "label": "੫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 2668, "label": "੬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2670, "label": "੮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2671, "label": "੯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2662, "label": "੦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 3303, "label": "೧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3304, "label": "೨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3305, "label": "೩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3306, "label": "೪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3307, "label": "೫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 3308, "label": "೬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3310, "label": "೮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3311, "label": "೯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3302, "label": "೦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 3431, "label": "൧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3432, "label": "൨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3433, "label": "൩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3434, "label": "൪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3435, "label": "൫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 3436, "label": "൬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3438, "label": "൮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3439, "label": "൯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3430, "label": "൦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 2919, "label": "୧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2920, "label": "୨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2921, "label": "୩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2922, "label": "୪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2923, "label": "୫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 2924, "label": "୬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2926, "label": "୮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2927, "label": "୯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2918, "label": "୦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 1777, "label": "۱", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1778, "label": "۲", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1779, "label": "۳", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1780, "label": "۴", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1781, "label": "۵", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 1782, "label": "۶", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1784, "label": "۸", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1785, "label": "۹", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1776, "label": "۰", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 3047, "label": "௧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3048, "label": "௨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3049, "label": "௩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3050, "label": "௪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3051, "label": "௫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 3052, "label": "௬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3054, "label": "௮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3055, "label": "௯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3046, "label": "௦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 3175, "label": "౧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3176, "label": "౨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3177, "label": "౩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3178, "label": "౪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3179, "label": "౫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 3180, "label": "౬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3182, "label": "౮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3183, "label": "౯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3174, "label": "౦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,20 +3,23 @@
|
||||
{ "code": 3665, "label": "๑", "type": "numeric", "popup": {
|
||||
"main": { "code": 185, "label": "¹" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3666, "label": "๒", "type": "numeric", "popup": {
|
||||
"main": { "code": 178, "label": "²" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
@@ -24,44 +27,59 @@
|
||||
{ "code": 3667, "label": "๓", "type": "numeric", "popup": {
|
||||
"main": { "code": 179, "label": "³" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3668, "label": "๔", "type": "numeric", "popup": {
|
||||
"main": { "code": 8308, "label": "⁴" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3669, "label": "๕", "type": "numeric", "popup": {
|
||||
"main": { "code": 8309, "label": "⁵" },
|
||||
"relevant": [
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3670, "label": "๖", "type": "numeric", "popup": {
|
||||
"main": { "code": 8310, "label": "⁶" }
|
||||
"main": { "code": 8310, "label": "⁶" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3671, "label": "๗", "type": "numeric", "popup": {
|
||||
"main": { "code": 8311, "label": "⁷" },
|
||||
"relevant": [
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8542, "label": "⅞" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3672, "label": "๘", "type": "numeric", "popup": {
|
||||
"main": { "code": 8312, "label": "⁸" }
|
||||
"main": { "code": 8312, "label": "⁸" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3673, "label": "๙", "type": "numeric", "popup": {
|
||||
"main": { "code": 8313, "label": "⁹" }
|
||||
"main": { "code": 8313, "label": "⁹" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3664, "label": "๐", "type": "numeric", "popup": {
|
||||
"main": { "code": 8304, "label": "⁰" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 71905, "label": "𑣡", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71906, "label": "𑣢", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71907, "label": "𑣣", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71908, "label": "𑣤", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71909, "label": "𑣥", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 71910, "label": "𑣦", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71912, "label": "𑣨", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71913, "label": "𑣩", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71904, "label": "𑣠", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -10,15 +10,17 @@
|
||||
"code": 49, "label": "1", "type": "numeric", "popup": {
|
||||
"main": { "code": 185, "label": "¹" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -29,6 +31,7 @@
|
||||
"code": 50, "label": "2", "type": "numeric", "popup": {
|
||||
"main": { "code": 178, "label": "²" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
@@ -45,9 +48,10 @@
|
||||
"code": 51, "label": "3", "type": "numeric", "popup": {
|
||||
"main": { "code": 179, "label": "³" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -68,6 +72,7 @@
|
||||
"code": 52, "label": "4", "type": "numeric", "popup": {
|
||||
"main": { "code": 8308, "label": "⁴" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
}
|
||||
@@ -86,6 +91,7 @@
|
||||
"code": 53, "label": "5", "type": "numeric", "popup": {
|
||||
"main": { "code": 8309, "label": "⁵" },
|
||||
"relevant": [
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -105,7 +111,10 @@
|
||||
},
|
||||
"default": {
|
||||
"code": 54, "label": "6", "type": "numeric", "popup": {
|
||||
"main": { "code": 8310, "label": "⁶" }
|
||||
"main": { "code": 8310, "label": "⁶" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -115,6 +124,7 @@
|
||||
"code": 55, "label": "7", "type": "numeric", "popup": {
|
||||
"main": { "code": 8311, "label": "⁷" },
|
||||
"relevant": [
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8542, "label": "⅞" }
|
||||
]
|
||||
}
|
||||
@@ -132,7 +142,10 @@
|
||||
},
|
||||
"default": {
|
||||
"code": 56, "label": "8", "type": "numeric", "popup": {
|
||||
"main": { "code": 8312, "label": "⁸" }
|
||||
"main": { "code": 8312, "label": "⁸" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -160,7 +173,10 @@
|
||||
},
|
||||
"default": {
|
||||
"code": 57, "label": "9", "type": "numeric", "popup": {
|
||||
"main": { "code": 8313, "label": "⁹" }
|
||||
"main": { "code": 8313, "label": "⁹" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -191,7 +207,9 @@
|
||||
"main": { "code": 8304, "label": "⁰" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,17 @@
|
||||
{ "code": 45, "label": "-", "popup": {
|
||||
"main": { "code": 95, "label": "_" },
|
||||
"relevant": [
|
||||
{ "code": 8315, "label": "⁻" },
|
||||
{ "code": 8212, "label": "—" },
|
||||
{ "code": 8211, "label": "–" },
|
||||
{ "code": 183, "label": "·" }
|
||||
]
|
||||
} },
|
||||
{ "code": 43, "label": "+", "popup": {
|
||||
"main": { "code": 177, "label": "±" }
|
||||
"main": { "code": 177, "label": "±" },
|
||||
"relevant": [
|
||||
{ "code": 8314, "label": "⁺" }
|
||||
]
|
||||
} },
|
||||
{ "$": "layout_direction_selector",
|
||||
"ltr": { "code": 40, "label": "(", "popup": {
|
||||
|
||||
@@ -401,6 +401,16 @@
|
||||
"characters": "org.florisboard.layouts:qwertz"
|
||||
}
|
||||
},
|
||||
{
|
||||
"languageTag": "hy",
|
||||
"composer": "org.florisboard.composers:appender",
|
||||
"currencySet": "org.florisboard.currencysets:armenian_dram",
|
||||
"popupMapping": "org.florisboard.localization:hy",
|
||||
"preferred": {
|
||||
"characters": "org.florisboard.layouts:armenian_alt_phonetic",
|
||||
"symbols": "org.florisboard.layouts:armenian"
|
||||
}
|
||||
},
|
||||
{
|
||||
"languageTag": "hy",
|
||||
"composer": "org.florisboard.composers:appender",
|
||||
|
||||
@@ -70,8 +70,9 @@
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 1611, "label": "ً" },
|
||||
"main": { "code": 1567, "label": "؟" },
|
||||
"relevant": [
|
||||
{ "code": 1611, "label": "ً" },
|
||||
{ "code": 1622, "label": "ٖ" },
|
||||
{ "code": 1648, "label": "ٰ" },
|
||||
{ "code": 1619, "label": "ٓ" },
|
||||
|
||||
@@ -34,9 +34,7 @@
|
||||
]
|
||||
},
|
||||
"h": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 7717, "label": "ḥ" }
|
||||
]
|
||||
"main": { "$": "auto_text_key", "code": 7717, "label": "ḥ" }
|
||||
},
|
||||
"i": {
|
||||
"main": { "$": "auto_text_key", "code": 237, "label": "í" },
|
||||
@@ -49,13 +47,11 @@
|
||||
]
|
||||
},
|
||||
"l": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 7735, "label": "ḷ" }
|
||||
]
|
||||
"main": { "$": "auto_text_key", "code": 7735, "label": "ḷ" }
|
||||
},
|
||||
"n": {
|
||||
"main": { "$": "auto_text_key", "code": 241, "label": "ñ" },
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 241, "label": "ñ" },
|
||||
{ "$": "auto_text_key", "code": 324, "label": "ń" }
|
||||
]
|
||||
},
|
||||
@@ -73,9 +69,7 @@
|
||||
]
|
||||
},
|
||||
"r": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 691, "label": "ʳ" }
|
||||
]
|
||||
"main": { "$": "auto_text_key", "code": 691, "label": "ʳ" }
|
||||
},
|
||||
"s": {
|
||||
"relevant": [
|
||||
@@ -112,4 +106,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
"main": { "$": "auto_text_key" ,"code" : 237, "label": "í" }
|
||||
},
|
||||
"o": {
|
||||
"main": { "$": "auto_text_key", "code" : 246, "label": "ö" },
|
||||
"main": { "$": "auto_text_key", "code" : 243, "label": "ó" },
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code" : 243, "label": "ó" },
|
||||
{ "$": "auto_text_key", "code" : 246, "label": "ö" },
|
||||
{ "$": "auto_text_key", "code" : 337, "label": "ő" }
|
||||
]
|
||||
},
|
||||
@@ -22,9 +22,9 @@
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"main": { "$": "auto_text_key", "code" : 252, "label": "ü" },
|
||||
"main": { "$": "auto_text_key", "code" : 250, "label": "ú" },
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code" : 250, "label": "ú" },
|
||||
{ "$": "auto_text_key", "code" : 252, "label": "ü" },
|
||||
{ "$": "auto_text_key", "code" : 369, "label": "ű" }
|
||||
]
|
||||
},
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"--secondary": "#ff9800",
|
||||
"--secondary-variant": "#e65100",
|
||||
"--background": "#e0e0e0",
|
||||
"--background-variant": "#d0d0d0",
|
||||
"--surface": "#ffffff",
|
||||
"--surface-variant": "#f5f5f5",
|
||||
|
||||
@@ -27,13 +28,13 @@
|
||||
"--on-surface-variant": "#5f5f5f",
|
||||
|
||||
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
|
||||
"--shape-chip": "rounded-corner(50%, 50%, 50%, 50%)"
|
||||
},
|
||||
|
||||
"window": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"clip": "no"
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
|
||||
"key": {
|
||||
@@ -110,6 +111,7 @@
|
||||
"smartbar-extended-actions-toggle": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"smartbar-action-key": {
|
||||
@@ -120,23 +122,34 @@
|
||||
"smartbar-action-key:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
|
||||
"smartbar-actions-overflow": {
|
||||
"margin": "4dp"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"font-size": "14sp",
|
||||
"margin": "0dp 8dp 0dp 0dp",
|
||||
"shape": "rounded-corner(24dp, 24dp, 24dp, 24dp)"
|
||||
},
|
||||
"smartbar-action-tile": {
|
||||
"background": "transparent",
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"shape": "var(--shape)",
|
||||
"text-max-lines": "2",
|
||||
"margin": "4dp",
|
||||
"padding": "4dp",
|
||||
"shape": "rounded-corner(20%, 20%, 20%, 20%)",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-action-tile:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"shape": "circle()"
|
||||
"smartbar-action-tile-icon": {
|
||||
"font-size": "24sp",
|
||||
"margin": "0dp 0dp 0dp 8dp"
|
||||
},
|
||||
|
||||
"smartbar-actions-editor": {
|
||||
@@ -194,6 +207,10 @@
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-candidate-word-secondary-text": {
|
||||
"font-size": "8sp",
|
||||
"margin": "0dp 2dp 0dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-clip": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
@@ -208,6 +225,9 @@
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-candidate-clip-icon": {
|
||||
"margin": "0dp 0dp 4dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-spacer": {
|
||||
"foreground": "var(--spacer-color)"
|
||||
},
|
||||
@@ -229,31 +249,80 @@
|
||||
},
|
||||
"clipboard-subheader": {
|
||||
"font-size": "14sp",
|
||||
"margin": "6dp"
|
||||
"margin": "6dp 10dp 6dp 6dp"
|
||||
},
|
||||
"clipboard-content": {
|
||||
"padding": "10dp"
|
||||
"padding": "10dp 0dp"
|
||||
},
|
||||
"clipboard-filter-row": {
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"padding": "0dp 0dp 4dp 0dp",
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-filter-chip": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp 4dp 0dp 4dp",
|
||||
"padding": "8dp 4dp",
|
||||
"shape": "var(--shape-chip)"
|
||||
},
|
||||
"clipboard-filter-chip[state=`active`]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)"
|
||||
},
|
||||
"clipboard-filter-chip-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-grid": {
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-item": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "12dp 8dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp",
|
||||
"text-max-lines": "10",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-item[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-description": {
|
||||
"font-size": "12sp",
|
||||
"font-style": "italic"
|
||||
},
|
||||
"clipboard-item-popup": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup": {
|
||||
"clipboard-item-popup[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-timestamp": {
|
||||
"font-size": "11sp",
|
||||
"padding": "16dp 8dp"
|
||||
},
|
||||
"clipboard-item-actions": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup-action": {
|
||||
"clipboard-item-action": {
|
||||
"font-size": "16sp",
|
||||
"padding": "12dp"
|
||||
},
|
||||
"clipboard-item-action-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
@@ -378,5 +447,17 @@
|
||||
"text-align": "center",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"subtype-panel-list-item": {
|
||||
"font-size": "16sp",
|
||||
"padding": "16dp"
|
||||
},
|
||||
"subtype-panel-list-item-icon-leading": {
|
||||
"font-size": "24sp",
|
||||
"padding": "0dp 0dp 16dp 0dp"
|
||||
},
|
||||
"subtype-panel-list-item-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"--secondary": "#ff9800",
|
||||
"--secondary-variant": "#e65100",
|
||||
"--background": "#e0e0e0",
|
||||
"--background-variant": "#d0d0d0",
|
||||
"--surface": "#f0f0f0",
|
||||
"--surface-variant": "#ffffff",
|
||||
|
||||
@@ -27,13 +28,13 @@
|
||||
"--on-surface-variant": "#5f5f5f",
|
||||
|
||||
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
|
||||
"--shape-chip": "rounded-corner(50%, 50%, 50%, 50%)"
|
||||
},
|
||||
|
||||
"window": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"clip": "no"
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
|
||||
"key": {
|
||||
@@ -112,6 +113,7 @@
|
||||
"smartbar-extended-actions-toggle": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"smartbar-action-key": {
|
||||
@@ -126,28 +128,36 @@
|
||||
"smartbar-action-key:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
|
||||
"smartbar-actions-overflow": {
|
||||
"margin": "4dp"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"font-size": "14sp",
|
||||
"margin": "0dp 8dp 0dp 0dp",
|
||||
"shape": "rounded-corner(24dp, 24dp, 24dp, 24dp)"
|
||||
},
|
||||
"smartbar-action-tile": {
|
||||
"background": "transparent",
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"shape": "var(--shape)",
|
||||
"text-max-lines": "2",
|
||||
"margin": "4dp",
|
||||
"padding": "4dp",
|
||||
"shape": "rounded-corner(20%, 20%, 20%, 20%)",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-action-tile:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-action-tile:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"shape": "circle()"
|
||||
"smartbar-action-tile-icon": {
|
||||
"font-size": "24sp",
|
||||
"margin": "0dp 0dp 0dp 8dp"
|
||||
},
|
||||
|
||||
"smartbar-actions-editor": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
@@ -203,6 +213,10 @@
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-word-secondary-text": {
|
||||
"font-size": "8sp",
|
||||
"margin": "0dp 2dp 0dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-clip": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
@@ -217,13 +231,15 @@
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-clip-icon": {
|
||||
"margin": "0dp 0dp 4dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-spacer": {
|
||||
"foreground": "var(--surface)"
|
||||
},
|
||||
|
||||
"clipboard-header": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface)",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"clipboard-header-button": {
|
||||
@@ -239,32 +255,80 @@
|
||||
},
|
||||
"clipboard-subheader": {
|
||||
"font-size": "14sp",
|
||||
"margin": "6dp"
|
||||
"margin": "6dp 10dp 6dp 6dp"
|
||||
},
|
||||
"clipboard-content": {
|
||||
"padding": "10dp"
|
||||
"padding": "10dp 0dp"
|
||||
},
|
||||
"clipboard-filter-row": {
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"padding": "0dp 0dp 4dp 0dp",
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-filter-chip": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp 4dp 0dp 4dp",
|
||||
"padding": "8dp 4dp",
|
||||
"shape": "var(--shape-chip)"
|
||||
},
|
||||
"clipboard-filter-chip[state=`active`]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)"
|
||||
},
|
||||
"clipboard-filter-chip-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-grid": {
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-item": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "12dp 8dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
"shadow-elevation": "2dp",
|
||||
"text-max-lines": "10",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-item[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-description": {
|
||||
"font-size": "12sp",
|
||||
"font-style": "italic"
|
||||
},
|
||||
"clipboard-item-popup": {
|
||||
"background": "var(--surface-variant)",
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "12dp 8dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup-action": {
|
||||
"clipboard-item-popup[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-timestamp": {
|
||||
"font-size": "11sp",
|
||||
"padding": "16dp 8dp"
|
||||
},
|
||||
"clipboard-item-actions": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-action": {
|
||||
"font-size": "16sp",
|
||||
"padding": "12dp"
|
||||
},
|
||||
"clipboard-item-action-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
@@ -389,6 +453,17 @@
|
||||
"text-align": "center",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"subtype-panel-list-item": {
|
||||
"font-size": "16sp",
|
||||
"padding": "16dp"
|
||||
},
|
||||
"subtype-panel-list-item-icon-leading": {
|
||||
"font-size": "24sp",
|
||||
"padding": "0dp 0dp 16dp 0dp"
|
||||
},
|
||||
"subtype-panel-list-item-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"--secondary": "#f57c00",
|
||||
"--secondary-variant": "#e65100",
|
||||
"--background": "#212121",
|
||||
"--background-variant": "#313131",
|
||||
"--surface": "#424242",
|
||||
"--surface-variant": "#616161",
|
||||
"--popup-surface": "#757575",
|
||||
@@ -22,13 +23,13 @@
|
||||
"--on-surface-variant": "#a0a0a0",
|
||||
|
||||
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
|
||||
"--shape-chip": "rounded-corner(50%, 50%, 50%, 50%)"
|
||||
},
|
||||
|
||||
"window": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"clip": "no"
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
|
||||
"key": {
|
||||
@@ -91,6 +92,7 @@
|
||||
"key-popup-extended-indicator": {
|
||||
"font-size": "16sp"
|
||||
},
|
||||
|
||||
"smartbar": {
|
||||
"font-size": "18sp"
|
||||
},
|
||||
@@ -104,6 +106,7 @@
|
||||
"smartbar-extended-actions-toggle": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"smartbar-action-key": {
|
||||
@@ -114,23 +117,34 @@
|
||||
"smartbar-action-key:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
|
||||
"smartbar-actions-overflow": {
|
||||
"margin": "4dp"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"font-size": "14sp",
|
||||
"margin": "0dp 8dp 0dp 0dp",
|
||||
"shape": "rounded-corner(24dp, 24dp, 24dp, 24dp)"
|
||||
},
|
||||
"smartbar-action-tile": {
|
||||
"background": "transparent",
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"shape": "var(--shape)",
|
||||
"text-max-lines": "2",
|
||||
"margin": "4dp",
|
||||
"padding": "4dp",
|
||||
"shape": "rounded-corner(20%, 20%, 20%, 20%)",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-action-tile:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"shape": "circle()"
|
||||
"smartbar-action-tile-icon": {
|
||||
"font-size": "24sp",
|
||||
"margin": "0dp 0dp 0dp 8dp"
|
||||
},
|
||||
|
||||
"smartbar-actions-editor": {
|
||||
@@ -188,6 +202,10 @@
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-candidate-word-secondary-text": {
|
||||
"font-size": "8sp",
|
||||
"margin": "0dp 2dp 0dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-clip": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
@@ -202,6 +220,9 @@
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-candidate-clip-icon": {
|
||||
"margin": "0dp 0dp 4dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-spacer": {
|
||||
"foreground": "var(--spacer-color)"
|
||||
},
|
||||
@@ -223,31 +244,80 @@
|
||||
},
|
||||
"clipboard-subheader": {
|
||||
"font-size": "14sp",
|
||||
"margin": "6dp"
|
||||
"margin": "6dp 10dp 6dp 6dp"
|
||||
},
|
||||
"clipboard-content": {
|
||||
"padding": "10dp"
|
||||
"padding": "10dp 0dp"
|
||||
},
|
||||
"clipboard-filter-row": {
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"padding": "0dp 0dp 4dp 0dp",
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-filter-chip": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp 4dp 0dp 4dp",
|
||||
"padding": "8dp 4dp",
|
||||
"shape": "var(--shape-chip)"
|
||||
},
|
||||
"clipboard-filter-chip[state=`active`]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)"
|
||||
},
|
||||
"clipboard-filter-chip-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-grid": {
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-item": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "12dp 8dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp",
|
||||
"text-max-lines": "10",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-item[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-description": {
|
||||
"font-size": "12sp",
|
||||
"font-style": "italic"
|
||||
},
|
||||
"clipboard-item-popup": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup": {
|
||||
"clipboard-item-popup[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-timestamp": {
|
||||
"font-size": "11sp",
|
||||
"padding": "16dp 8dp"
|
||||
},
|
||||
"clipboard-item-actions": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup-action": {
|
||||
"clipboard-item-action": {
|
||||
"font-size": "16sp",
|
||||
"padding": "12dp"
|
||||
},
|
||||
"clipboard-item-action-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
@@ -371,5 +441,17 @@
|
||||
"text-align": "center",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"subtype-panel-list-item": {
|
||||
"font-size": "16sp",
|
||||
"padding": "16dp"
|
||||
},
|
||||
"subtype-panel-list-item-icon-leading": {
|
||||
"font-size": "24sp",
|
||||
"padding": "0dp 0dp 16dp 0dp"
|
||||
},
|
||||
"subtype-panel-list-item-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"--secondary": "#f57c00",
|
||||
"--secondary-variant": "#e65100",
|
||||
"--background": "#212121",
|
||||
"--background-variant": "#313131",
|
||||
"--surface": "#424242",
|
||||
"--surface-variant": "#616161",
|
||||
"--popup-surface": "#757575",
|
||||
@@ -22,13 +23,13 @@
|
||||
"--on-surface-variant": "#a0a0a0",
|
||||
|
||||
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
|
||||
"--shape-chip": "rounded-corner(50%, 50%, 50%, 50%)"
|
||||
},
|
||||
|
||||
"window": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"clip": "no"
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
|
||||
"key": {
|
||||
@@ -93,6 +94,7 @@
|
||||
"key-popup-extended-indicator": {
|
||||
"font-size": "16sp"
|
||||
},
|
||||
|
||||
"smartbar": {
|
||||
"font-size": "18sp"
|
||||
},
|
||||
@@ -106,6 +108,7 @@
|
||||
"smartbar-extended-actions-toggle": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"smartbar-action-key": {
|
||||
@@ -120,28 +123,36 @@
|
||||
"smartbar-action-key:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
|
||||
"smartbar-actions-overflow": {
|
||||
"margin": "4dp"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"font-size": "14sp",
|
||||
"margin": "0dp 8dp 0dp 0dp",
|
||||
"shape": "rounded-corner(24dp, 24dp, 24dp, 24dp)"
|
||||
},
|
||||
"smartbar-action-tile": {
|
||||
"background": "transparent",
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"shape": "var(--shape)",
|
||||
"text-max-lines": "2",
|
||||
"margin": "4dp",
|
||||
"padding": "4dp",
|
||||
"shape": "rounded-corner(20%, 20%, 20%, 20%)",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-action-tile:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-action-tile:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"shape": "circle()"
|
||||
"smartbar-action-tile-icon": {
|
||||
"font-size": "24sp",
|
||||
"margin": "0dp 0dp 0dp 8dp"
|
||||
},
|
||||
|
||||
"smartbar-actions-editor": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
@@ -197,6 +208,10 @@
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-word-secondary-text": {
|
||||
"font-size": "8sp",
|
||||
"margin": "0dp 2dp 0dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-clip": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
@@ -211,13 +226,15 @@
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-clip-icon": {
|
||||
"margin": "0dp 0dp 4dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-spacer": {
|
||||
"foreground": "var(--surface)"
|
||||
},
|
||||
|
||||
"clipboard-header": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface)",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"clipboard-header-button": {
|
||||
@@ -233,32 +250,80 @@
|
||||
},
|
||||
"clipboard-subheader": {
|
||||
"font-size": "14sp",
|
||||
"margin": "6dp"
|
||||
"margin": "6dp 10dp 6dp 6dp"
|
||||
},
|
||||
"clipboard-content": {
|
||||
"padding": "10dp"
|
||||
"padding": "10dp 0dp"
|
||||
},
|
||||
"clipboard-filter-row": {
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"padding": "0dp 0dp 4dp 0dp",
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-filter-chip": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp 4dp 0dp 4dp",
|
||||
"padding": "8dp 4dp",
|
||||
"shape": "var(--shape-chip)"
|
||||
},
|
||||
"clipboard-filter-chip[state=`active`]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)"
|
||||
},
|
||||
"clipboard-filter-chip-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-grid": {
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-item": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "12dp 8dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
"shadow-elevation": "2dp",
|
||||
"text-max-lines": "10",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-item[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-description": {
|
||||
"font-size": "12sp",
|
||||
"font-style": "italic"
|
||||
},
|
||||
"clipboard-item-popup": {
|
||||
"background": "var(--surface-variant)",
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "12dp 8dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup-action": {
|
||||
"clipboard-item-popup[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-timestamp": {
|
||||
"font-size": "11sp",
|
||||
"padding": "16dp 8dp"
|
||||
},
|
||||
"clipboard-item-actions": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-action": {
|
||||
"font-size": "16sp",
|
||||
"padding": "12dp"
|
||||
},
|
||||
"clipboard-item-action-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
@@ -382,5 +447,17 @@
|
||||
"text-align": "center",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"subtype-panel-list-item": {
|
||||
"font-size": "16sp",
|
||||
"padding": "16dp"
|
||||
},
|
||||
"subtype-panel-list-item-icon-leading": {
|
||||
"font-size": "24sp",
|
||||
"padding": "0dp 0dp 16dp 0dp"
|
||||
},
|
||||
"subtype-panel-list-item-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"--secondary": "#ff9800",
|
||||
"--secondary-variant": "#804c00",
|
||||
"--background": "#000000",
|
||||
"--background-variant": "#111111",
|
||||
"--surface": "#212121",
|
||||
"--surface-variant": "#3d3d3d",
|
||||
"--popup-surface": "#424242",
|
||||
@@ -22,13 +23,13 @@
|
||||
"--on-surface-variant": "#ffffff73",
|
||||
|
||||
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
|
||||
"--shape-chip": "rounded-corner(50%, 50%, 50%, 50%)"
|
||||
},
|
||||
|
||||
"window": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"clip": "no"
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
|
||||
"key": {
|
||||
@@ -91,6 +92,7 @@
|
||||
"key-popup-extended-indicator": {
|
||||
"font-size": "16sp"
|
||||
},
|
||||
|
||||
"smartbar": {
|
||||
"font-size": "18sp"
|
||||
},
|
||||
@@ -104,6 +106,7 @@
|
||||
"smartbar-extended-actions-toggle": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"smartbar-action-key": {
|
||||
@@ -118,23 +121,34 @@
|
||||
"smartbar-action-key:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
|
||||
"smartbar-actions-overflow": {
|
||||
"margin": "4dp"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"font-size": "14sp",
|
||||
"margin": "0dp 8dp 0dp 0dp",
|
||||
"shape": "rounded-corner(24dp, 24dp, 24dp, 24dp)"
|
||||
},
|
||||
"smartbar-action-tile": {
|
||||
"background": "transparent",
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"shape": "var(--shape)",
|
||||
"text-max-lines": "2",
|
||||
"margin": "4dp",
|
||||
"padding": "4dp",
|
||||
"shape": "rounded-corner(20%, 20%, 20%, 20%)",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-action-tile:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"shape": "circle()"
|
||||
"smartbar-action-tile-icon": {
|
||||
"font-size": "24sp",
|
||||
"margin": "0dp 0dp 0dp 8dp"
|
||||
},
|
||||
|
||||
"smartbar-actions-editor": {
|
||||
@@ -192,6 +206,10 @@
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-candidate-word-secondary-text": {
|
||||
"font-size": "8sp",
|
||||
"margin": "0dp 2dp 0dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-clip": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
@@ -206,6 +224,9 @@
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-candidate-clip-icon": {
|
||||
"margin": "0dp 0dp 4dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-spacer": {
|
||||
"foreground": "var(--spacer-color)"
|
||||
},
|
||||
@@ -227,31 +248,80 @@
|
||||
},
|
||||
"clipboard-subheader": {
|
||||
"font-size": "14sp",
|
||||
"margin": "6dp"
|
||||
"margin": "6dp 10dp 6dp 6dp"
|
||||
},
|
||||
"clipboard-content": {
|
||||
"padding": "10dp"
|
||||
"padding": "10dp 0dp"
|
||||
},
|
||||
"clipboard-filter-row": {
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"padding": "0dp 0dp 4dp 0dp",
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-filter-chip": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp 4dp 0dp 4dp",
|
||||
"padding": "8dp 4dp",
|
||||
"shape": "var(--shape-chip)"
|
||||
},
|
||||
"clipboard-filter-chip[state=`active`]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)"
|
||||
},
|
||||
"clipboard-filter-chip-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-grid": {
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-item": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "12dp 8dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp",
|
||||
"text-max-lines": "10",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-item[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-description": {
|
||||
"font-size": "12sp",
|
||||
"font-style": "italic"
|
||||
},
|
||||
"clipboard-item-popup": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup": {
|
||||
"clipboard-item-popup[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-timestamp": {
|
||||
"font-size": "11sp",
|
||||
"padding": "16dp 8dp"
|
||||
},
|
||||
"clipboard-item-actions": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup-action": {
|
||||
"clipboard-item-action": {
|
||||
"font-size": "16sp",
|
||||
"padding": "12dp"
|
||||
},
|
||||
"clipboard-item-action-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
@@ -375,5 +445,17 @@
|
||||
"text-align": "center",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"subtype-panel-list-item": {
|
||||
"font-size": "16sp",
|
||||
"padding": "16dp"
|
||||
},
|
||||
"subtype-panel-list-item-icon-leading": {
|
||||
"font-size": "24sp",
|
||||
"padding": "0dp 0dp 16dp 0dp"
|
||||
},
|
||||
"subtype-panel-list-item-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"--secondary": "#ff9800",
|
||||
"--secondary-variant": "#804c00",
|
||||
"--background": "#000000",
|
||||
"--background-variant": "#111111",
|
||||
"--surface": "#212121",
|
||||
"--surface-variant": "#3d3d3d",
|
||||
"--popup-surface": "#424242",
|
||||
@@ -22,13 +23,13 @@
|
||||
"--on-surface-variant": "#ffffff73",
|
||||
|
||||
"--shape": "rounded-corner(8dp, 8dp, 8dp, 8dp)",
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)"
|
||||
"--shape-variant": "rounded-corner(12dp, 12dp, 12dp, 12dp)",
|
||||
"--shape-chip": "rounded-corner(50%, 50%, 50%, 50%)"
|
||||
},
|
||||
|
||||
"window": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
"clip": "no"
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
|
||||
"key": {
|
||||
@@ -93,6 +94,7 @@
|
||||
"key-popup-extended-indicator": {
|
||||
"font-size": "16sp"
|
||||
},
|
||||
|
||||
"smartbar": {
|
||||
"font-size": "18sp"
|
||||
},
|
||||
@@ -106,6 +108,7 @@
|
||||
"smartbar-extended-actions-toggle": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface-variant)",
|
||||
"margin": "6dp",
|
||||
"shape": "circle()"
|
||||
},
|
||||
"smartbar-action-key": {
|
||||
@@ -120,28 +123,36 @@
|
||||
"smartbar-action-key:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
|
||||
"smartbar-actions-overflow": {
|
||||
"margin": "4dp"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)",
|
||||
"font-size": "14sp",
|
||||
"margin": "0dp 8dp 0dp 0dp",
|
||||
"shape": "rounded-corner(24dp, 24dp, 24dp, 24dp)"
|
||||
},
|
||||
"smartbar-action-tile": {
|
||||
"background": "transparent",
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "14sp",
|
||||
"shape": "var(--shape)",
|
||||
"text-max-lines": "2",
|
||||
"margin": "4dp",
|
||||
"padding": "4dp",
|
||||
"shape": "rounded-corner(20%, 20%, 20%, 20%)",
|
||||
"text-align": "center",
|
||||
"text-max-lines": "2",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"smartbar-action-tile:pressed": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)"
|
||||
},
|
||||
"smartbar-action-tile:disabled": {
|
||||
"foreground": "var(--on-background-disabled)"
|
||||
},
|
||||
"smartbar-actions-overflow-customize-button": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"shape": "circle()"
|
||||
"smartbar-action-tile-icon": {
|
||||
"font-size": "24sp",
|
||||
"margin": "0dp 0dp 0dp 8dp"
|
||||
},
|
||||
|
||||
"smartbar-actions-editor": {
|
||||
"background": "var(--background)",
|
||||
"foreground": "var(--on-background)",
|
||||
@@ -197,6 +208,10 @@
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-word-secondary-text": {
|
||||
"font-size": "8sp",
|
||||
"margin": "0dp 2dp 0dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-clip": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-background)",
|
||||
@@ -211,13 +226,15 @@
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-background)"
|
||||
},
|
||||
"smartbar-candidate-clip-icon": {
|
||||
"margin": "0dp 0dp 4dp 0dp"
|
||||
},
|
||||
"smartbar-candidate-spacer": {
|
||||
"foreground": "var(--surface)"
|
||||
},
|
||||
|
||||
"clipboard-header": {
|
||||
"background": "transparent",
|
||||
"foreground": "var(--on-surface)",
|
||||
"foreground": "var(--on-background)",
|
||||
"font-size": "16sp"
|
||||
},
|
||||
"clipboard-header-button": {
|
||||
@@ -233,32 +250,80 @@
|
||||
},
|
||||
"clipboard-subheader": {
|
||||
"font-size": "14sp",
|
||||
"margin": "6dp"
|
||||
"margin": "6dp 10dp 6dp 6dp"
|
||||
},
|
||||
"clipboard-content": {
|
||||
"padding": "10dp"
|
||||
"padding": "10dp 0dp"
|
||||
},
|
||||
"clipboard-filter-row": {
|
||||
"background": "var(--background-variant)",
|
||||
"foreground": "var(--on-background)",
|
||||
"padding": "0dp 0dp 4dp 0dp",
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-filter-chip": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp 4dp 0dp 4dp",
|
||||
"padding": "8dp 4dp",
|
||||
"shape": "var(--shape-chip)"
|
||||
},
|
||||
"clipboard-filter-chip[state=`active`]": {
|
||||
"background": "var(--primary)",
|
||||
"foreground": "var(--on-primary)"
|
||||
},
|
||||
"clipboard-filter-chip-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-grid": {
|
||||
"shape": "var(--shape-variant)"
|
||||
},
|
||||
"clipboard-item": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "12dp 8dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
"shadow-elevation": "2dp",
|
||||
"text-max-lines": "10",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"clipboard-item[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-description": {
|
||||
"font-size": "12sp",
|
||||
"font-style": "italic"
|
||||
},
|
||||
"clipboard-item-popup": {
|
||||
"background": "var(--surface-variant)",
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"font-size": "14sp",
|
||||
"margin": "4dp",
|
||||
"padding": "12dp 8dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-popup-action": {
|
||||
"clipboard-item-popup[type=`text`]": {
|
||||
"padding": "12dp 8dp"
|
||||
},
|
||||
"clipboard-item-timestamp": {
|
||||
"font-size": "11sp",
|
||||
"padding": "16dp 8dp"
|
||||
},
|
||||
"clipboard-item-actions": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
"margin": "4dp",
|
||||
"shape": "var(--shape-variant)",
|
||||
"shadow-elevation": "2dp"
|
||||
},
|
||||
"clipboard-item-action": {
|
||||
"font-size": "16sp",
|
||||
"padding": "12dp"
|
||||
},
|
||||
"clipboard-item-action-text": {
|
||||
"margin": "4dp 0dp 0dp 0dp"
|
||||
},
|
||||
"clipboard-clear-all-dialog": {
|
||||
"background": "var(--surface)",
|
||||
"foreground": "var(--on-surface)",
|
||||
@@ -382,5 +447,17 @@
|
||||
"text-align": "center",
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
},
|
||||
"subtype-panel-list-item": {
|
||||
"font-size": "16sp",
|
||||
"padding": "16dp"
|
||||
},
|
||||
"subtype-panel-list-item-icon-leading": {
|
||||
"font-size": "24sp",
|
||||
"padding": "0dp 0dp 16dp 0dp"
|
||||
},
|
||||
"subtype-panel-list-item-text": {
|
||||
"text-max-lines": "1",
|
||||
"text-overflow": "ellipsis"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,10 @@ import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Handler
|
||||
import android.util.Log
|
||||
import androidx.core.os.UserManagerCompat
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.clipboard.ClipboardManager
|
||||
import dev.patrickgold.florisboard.ime.core.SubtypeManager
|
||||
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
|
||||
@@ -40,7 +42,11 @@ 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.ext.ExtensionManager
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import dev.patrickgold.jetpref.datastore.runtime.initAndroid
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
|
||||
import org.florisboard.lib.kotlin.tryOrNull
|
||||
import org.florisboard.libnative.dummyAdd
|
||||
@@ -63,8 +69,9 @@ class FlorisApplication : Application() {
|
||||
}
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val mainHandler by lazy { Handler(mainLooper) }
|
||||
private val scope = CoroutineScope(Dispatchers.Default)
|
||||
val preferenceStoreLoaded = MutableStateFlow(false)
|
||||
|
||||
val cacheManager = lazy { CacheManager(this) }
|
||||
val clipboardManager = lazy { ClipboardManager(this) }
|
||||
@@ -80,7 +87,6 @@ class FlorisApplication : Application() {
|
||||
super.onCreate()
|
||||
FlorisApplicationReference = WeakReference(this)
|
||||
try {
|
||||
JetPref.configure(saveIntervalMs = 500)
|
||||
Flog.install(
|
||||
context = this,
|
||||
isFloggingEnabled = BuildConfig.DEBUG,
|
||||
@@ -108,7 +114,14 @@ class FlorisApplication : Application() {
|
||||
|
||||
fun init() {
|
||||
cacheDir?.deleteContentsRecursively()
|
||||
prefs.initializeBlocking(this)
|
||||
scope.launch {
|
||||
val result = FlorisPreferenceStore.initAndroid(
|
||||
context = this@FlorisApplication,
|
||||
datastoreName = FlorisPreferenceModel.NAME,
|
||||
)
|
||||
Log.i("PREFS", result.toString())
|
||||
preferenceStoreLoaded.value = true
|
||||
}
|
||||
extensionManager.value.init()
|
||||
clipboardManager.value.initializeForContext(this)
|
||||
DictionaryManager.init(this)
|
||||
|
||||
@@ -75,8 +75,8 @@ import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import dev.patrickgold.florisboard.app.FlorisAppActivity
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
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.SelectSubtypePanel
|
||||
@@ -102,7 +102,6 @@ import dev.patrickgold.florisboard.ime.text.TextInputLayout
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.ime.theme.WallpaperChangeReceiver
|
||||
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
|
||||
@@ -114,16 +113,19 @@ import dev.patrickgold.florisboard.lib.util.debugSummarize
|
||||
import dev.patrickgold.florisboard.lib.util.launchActivity
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import java.lang.ref.WeakReference
|
||||
import kotlinx.coroutines.flow.update
|
||||
import org.florisboard.lib.android.AndroidInternalR
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.isOrientationLandscape
|
||||
import org.florisboard.lib.android.isOrientationPortrait
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.android.systemServiceOrNull
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
import org.florisboard.lib.compose.ProvideLocalizedResources
|
||||
import org.florisboard.lib.kotlin.collectIn
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggButton
|
||||
import org.florisboard.lib.snygg.ui.SnyggRow
|
||||
import org.florisboard.lib.snygg.ui.SnyggSurfaceView
|
||||
import org.florisboard.lib.snygg.ui.SnyggText
|
||||
import org.florisboard.lib.snygg.ui.rememberSnyggThemeQuery
|
||||
|
||||
@@ -246,12 +248,12 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
}
|
||||
}
|
||||
}
|
||||
ims.showShortToast("Failed to find voice IME, do you have one installed?")
|
||||
ims.showShortToastSync("Failed to find voice IME, do you have one installed?")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val editorInstance by editorInstance()
|
||||
private val keyboardManager by keyboardManager()
|
||||
private val nlpManager by nlpManager()
|
||||
@@ -277,20 +279,23 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
super.onCreate()
|
||||
FlorisImeServiceReference = WeakReference(this)
|
||||
WindowCompat.setDecorFitsSystemWindows(window.window!!, false)
|
||||
subtypeManager.activeSubtypeFlow.collectLatestIn(lifecycleScope) { subtype ->
|
||||
subtypeManager.activeSubtypeFlow.collectIn(lifecycleScope) { subtype ->
|
||||
val config = Configuration(resources.configuration)
|
||||
if (prefs.localization.displayKeyboardLabelsInSubtypeLanguage.get()) {
|
||||
config.setLocale(subtype.primaryLocale.base)
|
||||
}
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
prefs.localization.displayKeyboardLabelsInSubtypeLanguage.observeForever { shouldSync ->
|
||||
prefs.localization.displayKeyboardLabelsInSubtypeLanguage.asFlow().collectIn(lifecycleScope) { shouldSync ->
|
||||
val config = Configuration(resources.configuration)
|
||||
if (shouldSync) {
|
||||
config.setLocale(subtypeManager.activeSubtype.primaryLocale.base)
|
||||
}
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
prefs.physicalKeyboard.showOnScreenKeyboard.asFlow().collectIn(lifecycleScope) {
|
||||
updateInputViewShown()
|
||||
}
|
||||
@Suppress("DEPRECATION") // We do not retrieve the wallpaper but only listen to changes
|
||||
registerReceiver(wallpaperChangeReceiver, IntentFilter(Intent.ACTION_WALLPAPER_CHANGED))
|
||||
}
|
||||
@@ -329,6 +334,11 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
return defaultExtractView
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
themeManager.configurationChangeCounter.update { it + 1 }
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
unregisterReceiver(wallpaperChangeReceiver)
|
||||
@@ -356,6 +366,13 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEvaluateInputViewShown(): Boolean {
|
||||
val config = resources.configuration
|
||||
return super.onEvaluateInputViewShown()
|
||||
|| config.keyboard == Configuration.KEYBOARD_NOKEYS
|
||||
|| prefs.physicalKeyboard.showOnScreenKeyboard.get()
|
||||
}
|
||||
|
||||
override fun onUpdateSelection(
|
||||
oldSelStart: Int,
|
||||
oldSelEnd: Int,
|
||||
@@ -398,7 +415,6 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
flogInfo(LogTopic.IMS_EVENTS)
|
||||
}
|
||||
isWindowShown = true
|
||||
themeManager.updateActiveTheme()
|
||||
inputFeedbackController.updateSystemPrefsState()
|
||||
}
|
||||
|
||||
@@ -562,7 +578,10 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
|
||||
@Composable
|
||||
private fun ImeUiWrapper() {
|
||||
ProvideLocalizedResources(resourcesContext) {
|
||||
ProvideLocalizedResources(
|
||||
resourcesContext,
|
||||
appName = R.string.app_name,
|
||||
) {
|
||||
ProvideKeyboardRowBaseHeight {
|
||||
CompositionLocalProvider(LocalInputFeedbackController provides inputFeedbackController) {
|
||||
FlorisImeTheme {
|
||||
@@ -588,6 +607,10 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
@Composable
|
||||
private fun ImeUi() {
|
||||
val state by keyboardManager.activeState.collectAsState()
|
||||
val attributes = mapOf(
|
||||
FlorisImeUi.Attr.Mode to state.keyboardMode.toString(),
|
||||
FlorisImeUi.Attr.ShiftState to state.inputShiftState.toString(),
|
||||
)
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
LaunchedEffect(layoutDirection) {
|
||||
keyboardManager.activeState.layoutDirection = layoutDirection
|
||||
@@ -595,7 +618,7 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
|
||||
SnyggBox(
|
||||
elementName = FlorisImeUi.Window.elementName,
|
||||
attributes = mapOf(FlorisImeUi.Attr.ShiftState to state.inputShiftState.attrName()),
|
||||
attributes = attributes,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
@@ -603,8 +626,19 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
clickAndSemanticsModifier = Modifier
|
||||
// Do not remove below line or touch input may get stuck
|
||||
.pointerInteropFilter { false },
|
||||
supportsBackgroundImage = true,
|
||||
supportsBackgroundImage = !AndroidVersion.ATLEAST_API30_R,
|
||||
allowClip = false,
|
||||
) {
|
||||
// The SurfaceView is used to render the background image under inline-autofill chips. These are only
|
||||
// available on Android >=11, and SurfaceView causes trouble on Android 8/9, thus we render the image
|
||||
// in the SurfaceView for Android >=11, and in the Compose View Tree for Android <=10.
|
||||
if (AndroidVersion.ATLEAST_API30_R) {
|
||||
SnyggSurfaceView(
|
||||
elementName = FlorisImeUi.Window.elementName,
|
||||
attributes = attributes,
|
||||
modifier = Modifier.matchParentSize(),
|
||||
)
|
||||
}
|
||||
val configuration = LocalConfiguration.current
|
||||
val bottomOffset by if (configuration.isOrientationPortrait()) {
|
||||
prefs.keyboard.bottomOffsetPortrait
|
||||
@@ -656,10 +690,13 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean =
|
||||
if (keyboardManager.onHardwareKeyDown(keyCode, event)) true
|
||||
else super.onKeyDown(keyCode, event)
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
return keyboardManager.onHardwareKeyDown(keyCode, event) || super.onKeyDown(keyCode, event)
|
||||
}
|
||||
|
||||
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
return keyboardManager.onHardwareKeyUp(keyCode, event) || super.onKeyUp(keyCode, event)
|
||||
}
|
||||
|
||||
private inner class ComposeInputView : AbstractComposeView(this) {
|
||||
init {
|
||||
@@ -694,7 +731,11 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val state by keyboardManager.activeState.collectAsState()
|
||||
|
||||
ProvideLocalizedResources(resourcesContext, forceLayoutDirection = LayoutDirection.Ltr) {
|
||||
ProvideLocalizedResources(
|
||||
resourcesContext,
|
||||
appName = R.string.app_name,
|
||||
forceLayoutDirection = LayoutDirection.Ltr,
|
||||
) {
|
||||
FlorisImeTheme {
|
||||
BottomSheetHostUi(
|
||||
isShowing = state.isBottomSheetShowing() || state.isSubtypeSelectionShowing(),
|
||||
@@ -746,7 +787,11 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
|
||||
@Composable
|
||||
fun Content() {
|
||||
ProvideLocalizedResources(resourcesContext, forceLayoutDirection = LayoutDirection.Ltr) {
|
||||
ProvideLocalizedResources(
|
||||
resourcesContext,
|
||||
appName = R.string.app_name,
|
||||
forceLayoutDirection = LayoutDirection.Ltr,
|
||||
) {
|
||||
FlorisImeTheme {
|
||||
val activeEditorInfo by editorInstance.activeInfoFlow.collectAsState()
|
||||
SnyggBox(FlorisImeUi.ExtractedLandscapeInputLayout.elementName) {
|
||||
@@ -754,7 +799,8 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
SnyggBox(FlorisImeUi.ExtractedLandscapeInputLayout.elementName,
|
||||
SnyggBox(
|
||||
elementName = FlorisImeUi.ExtractedLandscapeInputLayout.elementName,
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(1f),
|
||||
|
||||
@@ -20,7 +20,7 @@ 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.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
|
||||
import dev.patrickgold.florisboard.ime.nlp.SpellingLanguageMode
|
||||
@@ -33,7 +33,7 @@ import kotlinx.coroutines.runBlocking
|
||||
import org.florisboard.lib.kotlin.map
|
||||
|
||||
class FlorisSpellCheckerService : SpellCheckerService() {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val dictionaryManager get() = DictionaryManager.default()
|
||||
private val nlpManager by nlpManager()
|
||||
private val subtypeManager by subtypeManager()
|
||||
|
||||
@@ -20,9 +20,11 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import dev.patrickgold.florisboard.app.settings.theme.ColorPreferenceSerializer
|
||||
import dev.patrickgold.florisboard.app.settings.theme.DisplayKbdAfterDialogs
|
||||
import dev.patrickgold.florisboard.app.settings.theme.SnyggLevel
|
||||
import dev.patrickgold.florisboard.app.setup.NotificationPermissionState
|
||||
import dev.patrickgold.florisboard.ime.clipboard.CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO
|
||||
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.input.CapitalizationBehavior
|
||||
@@ -52,11 +54,13 @@ import dev.patrickgold.florisboard.ime.text.key.UtilityKeyAction
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeMode
|
||||
import dev.patrickgold.florisboard.ime.theme.extCoreTheme
|
||||
import dev.patrickgold.florisboard.lib.compose.ColorPreferenceSerializer
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
|
||||
import dev.patrickgold.florisboard.lib.observeAsTransformingState
|
||||
import dev.patrickgold.florisboard.lib.util.VersionName
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import dev.patrickgold.jetpref.datastore.annotations.Preferences
|
||||
import dev.patrickgold.jetpref.datastore.jetprefDataStoreOf
|
||||
import dev.patrickgold.jetpref.datastore.model.LocalTime
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceData
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceMigrationEntry
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceModel
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
@@ -66,9 +70,14 @@ import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.isOrientationPortrait
|
||||
import org.florisboard.lib.color.DEFAULT_GREEN
|
||||
|
||||
fun florisPreferenceModel() = JetPref.getOrCreatePreferenceModel(AppPrefs::class, ::AppPrefs)
|
||||
val FlorisPreferenceStore = jetprefDataStoreOf(FlorisPreferenceModel::class)
|
||||
|
||||
@Preferences
|
||||
abstract class FlorisPreferenceModel : PreferenceModel() {
|
||||
companion object {
|
||||
const val NAME = "florisboard-app-prefs"
|
||||
}
|
||||
|
||||
class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
val clipboard = Clipboard()
|
||||
inner class Clipboard {
|
||||
val useInternalClipboard = boolean(
|
||||
@@ -87,6 +96,23 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
key = "clipboard__history_enabled",
|
||||
default = false,
|
||||
)
|
||||
val numHistoryGridColumnsPortrait = int(
|
||||
key = "clipboard__num_history_grid_columns_portrait",
|
||||
default = CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO,
|
||||
)
|
||||
val numHistoryGridColumnsLandscape = int(
|
||||
key = "clipboard__num_history_grid_columns_landscape",
|
||||
default = CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO,
|
||||
)
|
||||
@Composable
|
||||
fun numHistoryGridColumns(): PreferenceData<Int> {
|
||||
val configuration = LocalConfiguration.current
|
||||
return if (configuration.isOrientationPortrait()) {
|
||||
numHistoryGridColumnsPortrait
|
||||
} else {
|
||||
numHistoryGridColumnsLandscape
|
||||
}
|
||||
}
|
||||
val cleanUpOld = boolean(
|
||||
key = "clipboard__clean_up_old",
|
||||
default = false,
|
||||
@@ -619,6 +645,14 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
)
|
||||
}
|
||||
|
||||
val physicalKeyboard = PhysicalKeyboard()
|
||||
inner class PhysicalKeyboard {
|
||||
val showOnScreenKeyboard = boolean(
|
||||
key = "physical_keyboard__show_on_screen_keyboard",
|
||||
default = false,
|
||||
)
|
||||
}
|
||||
|
||||
val smartbar = Smartbar()
|
||||
inner class Smartbar {
|
||||
val enabled = boolean(
|
||||
@@ -730,14 +764,14 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
},
|
||||
serializer = ColorPreferenceSerializer,
|
||||
)
|
||||
//val sunriseTime = localTime(
|
||||
// key = "theme__sunrise_time",
|
||||
// default = LocalTime.of(6, 0),
|
||||
//)
|
||||
//val sunsetTime = localTime(
|
||||
// key = "theme__sunset_time",
|
||||
// default = LocalTime.of(18, 0),
|
||||
//)
|
||||
val sunriseTime = localTime(
|
||||
key = "theme__sunrise_time",
|
||||
default = LocalTime(6, 0),
|
||||
)
|
||||
val sunsetTime = localTime(
|
||||
key = "theme__sunset_time",
|
||||
default = LocalTime(18, 0),
|
||||
)
|
||||
val editorColorRepresentation = enum(
|
||||
key = "theme__editor_color_representation",
|
||||
default = ColorRepresentation.HEX,
|
||||
@@ -800,31 +834,38 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
|
||||
//Migrate one hand mode prefs keep until: 0.7 dev cycle
|
||||
"keyboard__one_handed_mode" -> {
|
||||
if (entry.rawValue != "OFF") {
|
||||
val prefs by florisPreferenceModel()
|
||||
prefs.keyboard.oneHandedModeEnabled.set(true)
|
||||
entry.keepAsIs()
|
||||
} else {
|
||||
if (entry.rawValue == "OFF") {
|
||||
entry.reset()
|
||||
} else {
|
||||
entry.keepAsIs()
|
||||
}
|
||||
}
|
||||
"smartbar__action_arrangement" -> {
|
||||
fun migrateAction(action: QuickAction): QuickAction {
|
||||
return if (action is QuickAction.InsertKey && action.data.code == KeyCode.COMPACT_LAYOUT_TO_RIGHT) {
|
||||
action.copy(TextKeyData.TOGGLE_COMPACT_LAYOUT)
|
||||
} else {
|
||||
action
|
||||
}
|
||||
}
|
||||
|
||||
val arrangement = QuickActionJsonConfig.decodeFromString<QuickActionArrangement>(entry.rawValue)
|
||||
var newArrangement = arrangement.copy(
|
||||
dynamicActions = arrangement.dynamicActions.map { action ->
|
||||
if (action is QuickAction.InsertKey && action.data.code == KeyCode.COMPACT_LAYOUT_TO_RIGHT) {
|
||||
action.copy(TextKeyData.TOGGLE_COMPACT_LAYOUT)
|
||||
} else {
|
||||
action
|
||||
}
|
||||
}
|
||||
stickyAction = arrangement.stickyAction?.let{ migrateAction(it) },
|
||||
dynamicActions = arrangement.dynamicActions.map { migrateAction(it) },
|
||||
hiddenActions = arrangement.hiddenActions.map { migrateAction(it) },
|
||||
)
|
||||
if (QuickAction.InsertKey(TextKeyData.LANGUAGE_SWITCH) !in newArrangement) {
|
||||
newArrangement = newArrangement.copy(
|
||||
dynamicActions = newArrangement.dynamicActions.plus(QuickAction.InsertKey(TextKeyData.LANGUAGE_SWITCH))
|
||||
)
|
||||
}
|
||||
val json = QuickActionJsonConfig.encodeToString(newArrangement)
|
||||
if (QuickAction.InsertKey(TextKeyData.FORWARD_DELETE) !in newArrangement) {
|
||||
newArrangement = newArrangement.copy(
|
||||
dynamicActions = newArrangement.dynamicActions.plus(QuickAction.InsertKey(TextKeyData.FORWARD_DELETE))
|
||||
)
|
||||
}
|
||||
val json = QuickActionJsonConfig.encodeToString(newArrangement.distinct())
|
||||
entry.transform(rawValue = json)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,9 @@ import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
|
||||
import dev.patrickgold.florisboard.ime.input.CapitalizationBehavior
|
||||
import dev.patrickgold.florisboard.ime.input.HapticVibrationMode
|
||||
import dev.patrickgold.florisboard.ime.input.InputFeedbackActivationMode
|
||||
import dev.patrickgold.florisboard.ime.input.InputShiftState
|
||||
import dev.patrickgold.florisboard.ime.keyboard.IncognitoMode
|
||||
import dev.patrickgold.florisboard.ime.keyboard.KeyboardMode
|
||||
import dev.patrickgold.florisboard.ime.keyboard.SpaceBarMode
|
||||
import dev.patrickgold.florisboard.ime.landscapeinput.LandscapeInputUiMode
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistory
|
||||
@@ -40,10 +42,10 @@ import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyHintMode
|
||||
import dev.patrickgold.florisboard.ime.text.key.UtilityKeyAction
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeMode
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreferenceEntry
|
||||
import dev.patrickgold.jetpref.datastore.ui.listPrefEntries
|
||||
import dev.patrickgold.jetpref.material.ui.ColorRepresentation
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.kotlin.curlyFormat
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@@ -364,6 +366,58 @@ private val ENUM_DISPLAY_ENTRIES = mapOf<Pair<KClass<*>, String>, @Composable ()
|
||||
)
|
||||
}
|
||||
},
|
||||
InputShiftState::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = InputShiftState.UNSHIFTED,
|
||||
label = stringRes(R.string.enum__input_shift_state__unshifted),
|
||||
)
|
||||
entry(
|
||||
key = InputShiftState.SHIFTED_MANUAL,
|
||||
label = stringRes(R.string.enum__input_shift_state__shifted_manual),
|
||||
)
|
||||
entry(
|
||||
key = InputShiftState.SHIFTED_AUTOMATIC,
|
||||
label = stringRes(R.string.enum__input_shift_state__shifted_automatic),
|
||||
)
|
||||
entry(
|
||||
key = InputShiftState.CAPS_LOCK,
|
||||
label = stringRes(R.string.enum__input_shift_state__caps_lock),
|
||||
)
|
||||
}
|
||||
},
|
||||
KeyboardMode::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = KeyboardMode.CHARACTERS,
|
||||
label = stringRes(R.string.enum__keyboard_mode__characters),
|
||||
)
|
||||
entry(
|
||||
key = KeyboardMode.SYMBOLS,
|
||||
label = stringRes(R.string.enum__keyboard_mode__symbols),
|
||||
)
|
||||
entry(
|
||||
key = KeyboardMode.SYMBOLS2,
|
||||
label = stringRes(R.string.enum__keyboard_mode__symbols2),
|
||||
)
|
||||
entry(
|
||||
key = KeyboardMode.NUMERIC,
|
||||
label = stringRes(R.string.enum__keyboard_mode__numeric),
|
||||
)
|
||||
entry(
|
||||
key = KeyboardMode.NUMERIC_ADVANCED,
|
||||
label = stringRes(R.string.enum__keyboard_mode__numeric_advanced),
|
||||
)
|
||||
entry(
|
||||
key = KeyboardMode.PHONE,
|
||||
label = stringRes(R.string.enum__keyboard_mode__phone),
|
||||
)
|
||||
entry(
|
||||
key = KeyboardMode.PHONE2,
|
||||
label = stringRes(R.string.enum__keyboard_mode__phone2),
|
||||
)
|
||||
}
|
||||
},
|
||||
LandscapeInputUiMode::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
|
||||
@@ -39,26 +39,30 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.apptheme.FlorisAppTheme
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionImportScreenType
|
||||
import dev.patrickgold.florisboard.app.setup.NotificationPermissionState
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.cacheManager
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
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.conditional
|
||||
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.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.ProvideDefaultDialogPrefStrings
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.hideAppIcon
|
||||
import org.florisboard.lib.android.showAppIcon
|
||||
import org.florisboard.lib.compose.ProvideLocalizedResources
|
||||
import org.florisboard.lib.compose.conditional
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.kotlin.collectIn
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
enum class AppTheme(val id: String) {
|
||||
AUTO("auto"),
|
||||
@@ -73,7 +77,8 @@ val LocalNavController = staticCompositionLocalOf<NavController> {
|
||||
}
|
||||
|
||||
class FlorisAppActivity : ComponentActivity() {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val appContext by appContext()
|
||||
private val cacheManager by cacheManager()
|
||||
private var appTheme by mutableStateOf(AppTheme.AUTO)
|
||||
private var showAppIcon = true
|
||||
@@ -83,40 +88,43 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// Splash screen should be installed before calling super.onCreate()
|
||||
installSplashScreen().apply {
|
||||
setKeepOnScreenCondition { !prefs.datastoreReadyStatus.get() }
|
||||
setKeepOnScreenCondition { !appContext.preferenceStoreLoaded.value }
|
||||
}
|
||||
super.onCreate(savedInstanceState)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
prefs.other.settingsTheme.observe(this) {
|
||||
prefs.other.settingsTheme.asFlow().collectIn(lifecycleScope) {
|
||||
appTheme = it
|
||||
}
|
||||
prefs.other.settingsLanguage.observe(this) {
|
||||
prefs.other.settingsLanguage.asFlow().collectIn(lifecycleScope) {
|
||||
val config = Configuration(resources.configuration)
|
||||
val locale = if (it == "auto") FlorisLocale.default() else FlorisLocale.fromTag(it)
|
||||
config.setLocale(locale.base)
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
if (AndroidVersion.ATMOST_API28_P) {
|
||||
prefs.other.showAppIcon.observe(this) {
|
||||
prefs.other.showAppIcon.asFlow().collectIn(lifecycleScope) {
|
||||
showAppIcon = it
|
||||
}
|
||||
}
|
||||
|
||||
//Check if android 13+ is running and the NotificationPermission is not set
|
||||
if (AndroidVersion.ATLEAST_API33_T &&
|
||||
prefs.internal.notificationPermissionState.get() == NotificationPermissionState.NOT_SET
|
||||
) {
|
||||
// update pref value to show the setup screen again again
|
||||
prefs.internal.isImeSetUp.set(false)
|
||||
}
|
||||
|
||||
// We defer the setContent call until the datastore model is loaded, until then the splash screen stays drawn
|
||||
prefs.datastoreReadyStatus.observe(this) { isModelLoaded ->
|
||||
if (!isModelLoaded) return@observe
|
||||
val isModelLoaded = AtomicBoolean(false)
|
||||
appContext.preferenceStoreLoaded.collectIn(lifecycleScope) { loaded ->
|
||||
if (!loaded || isModelLoaded.getAndSet(true)) return@collectIn
|
||||
// Check if android 13+ is running and the NotificationPermission is not set
|
||||
if (AndroidVersion.ATLEAST_API33_T &&
|
||||
prefs.internal.notificationPermissionState.get() == NotificationPermissionState.NOT_SET
|
||||
) {
|
||||
// update pref value to show the setup screen again
|
||||
prefs.internal.isImeSetUp.set(false)
|
||||
}
|
||||
AppVersionUtils.updateVersionOnInstallAndLastUse(this, prefs)
|
||||
setContent {
|
||||
ProvideLocalizedResources(resourcesContext) {
|
||||
ProvideLocalizedResources(
|
||||
resourcesContext,
|
||||
appName = R.string.app_name,
|
||||
) {
|
||||
FlorisAppTheme(theme = appTheme) {
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
AppContent()
|
||||
@@ -189,7 +197,7 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
Routes.AppNavHost(
|
||||
modifier = Modifier.weight(1.0f),
|
||||
navController = navController,
|
||||
startDestination = if (isImeSetUp) Routes.Settings.Home else Routes.Setup.Screen,
|
||||
startDestination = if (isImeSetUp) Routes.Settings.Home::class else Routes.Setup.Screen::class,
|
||||
)
|
||||
PreviewKeyboardField(previewFieldController)
|
||||
}
|
||||
|
||||
@@ -17,12 +17,17 @@
|
||||
package dev.patrickgold.florisboard.app
|
||||
|
||||
import androidx.compose.animation.AnimatedContentScope
|
||||
import androidx.compose.animation.EnterTransition
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.animation.slideIn
|
||||
import androidx.compose.animation.slideOut
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.TransformOrigin
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
@@ -30,6 +35,7 @@ import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.navDeepLink
|
||||
import androidx.navigation.toRoute
|
||||
import dev.patrickgold.florisboard.app.devtools.AndroidLocalesScreen
|
||||
import dev.patrickgold.florisboard.app.devtools.AndroidSettingsScreen
|
||||
import dev.patrickgold.florisboard.app.devtools.DevtoolsScreen
|
||||
@@ -47,8 +53,9 @@ 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.OtherScreen
|
||||
import dev.patrickgold.florisboard.app.settings.advanced.BackupScreen
|
||||
import dev.patrickgold.florisboard.app.settings.advanced.OtherScreen
|
||||
import dev.patrickgold.florisboard.app.settings.advanced.PhysicalKeyboardScreen
|
||||
import dev.patrickgold.florisboard.app.settings.advanced.RestoreScreen
|
||||
import dev.patrickgold.florisboard.app.settings.clipboard.ClipboardScreen
|
||||
import dev.patrickgold.florisboard.app.settings.dictionary.DictionaryScreen
|
||||
@@ -69,112 +76,185 @@ 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 org.florisboard.lib.kotlin.curlyFormat
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class Deeplink(val path: String)
|
||||
|
||||
inline fun <reified T : Any> NavGraphBuilder.composableWithDeepLink(
|
||||
kClass: KClass<T>,
|
||||
noinline content: @Composable (AnimatedContentScope.(NavBackStackEntry) -> Unit),
|
||||
) {
|
||||
val deeplink = requireNotNull(kClass.annotations.firstOrNull { it is Deeplink } as? Deeplink) {
|
||||
"faulty class: $kClass with annotations ${kClass.annotations}"
|
||||
}
|
||||
composable<T>(
|
||||
deepLinks = listOf(navDeepLink<T>(basePath = "ui://florisboard/${deeplink.path}")),
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("FunctionName", "ConstPropertyName")
|
||||
object Routes {
|
||||
object Setup {
|
||||
const val Screen = "setup"
|
||||
@Serializable
|
||||
object Screen
|
||||
}
|
||||
|
||||
object Settings {
|
||||
const val Home = "settings"
|
||||
@Serializable
|
||||
@Deeplink("settings/home")
|
||||
object Home
|
||||
|
||||
const val Localization = "settings/localization"
|
||||
const val SelectLocale = "settings/localization/select-locale"
|
||||
const val LanguagePackManager = "settings/localization/language-pack-manage/{action}"
|
||||
fun LanguagePackManager(action: LanguagePackManagerScreenAction) =
|
||||
LanguagePackManager.curlyFormat("action" to action.id)
|
||||
const val SubtypeAdd = "settings/localization/subtype/add"
|
||||
const val SubtypeEdit = "settings/localization/subtype/edit/{id}"
|
||||
fun SubtypeEdit(id: Long) = SubtypeEdit.curlyFormat("id" to id)
|
||||
@Serializable
|
||||
@Deeplink("settings/localization")
|
||||
object Localization
|
||||
|
||||
const val Theme = "settings/theme"
|
||||
const val ThemeManager = "settings/theme/manage/{action}"
|
||||
fun ThemeManager(action: ThemeManagerScreenAction) = ThemeManager.curlyFormat("action" to action.id)
|
||||
@Serializable
|
||||
@Deeplink("settings/localization/select-locale")
|
||||
object SelectLocale
|
||||
|
||||
const val Keyboard = "settings/keyboard"
|
||||
const val InputFeedback = "settings/keyboard/input-feedback"
|
||||
@Serializable
|
||||
@Deeplink("settings/localization/language-pack-manage")
|
||||
data class LanguagePackManager(val action: LanguagePackManagerScreenAction)
|
||||
|
||||
const val Smartbar = "settings/smartbar"
|
||||
@Serializable
|
||||
@Deeplink("settings/localization/subtype/add")
|
||||
object SubtypeAdd
|
||||
|
||||
const val Typing = "settings/typing"
|
||||
@Serializable
|
||||
@Deeplink("settings/localization/subtype/edit")
|
||||
data class SubtypeEdit(val id: Long)
|
||||
|
||||
const val Dictionary = "settings/dictionary"
|
||||
const val UserDictionary = "settings/dictionary/user-dictionary/{type}"
|
||||
fun UserDictionary(type: UserDictionaryType) = UserDictionary.curlyFormat("type" to type.id)
|
||||
@Serializable
|
||||
@Deeplink("settings/theme")
|
||||
object Theme
|
||||
|
||||
const val Gestures = "settings/gestures"
|
||||
@Serializable
|
||||
@Deeplink("settings/theme/manage")
|
||||
data class ThemeManager(val action: ThemeManagerScreenAction)
|
||||
|
||||
const val Clipboard = "settings/clipboard"
|
||||
@Serializable
|
||||
@Deeplink("settings/keyboard")
|
||||
object Keyboard
|
||||
|
||||
const val Media = "settings/media"
|
||||
@Serializable
|
||||
@Deeplink("settings/keyboard/input-feedback")
|
||||
object InputFeedback
|
||||
|
||||
const val Other = "settings/other"
|
||||
const val Backup = "settings/other/backup"
|
||||
const val Restore = "settings/other/restore"
|
||||
@Serializable
|
||||
@Deeplink("settings/smartbar")
|
||||
object Smartbar
|
||||
|
||||
const val About = "settings/about"
|
||||
const val ProjectLicense = "settings/about/project-license"
|
||||
const val ThirdPartyLicenses = "settings/about/third-party-licenses"
|
||||
@Serializable
|
||||
@Deeplink("settings/typing")
|
||||
object Typing
|
||||
|
||||
@Serializable
|
||||
@Deeplink("settings/dictionary")
|
||||
object Dictionary
|
||||
|
||||
@Serializable
|
||||
@Deeplink("settings/dictionary/user-dictionary")
|
||||
data class UserDictionary(val type: UserDictionaryType)
|
||||
|
||||
@Serializable
|
||||
@Deeplink("settings/gestures")
|
||||
object Gestures
|
||||
|
||||
@Serializable
|
||||
@Deeplink("settings/clipboard")
|
||||
object Clipboard
|
||||
|
||||
@Serializable
|
||||
@Deeplink("settings/media")
|
||||
object Media
|
||||
|
||||
@Serializable
|
||||
@Deeplink("settings/other")
|
||||
object Other
|
||||
|
||||
@Serializable
|
||||
@Deeplink("settings/other/physical-keyboard")
|
||||
object PhysicalKeyboard
|
||||
|
||||
@Serializable
|
||||
@Deeplink("settings/other/backup")
|
||||
object Backup
|
||||
|
||||
@Serializable
|
||||
@Deeplink("settings/other/restore")
|
||||
object Restore
|
||||
|
||||
@Serializable
|
||||
@Deeplink("settings/about")
|
||||
object About
|
||||
|
||||
@Serializable
|
||||
@Deeplink("settings/about/project-license")
|
||||
object ProjectLicense
|
||||
|
||||
@Serializable
|
||||
@Deeplink("settings/about/third-party-licenses")
|
||||
object ThirdPartyLicenses
|
||||
}
|
||||
|
||||
object Devtools {
|
||||
const val Home = "devtools"
|
||||
@Serializable
|
||||
@Deeplink("devtools")
|
||||
object Home
|
||||
|
||||
const val AndroidLocales = "devtools/android/locales"
|
||||
const val AndroidSettings = "devtools/android/settings/{name}"
|
||||
fun AndroidSettings(name: String) = AndroidSettings.curlyFormat("name" to name)
|
||||
@Serializable
|
||||
@Deeplink("devtools/android/locales")
|
||||
object AndroidLocales
|
||||
|
||||
const val ExportDebugLog = "export-debug-log"
|
||||
@Serializable
|
||||
@Deeplink("devtools/android/settings")
|
||||
data class AndroidSettings(val name: String)
|
||||
|
||||
@Serializable
|
||||
@Deeplink("export-debug-log")
|
||||
object ExportDebugLog
|
||||
}
|
||||
|
||||
object Ext {
|
||||
const val Home = "ext"
|
||||
@Serializable
|
||||
@Deeplink("ext")
|
||||
object Home
|
||||
|
||||
const val List = "ext/list/{type}?showUpdate={showUpdate}"
|
||||
fun List(
|
||||
type: ExtensionListScreenType,
|
||||
showUpdate: Boolean
|
||||
) = List.curlyFormat("type" to type.id, "showUpdate" to showUpdate)
|
||||
@Serializable
|
||||
@Deeplink("ext/list")
|
||||
data class List(val type: ExtensionListScreenType, val showUpdate: Boolean? = null)
|
||||
|
||||
const val Edit = "ext/edit/{id}?create={serial_type}"
|
||||
fun Edit(id: String, serialType: String? = null): String {
|
||||
return Edit.curlyFormat("id" to id, "serial_type" to (serialType ?: ""))
|
||||
}
|
||||
@Serializable
|
||||
@Deeplink("ext/edit")
|
||||
data class Edit(val id: String, @SerialName("create") val serialType: String? = null)
|
||||
|
||||
const val Export = "ext/export/{id}"
|
||||
fun Export(id: String) = Export.curlyFormat("id" to id)
|
||||
@Serializable
|
||||
@Deeplink("ext/export")
|
||||
data class Export(val id: String)
|
||||
|
||||
const val Import = "ext/import/{type}?uuid={uuid}"
|
||||
fun Import(
|
||||
type: ExtensionImportScreenType,
|
||||
uuid: String?,
|
||||
) = Import.curlyFormat("type" to type.id, "uuid" to uuid.toString())
|
||||
@Serializable
|
||||
@Deeplink("ext/import")
|
||||
data class Import(val type: ExtensionImportScreenType, val uuid: String? = null)
|
||||
|
||||
const val View = "ext/view/{id}"
|
||||
fun View(id: String) = View.curlyFormat("id" to id)
|
||||
@Serializable
|
||||
@Deeplink("ext/view")
|
||||
data class View(val id: String)
|
||||
|
||||
const val CheckUpdates = "ext/check-updates"
|
||||
@Serializable
|
||||
@Deeplink("ext/check-updates")
|
||||
object CheckUpdates
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AppNavHost(
|
||||
modifier: Modifier,
|
||||
navController: NavHostController,
|
||||
startDestination: String,
|
||||
startDestination: KClass<*>,
|
||||
) {
|
||||
fun NavGraphBuilder.composableWithDeepLink(
|
||||
route: String,
|
||||
content: @Composable (AnimatedContentScope.(NavBackStackEntry) -> Unit),
|
||||
) {
|
||||
composable(
|
||||
route = route,
|
||||
deepLinks = listOf(navDeepLink { uriPattern = "ui://florisboard/$route" }),
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
NavHost(
|
||||
modifier = modifier,
|
||||
navController = navController,
|
||||
@@ -185,108 +265,103 @@ object Routes {
|
||||
exitTransition = {
|
||||
slideOut { IntOffset(-it.width, 0) } + fadeOut()
|
||||
},
|
||||
popEnterTransition = {
|
||||
slideIn { IntOffset(-it.width, 0) } + fadeIn()
|
||||
},
|
||||
popEnterTransition = { EnterTransition.None },
|
||||
popExitTransition = {
|
||||
slideOut { IntOffset(it.width, 0) } + fadeOut()
|
||||
}
|
||||
scaleOut(
|
||||
targetScale = 0.85F,
|
||||
transformOrigin = TransformOrigin(pivotFractionX = 0.8f, pivotFractionY = 0.5f)
|
||||
) + fadeOut(spring(stiffness = Spring.StiffnessMedium))
|
||||
},
|
||||
) {
|
||||
composable(Setup.Screen) { SetupScreen() }
|
||||
composable<Setup.Screen> { SetupScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.Home) { HomeScreen() }
|
||||
composableWithDeepLink(Settings.Home::class) { HomeScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.Localization) { LocalizationScreen() }
|
||||
composableWithDeepLink(Settings.SelectLocale) { SelectLocaleScreen() }
|
||||
composableWithDeepLink(Settings.LanguagePackManager) { navBackStack ->
|
||||
val action = navBackStack.arguments?.getString("action")?.let { actionId ->
|
||||
LanguagePackManagerScreenAction.entries.firstOrNull { it.id == actionId }
|
||||
}
|
||||
LanguagePackManagerScreen(action)
|
||||
composableWithDeepLink(Settings.Localization::class) { LocalizationScreen() }
|
||||
composableWithDeepLink(Settings.SelectLocale::class) { SelectLocaleScreen() }
|
||||
composableWithDeepLink(Settings.LanguagePackManager::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Settings.LanguagePackManager>()
|
||||
LanguagePackManagerScreen(payload.action)
|
||||
}
|
||||
composableWithDeepLink(Settings.SubtypeAdd) { SubtypeEditorScreen(null) }
|
||||
composableWithDeepLink(Settings.SubtypeEdit) { navBackStack ->
|
||||
val id = navBackStack.arguments?.getString("id")?.toLongOrNull()
|
||||
SubtypeEditorScreen(id)
|
||||
composableWithDeepLink(Settings.SubtypeAdd::class) { SubtypeEditorScreen(null) }
|
||||
composableWithDeepLink(Settings.SubtypeEdit::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Settings.SubtypeEdit>()
|
||||
SubtypeEditorScreen(payload.id)
|
||||
}
|
||||
|
||||
composableWithDeepLink(Settings.Theme) { ThemeScreen() }
|
||||
composableWithDeepLink(Settings.ThemeManager) { navBackStack ->
|
||||
val action = navBackStack.arguments?.getString("action")?.let { actionId ->
|
||||
ThemeManagerScreenAction.entries.firstOrNull { it.id == actionId }
|
||||
}
|
||||
ThemeManagerScreen(action)
|
||||
composableWithDeepLink(Settings.Theme::class) { ThemeScreen() }
|
||||
composableWithDeepLink(Settings.ThemeManager::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Settings.ThemeManager>()
|
||||
ThemeManagerScreen(payload.action)
|
||||
}
|
||||
|
||||
composableWithDeepLink(Settings.Keyboard) { KeyboardScreen() }
|
||||
composableWithDeepLink(Settings.InputFeedback) { InputFeedbackScreen() }
|
||||
composableWithDeepLink(Settings.Keyboard::class) { KeyboardScreen() }
|
||||
composableWithDeepLink(Settings.InputFeedback::class) { InputFeedbackScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.Smartbar) { SmartbarScreen() }
|
||||
composableWithDeepLink(Settings.Smartbar::class) { SmartbarScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.Typing) { TypingScreen() }
|
||||
composableWithDeepLink(Settings.Typing::class) { TypingScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.Dictionary) { DictionaryScreen() }
|
||||
composableWithDeepLink(Settings.UserDictionary) { navBackStack ->
|
||||
val type = navBackStack.arguments?.getString("type")?.let { typeId ->
|
||||
UserDictionaryType.entries.firstOrNull { it.id == typeId }
|
||||
}
|
||||
UserDictionaryScreen(type!!)
|
||||
composableWithDeepLink(Settings.Dictionary::class) { DictionaryScreen() }
|
||||
composableWithDeepLink(Settings.UserDictionary::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Settings.UserDictionary>()
|
||||
UserDictionaryScreen(payload.type)
|
||||
}
|
||||
|
||||
composableWithDeepLink(Settings.Gestures) { GesturesScreen() }
|
||||
composableWithDeepLink(Settings.Gestures::class) { GesturesScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.Clipboard) { ClipboardScreen() }
|
||||
composableWithDeepLink(Settings.Clipboard::class) { ClipboardScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.Media) { MediaScreen() }
|
||||
composableWithDeepLink(Settings.Media::class) { MediaScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.Other) { OtherScreen() }
|
||||
composableWithDeepLink(Settings.Backup) { BackupScreen() }
|
||||
composableWithDeepLink(Settings.Restore) { RestoreScreen() }
|
||||
composableWithDeepLink(Settings.Other::class) { OtherScreen() }
|
||||
composableWithDeepLink(Settings.PhysicalKeyboard::class) { PhysicalKeyboardScreen() }
|
||||
composableWithDeepLink(Settings.Backup::class) { BackupScreen() }
|
||||
composableWithDeepLink(Settings.Restore::class) { RestoreScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.About) { AboutScreen() }
|
||||
composableWithDeepLink(Settings.ProjectLicense) { ProjectLicenseScreen() }
|
||||
composableWithDeepLink(Settings.ThirdPartyLicenses) { ThirdPartyLicensesScreen() }
|
||||
composableWithDeepLink(Settings.About::class) { AboutScreen() }
|
||||
composableWithDeepLink(Settings.ProjectLicense::class) { ProjectLicenseScreen() }
|
||||
composableWithDeepLink(Settings.ThirdPartyLicenses::class) { ThirdPartyLicensesScreen() }
|
||||
|
||||
composableWithDeepLink(Devtools.Home) { DevtoolsScreen() }
|
||||
composableWithDeepLink(Devtools.AndroidLocales) { AndroidLocalesScreen() }
|
||||
composableWithDeepLink(Devtools.AndroidSettings) { navBackStack ->
|
||||
val name = navBackStack.arguments?.getString("name")
|
||||
AndroidSettingsScreen(name)
|
||||
composableWithDeepLink(Devtools.Home::class) { DevtoolsScreen() }
|
||||
composableWithDeepLink(Devtools.AndroidLocales::class) { AndroidLocalesScreen() }
|
||||
composableWithDeepLink(Devtools.AndroidSettings::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Devtools.AndroidSettings>()
|
||||
AndroidSettingsScreen(payload.name)
|
||||
}
|
||||
composableWithDeepLink(Devtools.ExportDebugLog) { ExportDebugLogScreen() }
|
||||
composableWithDeepLink(Devtools.ExportDebugLog::class) { ExportDebugLogScreen() }
|
||||
|
||||
composableWithDeepLink(Ext.Home) { ExtensionHomeScreen() }
|
||||
composableWithDeepLink(Ext.List) { navBackStack ->
|
||||
val type = navBackStack.arguments?.getString("type")?.let { typeId ->
|
||||
ExtensionListScreenType.entries.firstOrNull { it.id == typeId }
|
||||
} ?: error("unknown type")
|
||||
val showUpdate = navBackStack.arguments?.getString("showUpdate")
|
||||
ExtensionListScreen(type, showUpdate == "true")
|
||||
composableWithDeepLink(Ext.Home::class) { ExtensionHomeScreen() }
|
||||
composableWithDeepLink(Ext.List::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Ext.List>()
|
||||
val showUpdate = payload.showUpdate != null && payload.showUpdate
|
||||
ExtensionListScreen(payload.type, showUpdate)
|
||||
}
|
||||
composableWithDeepLink(Ext.Edit) { navBackStack ->
|
||||
val extensionId = navBackStack.arguments?.getString("id")
|
||||
val serialType = navBackStack.arguments?.getString("serial_type")
|
||||
composableWithDeepLink(Ext.Edit::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Ext.Edit>()
|
||||
val extensionId = payload.id
|
||||
val serialType = payload.serialType
|
||||
ExtensionEditScreen(
|
||||
id = extensionId.toString(),
|
||||
id = extensionId,
|
||||
createSerialType = serialType.takeIf { !it.isNullOrBlank() },
|
||||
)
|
||||
}
|
||||
composableWithDeepLink(Ext.Export) { navBackStack ->
|
||||
val extensionId = navBackStack.arguments?.getString("id")
|
||||
ExtensionExportScreen(id = extensionId.toString())
|
||||
composableWithDeepLink(Ext.Export::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Ext.Export>()
|
||||
val extensionId = payload.id
|
||||
ExtensionExportScreen(id = extensionId)
|
||||
}
|
||||
composableWithDeepLink(Ext.Import) { navBackStack ->
|
||||
val type = navBackStack.arguments?.getString("type")?.let { typeId ->
|
||||
ExtensionImportScreenType.entries.firstOrNull { it.id == typeId }
|
||||
} ?: ExtensionImportScreenType.EXT_ANY
|
||||
val uuid = navBackStack.arguments?.getString("uuid")?.takeIf { it != "null" }
|
||||
ExtensionImportScreen(type, uuid)
|
||||
composableWithDeepLink(Ext.Import::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Ext.Import>()
|
||||
val uuid = payload.uuid
|
||||
ExtensionImportScreen(payload.type, uuid)
|
||||
}
|
||||
composableWithDeepLink(Ext.View) { navBackStack ->
|
||||
val extensionId = navBackStack.arguments?.getString("id")
|
||||
ExtensionViewScreen(id = extensionId.toString())
|
||||
composableWithDeepLink(Ext.View::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Ext.View>()
|
||||
val extensionId = payload.id
|
||||
ExtensionViewScreen(id = extensionId)
|
||||
}
|
||||
composableWithDeepLink(Ext.CheckUpdates) {
|
||||
composableWithDeepLink(Ext.CheckUpdates::class) {
|
||||
CheckUpdatesScreen()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,8 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
import dev.patrickgold.florisboard.app.AppTheme
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.color.ColorMappings
|
||||
|
||||
/*private val AmoledDarkColorPalette = darkColorScheme(
|
||||
@@ -79,7 +78,7 @@ fun getColorScheme(
|
||||
context: Context,
|
||||
theme: AppTheme,
|
||||
): ColorScheme {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val accentColor by prefs.other.accentColor.observeAsState()
|
||||
val isDark = isSystemInDarkTheme()
|
||||
|
||||
|
||||
@@ -32,11 +32,11 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.compose.FlorisIconButton
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import java.util.Locale
|
||||
@@ -66,9 +66,9 @@ fun AndroidLocalesScreen() = FlorisScreen {
|
||||
out.appendLine()
|
||||
}
|
||||
}
|
||||
context.showLongToast("Exported available system locales to \"${txtFile.path}\"")
|
||||
context.showLongToastSync("Exported available system locales to \"${txtFile.path}\"")
|
||||
} catch (e: Exception) {
|
||||
context.showLongToast(
|
||||
context.showLongToastSync(
|
||||
R.string.error__snackbar_message_template,
|
||||
"error_message" to e.message.toString(),
|
||||
)
|
||||
|
||||
@@ -27,11 +27,11 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import org.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
|
||||
import org.florisboard.lib.android.AndroidSettings
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
fun AndroidSettingsScreen(name: String?) = FlorisScreen {
|
||||
|
||||
@@ -29,7 +29,6 @@ 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
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -40,7 +39,8 @@ 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.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.ime.keyboard.CachedLayout
|
||||
@@ -53,10 +53,10 @@ import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.florisboard.nlpManager
|
||||
import dev.patrickgold.florisboard.themeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.snygg.SnyggMissingSchemaException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.snygg.SnyggMissingSchemaException
|
||||
|
||||
private val CardBackground = Color.Black.copy(0.6f)
|
||||
private val DateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss", FlorisLocale.default().base)
|
||||
@@ -64,7 +64,8 @@ private val DateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss", FlorisLocale.
|
||||
@Composable
|
||||
fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
val context = LocalContext.current
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val appContext by context.appContext()
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val themeManager by context.themeManager()
|
||||
|
||||
@@ -73,9 +74,10 @@ fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
val showInputStateOverlay by prefs.devtools.showInputStateOverlay.observeAsState()
|
||||
val showSpellingOverlay by prefs.devtools.showSpellingOverlay.observeAsState()
|
||||
val showInlineAutofillOverlay by prefs.devtools.showInlineAutofillOverlay.observeAsState()
|
||||
val prefsLoaded by appContext.preferenceStoreLoaded.collectAsState()
|
||||
|
||||
val debugLayoutResult by keyboardManager.layoutManager.debugLayoutComputationResultFlow.collectAsState()
|
||||
val themeInfo by themeManager.activeThemeInfo.observeAsState()
|
||||
val themeInfo by themeManager.activeThemeInfo.collectAsState()
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides Color.White,
|
||||
@@ -97,8 +99,8 @@ fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
if (devtoolsEnabled && showInlineAutofillOverlay && AndroidVersion.ATLEAST_API30_R) {
|
||||
DevtoolsInlineAutofillOverlay()
|
||||
}
|
||||
val loadFailure = themeInfo?.loadFailure
|
||||
if (loadFailure != null) {
|
||||
val loadFailure = themeInfo.loadFailure
|
||||
if (loadFailure != null && prefsLoaded) {
|
||||
DevtoolsStylesheetFailedToLoadOverlay(loadFailure)
|
||||
}
|
||||
}
|
||||
@@ -163,13 +165,13 @@ private fun DevtoolsLastLayoutComputationOverlay(debugLayoutResult: DebugLayoutC
|
||||
return@DevtoolsOverlayBox
|
||||
}
|
||||
DevtoolsSubGroup(title = "main") {
|
||||
PrintResult(debugLayoutResult!!.main)
|
||||
PrintResult(debugLayoutResult.main)
|
||||
}
|
||||
DevtoolsSubGroup(title = "mod") {
|
||||
PrintResult(debugLayoutResult!!.mod)
|
||||
PrintResult(debugLayoutResult.mod)
|
||||
}
|
||||
DevtoolsSubGroup(title = "ext") {
|
||||
PrintResult(debugLayoutResult!!.ext)
|
||||
PrintResult(debugLayoutResult.ext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
@@ -27,16 +28,19 @@ import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
|
||||
import dev.patrickgold.florisboard.ime.dictionary.FlorisUserDictionaryDatabase
|
||||
import org.florisboard.lib.android.AndroidSettings
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionArrangement
|
||||
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
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import org.florisboard.lib.android.AndroidSettings
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
|
||||
class DebugOnPurposeCrashException : Exception(
|
||||
"Success! The app crashed purposely to display this beautiful screen we all love :)"
|
||||
@@ -50,6 +54,7 @@ fun DevtoolsScreen() = FlorisScreen {
|
||||
val context = LocalContext.current
|
||||
val navController = LocalNavController.current
|
||||
val extensionManager by context.extensionManager()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val (showDialog, setShowDialog) = remember { mutableStateOf(false) }
|
||||
|
||||
@@ -104,10 +109,21 @@ fun DevtoolsScreen() = FlorisScreen {
|
||||
onClick = { setShowDialog(true) },
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
)
|
||||
Preference(
|
||||
title = stringRes(R.string.devtools__reset_quick_actions_to_default__label),
|
||||
summary = stringRes(R.string.devtools__reset_quick_actions_to_default__summary),
|
||||
onClick = {
|
||||
scope.launch {
|
||||
prefs.smartbar.actionArrangement.set(QuickActionArrangement.Default)
|
||||
}
|
||||
context.showLongToastSync(R.string.devtools__reset_quick_actions_to_default__toast_success)
|
||||
},
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
)
|
||||
Preference(
|
||||
title = stringRes(R.string.devtools__reset_flag__label, "flag_name" to "isImeSetUp"),
|
||||
summary = stringRes(R.string.devtools__reset_flag_is_ime_set_up__summary),
|
||||
onClick = { prefs.internal.isImeSetUp.set(false) },
|
||||
onClick = { scope.launch { prefs.internal.isImeSetUp.set(false) } },
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
)
|
||||
Preference(
|
||||
@@ -189,14 +205,14 @@ fun DevtoolsScreen() = FlorisScreen {
|
||||
title = "keyboardExtensions",
|
||||
summary = extensionManager.keyboardExtensions.internalModuleDir.absolutePath,
|
||||
onClick = {
|
||||
context.showLongToast(extensionManager.keyboardExtensions.internalModuleDir.absolutePath)
|
||||
context.showLongToastSync(extensionManager.keyboardExtensions.internalModuleDir.absolutePath)
|
||||
},
|
||||
)
|
||||
Preference(
|
||||
title = "themes",
|
||||
summary = extensionManager.themes.internalModuleDir.absolutePath,
|
||||
onClick = {
|
||||
context.showLongToast(extensionManager.themes.internalModuleDir.absolutePath)
|
||||
context.showLongToastSync(extensionManager.themes.internalModuleDir.absolutePath)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,15 +38,16 @@ 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.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.devtools.Devtools
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.compose.FlorisButton
|
||||
import org.florisboard.lib.compose.florisHorizontalScroll
|
||||
import org.florisboard.lib.compose.florisScrollbar
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
|
||||
// TODO: This screen is just a quick thrown-together thing and needs further enhancing in the UI
|
||||
@Composable
|
||||
@@ -54,7 +55,7 @@ fun ExportDebugLogScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.devtools__debuglog__title)
|
||||
scrollable = false
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val clipboardManager by context.clipboardManager()
|
||||
|
||||
@@ -74,7 +75,7 @@ fun ExportDebugLogScreen() = FlorisScreen {
|
||||
FlorisButton(
|
||||
onClick = {
|
||||
clipboardManager.addNewPlaintext(debugLog!!.joinToString("\n"))
|
||||
context.showShortToast(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
context.showShortToastSync(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
},
|
||||
modifier = Modifier,
|
||||
text = stringRes(R.string.devtools__debuglog__copy_log),
|
||||
@@ -83,7 +84,7 @@ fun ExportDebugLogScreen() = FlorisScreen {
|
||||
FlorisButton(
|
||||
onClick = {
|
||||
clipboardManager.addNewPlaintext(formattedDebugLog!!.joinToString("\n"))
|
||||
context.showShortToast(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
context.showShortToastSync(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
},
|
||||
text = stringRes(R.string.devtools__debuglog__copy_for_github),
|
||||
enabled = debugLog != null,
|
||||
|
||||
@@ -35,13 +35,13 @@ import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisTextButton
|
||||
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.generateUpdateUrl
|
||||
import dev.patrickgold.florisboard.lib.util.launchUrl
|
||||
import org.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.FlorisTextButton
|
||||
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.kotlin.curlyFormat
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -21,7 +21,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
fun CheckUpdatesScreen() = FlorisScreen {
|
||||
|
||||
@@ -40,13 +40,13 @@ import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.nlp.LanguagePackComponent
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
|
||||
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
|
||||
import org.florisboard.lib.compose.FlorisIconButton
|
||||
import org.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.FlorisTextButton
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
fun ExtensionComponentNoneFoundView() {
|
||||
|
||||
@@ -46,12 +46,9 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.MimeTypeFilter
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.cache.CacheManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
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
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
@@ -60,24 +57,28 @@ import java.util.*
|
||||
import org.florisboard.lib.android.query
|
||||
import org.florisboard.lib.android.readToFile
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.compose.FlorisIconButton
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.kotlin.io.parentDir
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import org.florisboard.lib.kotlin.mimeTypeFilterOf
|
||||
|
||||
const val FONTS = "fonts"
|
||||
const val IMAGES = "images"
|
||||
|
||||
val MIME_TYPES = mapOf(
|
||||
FONTS to listOf(
|
||||
FONTS to mimeTypeFilterOf(
|
||||
// Source: https://www.alienfactory.co.uk/articles/mime-types-for-web-fonts-in-bedsheet#mimeTypes
|
||||
"font/*",
|
||||
"application/vnd.ms-fontobject", // .eot
|
||||
"application/font-woff", // .woff
|
||||
"application/x-font-truetype", // .ttf
|
||||
"application/x-font-opentype", // .otf
|
||||
"application/font-*",
|
||||
"application/x-font-*",
|
||||
"application/vnd.ms-fontobject",
|
||||
),
|
||||
IMAGES to listOf(
|
||||
IMAGES to mimeTypeFilterOf(
|
||||
"image/*",
|
||||
),
|
||||
)
|
||||
@@ -118,9 +119,9 @@ fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = Fl
|
||||
val tempFile = context.cacheDir.subFile("temp_${UUID.randomUUID()}")
|
||||
context.contentResolver.readToFile(uri, tempFile)
|
||||
val mimeType = context.contentResolver.getType(uri)
|
||||
val types = MIME_TYPES[currentImportDest!!]!!
|
||||
checkNotNull(MimeTypeFilter.matches(mimeType, types.toTypedArray())) {
|
||||
"Given file mime type was '$mimeType', expected one of $types"
|
||||
val filter = MIME_TYPES[currentImportDest!!]!!
|
||||
check(filter.matches(mimeType)) {
|
||||
"Given file mime type was '$mimeType', expected one of ${filter.types}"
|
||||
}
|
||||
val fileName = context.contentResolver.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME)).use { cursor ->
|
||||
if (cursor == null || !cursor.moveToFirst()) return@use null
|
||||
@@ -187,9 +188,9 @@ fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = Fl
|
||||
allowOutsideDismissal = true,
|
||||
onNeutral = {
|
||||
if (file.delete()) {
|
||||
context.showShortToast("Successfully deleted")
|
||||
context.showShortToastSync("Successfully deleted")
|
||||
} else {
|
||||
context.showShortToast("Failed to delete")
|
||||
context.showShortToastSync("Failed to delete")
|
||||
}
|
||||
dialogFile = null
|
||||
version++
|
||||
@@ -197,18 +198,18 @@ fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = Fl
|
||||
onConfirm = {
|
||||
val newFile = file.parentFile!!.subFile(fileNameInput).canonicalFile
|
||||
if (newFile.parentFile != file.canonicalFile.parentFile) {
|
||||
context.showLongToast("Invalid file name!")
|
||||
context.showLongToastSync("Invalid file name!")
|
||||
return@JetPrefAlertDialog
|
||||
}
|
||||
if (newFile.exists()) {
|
||||
context.showShortToast("Filename already exists.")
|
||||
context.showShortToastSync("Filename already exists.")
|
||||
return@JetPrefAlertDialog
|
||||
}
|
||||
val success = file.renameTo(newFile)
|
||||
if (success) {
|
||||
context.showShortToast("Successfully renamed")
|
||||
context.showShortToastSync("Successfully renamed")
|
||||
} else {
|
||||
context.showShortToast("Failed to rename the file.")
|
||||
context.showShortToastSync("Failed to rename the file.")
|
||||
}
|
||||
dialogFile = null
|
||||
version++
|
||||
@@ -258,13 +259,13 @@ fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = Fl
|
||||
dir.mkdirs()
|
||||
val file = dir.subFile(fileName)
|
||||
if (file.parentDir != workspace.extDir.subDir(dest)) {
|
||||
context.showShortToast("Invalid file name")
|
||||
context.showShortToastSync("Invalid file name")
|
||||
} else if (file.exists()) {
|
||||
context.showShortToast("File already exists")
|
||||
context.showShortToastSync("File already exists")
|
||||
} else {
|
||||
val tempFile = result.first
|
||||
if (!tempFile.renameTo(file)) {
|
||||
context.showShortToast("Failed to rename file")
|
||||
context.showShortToastSync("Failed to rename file")
|
||||
tempFile.delete()
|
||||
}
|
||||
currentImportDest = null
|
||||
|
||||
@@ -42,7 +42,9 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
@@ -60,15 +62,9 @@ import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponentImpl
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionEditor
|
||||
import dev.patrickgold.florisboard.lib.ValidationResult
|
||||
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.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisUnsavedChangesDialog
|
||||
import dev.patrickgold.florisboard.lib.compose.Validation
|
||||
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
|
||||
@@ -85,11 +81,16 @@ import dev.patrickgold.florisboard.lib.io.ZipUtils
|
||||
import dev.patrickgold.florisboard.lib.rememberValidationResult
|
||||
import dev.patrickgold.florisboard.themeManager
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.vectorResource
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import java.util.*
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.compose.FlorisButtonBar
|
||||
import org.florisboard.lib.compose.FlorisIconButton
|
||||
import org.florisboard.lib.compose.FlorisInfoCard
|
||||
import org.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
@@ -287,7 +288,7 @@ private fun EditScreen(
|
||||
stylesheetFile.writeText(stylesheet)
|
||||
}.onFailure {
|
||||
// TODO: better error handling
|
||||
context.showLongToast(it.message.toString())
|
||||
context.showLongToastSync(it.message.toString())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
@@ -352,7 +353,7 @@ private fun EditScreen(
|
||||
)
|
||||
Preference(
|
||||
onClick = { workspace.currentAction = EditorAction.ManageFiles },
|
||||
icon = vectorResource(R.drawable.ic_file_blank),
|
||||
icon = ImageVector.vectorResource(R.drawable.ic_file_blank),
|
||||
title = stringRes(R.string.ext__editor__files__title),
|
||||
)
|
||||
}
|
||||
@@ -627,7 +628,7 @@ private fun <T : ExtensionComponent> CreateComponentScreen(
|
||||
for (theme in editor.themes) {
|
||||
put(ExtensionComponentName(extId, theme.id), theme)
|
||||
}
|
||||
for ((componentName, theme) in themeManager.indexedThemeConfigs.value ?: emptyMap()) {
|
||||
for ((componentName, theme) in themeManager.indexedThemeConfigs.value.first) {
|
||||
if (componentName.extensionId != extId) {
|
||||
put(componentName, theme)
|
||||
}
|
||||
@@ -665,7 +666,7 @@ private fun <T : ExtensionComponent> CreateComponentScreen(
|
||||
when (createFrom) {
|
||||
CreateFrom.EMPTY -> {
|
||||
if (editor.themes.any { it.id == newId.trim() }) {
|
||||
context.showLongToast("A theme with this ID already exists!")
|
||||
context.showLongToastSync("A theme with this ID already exists!")
|
||||
} else {
|
||||
val componentEditor = ThemeExtensionComponentEditor(
|
||||
id = newId.trim(),
|
||||
@@ -709,7 +710,7 @@ private fun <T : ExtensionComponent> CreateComponentScreen(
|
||||
}
|
||||
editor.themes.add(componentEditor)
|
||||
} else {
|
||||
val component = themeManager.indexedThemeConfigs.value?.get(componentName) ?: return
|
||||
val component = themeManager.indexedThemeConfigs.value.first.get(componentName) ?: return
|
||||
val componentEditor = (component as? ThemeExtensionComponentImpl)?.edit() ?: return
|
||||
componentEditor.id = componentId
|
||||
componentEditor.stylesheetPath = ""
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.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
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
|
||||
@Composable
|
||||
fun ExtensionExportScreen(id: String) {
|
||||
@@ -61,9 +62,9 @@ private fun ExportScreen(ext: Extension) = FlorisScreen {
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
runCatching { extensionManager.export(ext, uri) }.onSuccess {
|
||||
context.showLongToast(R.string.ext__export__success)
|
||||
context.showLongToastSync(R.string.ext__export__success)
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(R.string.ext__export__failure, "error_message" to error.localizedMessage)
|
||||
context.showLongToastSync(R.string.ext__export__failure, "error_message" to error.localizedMessage)
|
||||
}
|
||||
navController.popBackStack()
|
||||
},
|
||||
|
||||
@@ -27,8 +27,8 @@ import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
fun ExtensionHomeScreen() = FlorisScreen {
|
||||
|
||||
@@ -50,17 +50,17 @@ import dev.patrickgold.florisboard.ime.keyboard.KeyboardExtension
|
||||
import dev.patrickgold.florisboard.ime.nlp.LanguagePackExtension
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtension
|
||||
import dev.patrickgold.florisboard.lib.NATIVE_NULLPTR
|
||||
import org.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 org.florisboard.lib.compose.FlorisBulletSpacer
|
||||
import org.florisboard.lib.compose.FlorisButtonBar
|
||||
import org.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.FlorisOutlinedButton
|
||||
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.florisHorizontalScroll
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.kotlin.resultOk
|
||||
|
||||
enum class ExtensionImportScreenType(
|
||||
@@ -188,10 +188,10 @@ fun ExtensionImportScreen(type: ExtensionImportScreenType, initUuid: String?) =
|
||||
}
|
||||
}.onSuccess {
|
||||
workspace.close()
|
||||
context.showLongToast(R.string.ext__import__success)
|
||||
context.showLongToastSync(R.string.ext__import__success)
|
||||
navController.popBackStack()
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(R.string.ext__import__failure, "error_message" to error.localizedMessage)
|
||||
context.showLongToastSync(R.string.ext__import__failure, "error_message" to error.localizedMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisChip
|
||||
import org.florisboard.lib.compose.FlorisChip
|
||||
|
||||
@Composable
|
||||
fun ExtensionKeywordChip(
|
||||
|
||||
@@ -52,14 +52,14 @@ import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtension
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisTextButton
|
||||
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import org.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.FlorisTextButton
|
||||
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.florisScrollbar
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
enum class ExtensionListScreenType(
|
||||
val id: String,
|
||||
|
||||
@@ -30,10 +30,10 @@ 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.lib.compose.FlorisChip
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionMaintainer
|
||||
import dev.patrickgold.florisboard.lib.util.launchUrl
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import org.florisboard.lib.compose.FlorisChip
|
||||
|
||||
@Composable
|
||||
fun ExtensionMaintainerChip(
|
||||
|
||||
@@ -24,7 +24,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
internal fun ExtensionNotFoundScreen(id: String) = FlorisScreen {
|
||||
|
||||
@@ -51,17 +51,17 @@ import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.nlp.LanguagePackExtension
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtension
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponentImpl
|
||||
import org.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
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.compose.FlorisOutlinedButton
|
||||
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
fun ExtensionViewScreen(id: String) {
|
||||
@@ -202,7 +202,7 @@ private fun ViewScreen(ext: Extension) = FlorisScreen {
|
||||
}.onSuccess {
|
||||
navController.popBackStack()
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(
|
||||
context.showLongToastSync(
|
||||
R.string.error__snackbar_message,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
|
||||
@@ -19,7 +19,6 @@ package dev.patrickgold.florisboard.app.settings
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.Assignment
|
||||
import androidx.compose.material.icons.filled.Adb
|
||||
import androidx.compose.material.icons.filled.Extension
|
||||
import androidx.compose.material.icons.filled.Gesture
|
||||
import androidx.compose.material.icons.filled.Language
|
||||
@@ -38,13 +37,13 @@ import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
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
|
||||
import org.florisboard.lib.compose.FlorisErrorCard
|
||||
import org.florisboard.lib.compose.FlorisWarningCard
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
fun HomeScreen() = FlorisScreen {
|
||||
|
||||
@@ -41,12 +41,12 @@ import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.lib.util.launchUrl
|
||||
import org.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.florisboard.lib.util.launchUrl
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import org.florisboard.lib.android.stringRes
|
||||
import org.florisboard.lib.compose.FlorisCanvasIcon
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
fun AboutScreen() = FlorisScreen {
|
||||
|
||||
@@ -29,11 +29,11 @@ import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.sp
|
||||
import dev.patrickgold.florisboard.R
|
||||
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
|
||||
import dev.patrickgold.florisboard.lib.io.loadTextAsset
|
||||
import org.florisboard.lib.compose.florisHorizontalScroll
|
||||
import org.florisboard.lib.compose.florisVerticalScroll
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
fun ProjectLicenseScreen() = FlorisScreen {
|
||||
|
||||
@@ -21,12 +21,13 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults
|
||||
import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
|
||||
import com.mikepenz.aboutlibraries.ui.compose.m3.LibraryDefaults
|
||||
import com.mikepenz.aboutlibraries.ui.compose.m3.libraryColors
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.compose.florisScrollbar
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
fun ThirdPartyLicensesScreen() = FlorisScreen {
|
||||
|
||||
@@ -32,6 +32,7 @@ import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -41,28 +42,33 @@ import androidx.core.app.ShareCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.cacheManager
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ItemType
|
||||
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.devtools.flogError
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
|
||||
import dev.patrickgold.florisboard.lib.io.FileRegistry
|
||||
import dev.patrickgold.florisboard.lib.io.ZipUtils
|
||||
import dev.patrickgold.jetpref.datastore.jetprefDatastoreDir
|
||||
import dev.patrickgold.jetpref.datastore.runtime.AndroidAppDataStorage
|
||||
import dev.patrickgold.jetpref.datastore.runtime.FileBasedStorage
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.android.writeFromFile
|
||||
import org.florisboard.lib.compose.FlorisButtonBar
|
||||
import org.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.rippleClickable
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import org.florisboard.lib.kotlin.io.writeJson
|
||||
@@ -136,6 +142,7 @@ fun BackupScreen() = FlorisScreen {
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val cacheManager by context.cacheManager()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
var backupDestination by remember { mutableStateOf(Backup.Destination.FILE_SYS) }
|
||||
val backupFilesSelector = remember { Backup.FilesSelector() }
|
||||
@@ -155,22 +162,24 @@ fun BackupScreen() = FlorisScreen {
|
||||
context.contentResolver.writeFromFile(uri, backupWorkspace!!.zipFile)
|
||||
backupWorkspace!!.close()
|
||||
}.onSuccess {
|
||||
context.showLongToast(R.string.backup_and_restore__back_up__success)
|
||||
context.showLongToastSync(R.string.backup_and_restore__back_up__success)
|
||||
navController.popBackStack()
|
||||
}.onFailure { error ->
|
||||
flogError { error.stackTraceToString() }
|
||||
context.showLongToast(R.string.backup_and_restore__back_up__failure, "error_message" to error.message)
|
||||
context.showLongToastSync(R.string.backup_and_restore__back_up__failure, "error_message" to error.message)
|
||||
backupWorkspace = null
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
fun prepareBackupWorkspace() {
|
||||
suspend fun prepareBackupWorkspace() {
|
||||
val workspace = cacheManager.backupAndRestore.new()
|
||||
if (backupFilesSelector.jetprefDatastore) {
|
||||
context.jetprefDatastoreDir.let { dir ->
|
||||
dir.copyRecursively(workspace.inputDir.subDir(dir.name))
|
||||
}
|
||||
val fileBasedStorage = workspace.inputDir
|
||||
.subDir(AndroidAppDataStorage.JETPREF_DIR_NAME)
|
||||
.subFile("${FlorisPreferenceModel.NAME}.${AndroidAppDataStorage.JETPREF_FILE_EXT}")
|
||||
.let { FileBasedStorage(it.path) }
|
||||
FlorisPreferenceStore.export(fileBasedStorage).getOrThrow()
|
||||
}
|
||||
val workspaceFilesDir = workspace.inputDir.subDir("files")
|
||||
if (backupFilesSelector.imeKeyboard) {
|
||||
@@ -225,7 +234,7 @@ fun BackupScreen() = FlorisScreen {
|
||||
backupWorkspace = workspace
|
||||
}
|
||||
|
||||
fun prepareAndPerformBackup() {
|
||||
suspend fun prepareAndPerformBackup() {
|
||||
runCatching {
|
||||
if (backupWorkspace == null || backupWorkspace!!.isClosed()) {
|
||||
prepareBackupWorkspace()
|
||||
@@ -265,7 +274,7 @@ fun BackupScreen() = FlorisScreen {
|
||||
)
|
||||
ButtonBarButton(
|
||||
onClick = {
|
||||
prepareAndPerformBackup()
|
||||
scope.launch { prepareAndPerformBackup() }
|
||||
},
|
||||
text = stringRes(R.string.action__back_up),
|
||||
enabled = backupFilesSelector.atLeastOneSelected(),
|
||||
|
||||
@@ -27,7 +27,9 @@ import androidx.compose.material.icons.filled.SettingsBackupRestore
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.AppTheme
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
@@ -36,7 +38,6 @@ import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
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.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.ColorPickerPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
@@ -47,6 +48,7 @@ import dev.patrickgold.jetpref.datastore.ui.isMaterialYou
|
||||
import dev.patrickgold.jetpref.datastore.ui.listPrefEntries
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.color.ColorMappings
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
|
||||
@Composable
|
||||
@@ -154,6 +156,11 @@ fun OtherScreen() = FlorisScreen {
|
||||
},
|
||||
enabledIf = { AndroidVersion.ATMOST_API28_P },
|
||||
)
|
||||
Preference(
|
||||
icon = ImageVector.vectorResource(R.drawable.ic_keyboard_keys),
|
||||
title = stringRes(R.string.physical_keyboard__title),
|
||||
onClick = { navController.navigate(Routes.Settings.PhysicalKeyboard) },
|
||||
)
|
||||
Preference(
|
||||
icon = Icons.Default.Adb,
|
||||
title = stringRes(R.string.devtools__title),
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The FlorisBoard Contributors
|
||||
*
|
||||
* 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.settings.advanced
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.provider.Settings
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
fun PhysicalKeyboardScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.physical_keyboard__title)
|
||||
|
||||
val context = LocalContext.current
|
||||
val physicalKeyboardAttached by remember {
|
||||
mutableStateOf(context.resources.configuration.keyboard != Configuration.KEYBOARD_NOKEYS)
|
||||
}
|
||||
|
||||
val activityForResult = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { }
|
||||
|
||||
content {
|
||||
if (physicalKeyboardAttached) {
|
||||
Preference(
|
||||
title = stringRes(R.string.physical_keyboard__system_settings__title),
|
||||
summary = stringRes(R.string.physical_keyboard__system_settings__summary),
|
||||
onClick = {
|
||||
activityForResult.launch(Intent(Settings.ACTION_HARD_KEYBOARD_SETTINGS))
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Preference(
|
||||
title = stringRes(R.string.physical_keyboard__system_settings__title),
|
||||
summary = stringRes(R.string.physical_keyboard__system_settings__summary_not_attached),
|
||||
)
|
||||
}
|
||||
SwitchPreference(
|
||||
pref = prefs.physicalKeyboard.showOnScreenKeyboard,
|
||||
title = stringRes(R.string.physical_keyboard__show_on_screen_keyboard__title),
|
||||
summary = stringRes(R.string.physical_keyboard__show_on_screen_keyboard__summary),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -43,47 +43,46 @@ import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.cacheManager
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardItem
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ItemType
|
||||
import dev.patrickgold.florisboard.lib.cache.CacheManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisButtonBar
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisCardDefaults
|
||||
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.jetpref.datastore.JetPref
|
||||
import dev.patrickgold.jetpref.datastore.runtime.AndroidAppDataStorage
|
||||
import dev.patrickgold.jetpref.datastore.runtime.FileBasedStorage
|
||||
import dev.patrickgold.jetpref.datastore.runtime.ImportStrategy
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import java.io.FileNotFoundException
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.readToFile
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.compose.FlorisButtonBar
|
||||
import org.florisboard.lib.compose.FlorisCardDefaults
|
||||
import org.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.FlorisOutlinedButton
|
||||
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
|
||||
import org.florisboard.lib.kotlin.io.readJson
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import java.io.FileNotFoundException
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
|
||||
object Restore {
|
||||
const val MIN_VERSION_CODE = 64
|
||||
const val PACKAGE_NAME = "dev.patrickgold.florisboard"
|
||||
const val BACKUP_ARCHIVE_FILE_NAME = "backup.zip"
|
||||
|
||||
enum class Mode {
|
||||
MERGE,
|
||||
ERASE_AND_OVERWRITE;
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -91,13 +90,12 @@ 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()
|
||||
|
||||
val restoreFilesSelector = remember { Backup.FilesSelector() }
|
||||
var restoreMode by remember { mutableStateOf(Restore.Mode.MERGE) }
|
||||
var importStrategy by remember { mutableStateOf(ImportStrategy.Merge) }
|
||||
// TODO: rememberCoroutineScope() is unusable because it provides the scope in a cancelled state, which does
|
||||
// not make sense at all. I suspect that this is a bug and once it is resolved we can use it here again.
|
||||
val restoreScope = remember { CoroutineScope(Dispatchers.Main) }
|
||||
@@ -138,7 +136,7 @@ fun RestoreScreen() = FlorisScreen {
|
||||
}
|
||||
restoreWorkspace = workspace
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(
|
||||
context.showLongToastSync(
|
||||
R.string.backup_and_restore__restore__failure,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
@@ -148,14 +146,14 @@ fun RestoreScreen() = FlorisScreen {
|
||||
|
||||
suspend fun performRestore() {
|
||||
val workspace = restoreWorkspace!!
|
||||
val shouldReset = restoreMode == Restore.Mode.ERASE_AND_OVERWRITE
|
||||
val shouldReset = importStrategy == ImportStrategy.Erase
|
||||
if (restoreFilesSelector.jetprefDatastore) {
|
||||
val datastoreFile = workspace.outputDir
|
||||
.subDir(JetPref.JETPREF_DIR_NAME)
|
||||
.subFile("${prefs.name}.${JetPref.JETPREF_FILE_EXT}")
|
||||
if (datastoreFile.exists()) {
|
||||
prefs.datastorePersistenceHandler?.loadPrefs(datastoreFile, shouldReset)
|
||||
prefs.datastorePersistenceHandler?.persistPrefs()
|
||||
val file = workspace.outputDir
|
||||
.subDir(AndroidAppDataStorage.JETPREF_DIR_NAME)
|
||||
.subFile("${FlorisPreferenceModel.NAME}.${AndroidAppDataStorage.JETPREF_FILE_EXT}")
|
||||
if (file.exists()) {
|
||||
val fileBasedStorage = FileBasedStorage(file.path)
|
||||
FlorisPreferenceStore.import(importStrategy, fileBasedStorage).getOrThrow()
|
||||
}
|
||||
}
|
||||
val workspaceFilesDir = workspace.outputDir.subDir("files")
|
||||
@@ -275,16 +273,16 @@ fun RestoreScreen() = FlorisScreen {
|
||||
) {
|
||||
RadioListItem(
|
||||
onClick = {
|
||||
restoreMode = Restore.Mode.MERGE
|
||||
importStrategy = ImportStrategy.Merge
|
||||
},
|
||||
selected = restoreMode == Restore.Mode.MERGE,
|
||||
selected = importStrategy == ImportStrategy.Merge,
|
||||
text = stringRes(R.string.backup_and_restore__restore__mode_merge),
|
||||
)
|
||||
RadioListItem(
|
||||
onClick = {
|
||||
restoreMode = Restore.Mode.ERASE_AND_OVERWRITE
|
||||
importStrategy = ImportStrategy.Erase
|
||||
},
|
||||
selected = restoreMode == Restore.Mode.ERASE_AND_OVERWRITE,
|
||||
selected = importStrategy == ImportStrategy.Erase,
|
||||
text = stringRes(R.string.backup_and_restore__restore__mode_erase_and_overwrite),
|
||||
)
|
||||
}
|
||||
@@ -293,7 +291,7 @@ fun RestoreScreen() = FlorisScreen {
|
||||
runCatching {
|
||||
restoreDataFromFileSystemLauncher.launch("*/*")
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(
|
||||
context.showLongToastSync(
|
||||
R.string.backup_and_restore__restore__failure,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
|
||||
@@ -18,14 +18,15 @@ package dev.patrickgold.florisboard.app.settings.clipboard
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.clipboard.CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO
|
||||
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
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.compose.pluralsRes
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@OptIn(ExperimentalJetPrefDatastoreUi::class)
|
||||
@Composable
|
||||
@@ -75,6 +76,24 @@ fun ClipboardScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.pref__clipboard__enable_clipboard_history__label),
|
||||
summary = stringRes(R.string.pref__clipboard__enable_clipboard_history__summary),
|
||||
)
|
||||
DialogSliderPreference(
|
||||
primaryPref = prefs.clipboard.numHistoryGridColumnsPortrait,
|
||||
secondaryPref = prefs.clipboard.numHistoryGridColumnsLandscape,
|
||||
title = stringRes(R.string.pref__clipboard__num_history_grid_columns__label),
|
||||
primaryLabel = stringRes(R.string.screen_orientation__portrait),
|
||||
secondaryLabel = stringRes(R.string.screen_orientation__landscape),
|
||||
valueLabel = { numGridColumns ->
|
||||
if (numGridColumns == CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO) {
|
||||
stringRes(R.string.general__auto)
|
||||
} else {
|
||||
numGridColumns.toString()
|
||||
}
|
||||
},
|
||||
min = 0,
|
||||
max = 10,
|
||||
stepIncrement = 1,
|
||||
enabledIf = { prefs.clipboard.historyEnabled isEqualTo true },
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.clipboard.cleanUpOld,
|
||||
title = stringRes(R.string.pref__clipboard__clean_up_old__label),
|
||||
|
||||
@@ -21,9 +21,9 @@ import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
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
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
fun DictionaryScreen() = FlorisScreen {
|
||||
|
||||
@@ -55,11 +55,8 @@ 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.compose.FlorisIconButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.Validation
|
||||
import dev.patrickgold.florisboard.lib.compose.rippleClickable
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.rememberValidationResult
|
||||
import dev.patrickgold.florisboard.lib.util.launchActivity
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
@@ -68,7 +65,11 @@ import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.android.stringRes
|
||||
import org.florisboard.lib.compose.FlorisIconButton
|
||||
import org.florisboard.lib.compose.rippleClickable
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
private val AllLanguagesLocale = FlorisLocale.from(language = "zz")
|
||||
private val UserDictionaryEntryToAdd = UserDictionaryEntry(id = 0, "", 255, null, null)
|
||||
@@ -143,16 +144,16 @@ fun UserDictionaryScreen(type: UserDictionaryType) = FlorisScreen {
|
||||
UserDictionaryType.SYSTEM -> dictionaryManager.systemUserDictionaryDatabase()
|
||||
}
|
||||
if (db == null) {
|
||||
context.showLongToast("Database handle is null, failed to import")
|
||||
context.showLongToastSync("Database handle is null, failed to import")
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
runCatching {
|
||||
db.importCombinedList(context, uri)
|
||||
}.onSuccess {
|
||||
buildUi()
|
||||
context.showLongToast(R.string.settings__udm__dictionary_import_success)
|
||||
context.showLongToastSync(R.string.settings__udm__dictionary_import_success)
|
||||
}.onFailure { error ->
|
||||
context.showLongToast("Error: ${error.localizedMessage}")
|
||||
context.showLongToastSync("Error: ${error.localizedMessage}")
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -168,15 +169,15 @@ fun UserDictionaryScreen(type: UserDictionaryType) = FlorisScreen {
|
||||
UserDictionaryType.SYSTEM -> dictionaryManager.systemUserDictionaryDatabase()
|
||||
}
|
||||
if (db == null) {
|
||||
context.showLongToast("Database handle is null, failed to export")
|
||||
context.showLongToastSync("Database handle is null, failed to export")
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
runCatching {
|
||||
db.exportCombinedList(context, uri)
|
||||
}.onSuccess {
|
||||
context.showLongToast(R.string.settings__udm__dictionary_export_success)
|
||||
context.showLongToastSync(R.string.settings__udm__dictionary_export_success)
|
||||
}.onFailure { error ->
|
||||
context.showLongToast("Error: ${error.localizedMessage}")
|
||||
context.showLongToastSync("Error: ${error.localizedMessage}")
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -23,14 +23,13 @@ import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
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
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import org.florisboard.lib.compose.FlorisInfoCard
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@OptIn(ExperimentalJetPrefDatastoreUi::class)
|
||||
@Composable
|
||||
|
||||
@@ -20,18 +20,17 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.input.InputFeedbackActivationMode
|
||||
import dev.patrickgold.florisboard.ime.input.HapticVibrationMode
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.systemVibratorOrNull
|
||||
import org.florisboard.lib.android.vibrate
|
||||
import dev.patrickgold.florisboard.ime.input.InputFeedbackActivationMode
|
||||
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
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import org.florisboard.lib.android.systemVibratorOrNull
|
||||
import org.florisboard.lib.android.vibrate
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@OptIn(ExperimentalJetPrefDatastoreUi::class)
|
||||
@Composable
|
||||
@@ -138,8 +137,6 @@ fun InputFeedbackScreen() = FlorisScreen {
|
||||
summary = { strength ->
|
||||
if (vibrator == null || !vibrator.hasVibrator()) {
|
||||
stringRes(R.string.pref__input_feedback__haptic_vibration_strength__summary_no_vibrator)
|
||||
} else if (AndroidVersion.ATMOST_API25_N_MR1) {
|
||||
stringRes(R.string.pref__input_feedback__haptic_vibration_strength__summary_unsupported_android_version)
|
||||
} else if (!vibrator.hasAmplitudeControl()) {
|
||||
stringRes(R.string.pref__input_feedback__haptic_vibration_strength__summary_no_amplitude_ctrl)
|
||||
} else {
|
||||
@@ -157,7 +154,7 @@ fun InputFeedbackScreen() = FlorisScreen {
|
||||
prefs.inputFeedback.hapticEnabled isEqualTo true &&
|
||||
prefs.inputFeedback.hapticVibrationMode isEqualTo HapticVibrationMode.USE_VIBRATOR_DIRECTLY &&
|
||||
vibrator != null && vibrator.hasVibrator() &&
|
||||
AndroidVersion.ATLEAST_API26_O && vibrator.hasAmplitudeControl()
|
||||
vibrator.hasAmplitudeControl()
|
||||
},
|
||||
)
|
||||
SwitchPreference(
|
||||
|
||||
@@ -29,13 +29,13 @@ import dev.patrickgold.florisboard.ime.smartbar.IncognitoDisplayMode
|
||||
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
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@OptIn(ExperimentalJetPrefDatastoreUi::class)
|
||||
@Composable
|
||||
|
||||
@@ -41,26 +41,27 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionImportScreenType
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.nlp.LanguagePackComponent
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisConfirmDeleteDialog
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisTextButton
|
||||
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.Extension
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.FlorisTextButton
|
||||
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.rippleClickable
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
|
||||
enum class LanguagePackManagerScreenAction(val id: String) {
|
||||
MANAGE("manage-installed-language-packs");
|
||||
@@ -75,7 +76,7 @@ fun LanguagePackManagerScreen(action: LanguagePackManagerScreenAction?) = Floris
|
||||
else -> error("LanguagePack manager screen action must not be null")
|
||||
})
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val extensionManager by context.extensionManager()
|
||||
@@ -199,7 +200,7 @@ fun LanguagePackManagerScreen(action: LanguagePackManagerScreenAction?) = Floris
|
||||
runCatching {
|
||||
extensionManager.delete(languagePackExtToDelete!!)
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(
|
||||
context.showLongToastSync(
|
||||
R.string.error__snackbar_message,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
|
||||
@@ -45,8 +45,6 @@ import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
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
|
||||
@@ -55,8 +53,9 @@ import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.florisboard.lib.compose.FlorisWarningCard
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
internal val SubtypeSaver = Saver<MutableState<Subtype?>, String>(
|
||||
save = {
|
||||
|
||||
@@ -44,15 +44,15 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
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
|
||||
import org.florisboard.lib.compose.florisScrollbar
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
const val SelectLocaleScreenResultLanguageTag = "SelectLocaleScreen.languageTag"
|
||||
|
||||
@@ -61,7 +61,7 @@ fun SelectLocaleScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.settings__localization__subtype_select_locale)
|
||||
scrollable = false
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val navController = LocalNavController.current
|
||||
|
||||
val displayLanguageNamesIn by prefs.localization.displayLanguageNamesIn.observeAsState()
|
||||
|
||||
@@ -37,6 +37,7 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ShapeDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
@@ -58,9 +59,9 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
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
|
||||
@@ -74,19 +75,20 @@ import dev.patrickgold.florisboard.ime.nlp.han.HanShapeBasedLanguageProvider
|
||||
import dev.patrickgold.florisboard.ime.nlp.latin.LatinLanguageProvider
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
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.florisScrollbar
|
||||
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
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefDropdown
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefDropdownMenuDefaults
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import kotlinx.serialization.encodeToString
|
||||
import org.florisboard.lib.compose.FlorisButtonBar
|
||||
import org.florisboard.lib.compose.FlorisDropdownLikeButton
|
||||
import org.florisboard.lib.compose.florisScrollbar
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
|
||||
private val SelectComponentName = ExtensionComponentName("00", "00")
|
||||
private val SelectNlpProviderId = SelectComponentName.toString()
|
||||
@@ -186,7 +188,7 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
val selectValue = stringRes(R.string.settings__localization__subtype_select_placeholder)
|
||||
val selectListValues = remember(selectValue) { listOf(selectValue) }
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val configuration = LocalConfiguration.current
|
||||
@@ -366,6 +368,7 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
onClick = {
|
||||
navController.navigate(Routes.Settings.SelectLocale)
|
||||
},
|
||||
appearance = JetPrefDropdownMenuDefaults.outlined(shape = ShapeDefaults.Small),
|
||||
)
|
||||
}
|
||||
SubtypeProperty(stringRes(R.string.settings__localization__subtype_popup_mapping)) {
|
||||
@@ -375,16 +378,15 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
val popupMappingLabels = remember(popupMappings) {
|
||||
selectListValues + popupMappings.values.map { it.label }
|
||||
}
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
val selectedIndex = popupMappingIds.indexOf(popupMapping).coerceAtLeast(0)
|
||||
FlorisDropdownMenu(
|
||||
items = popupMappingLabels,
|
||||
JetPrefDropdown(
|
||||
options = popupMappingLabels,
|
||||
expanded = expanded,
|
||||
selectedIndex = selectedIndex,
|
||||
selectedOptionIndex = selectedIndex,
|
||||
isError = showSelectAsError && selectedIndex == 0,
|
||||
onSelectItem = { popupMapping = popupMappingIds[it] },
|
||||
onExpandRequest = { expanded = true },
|
||||
onDismissRequest = { expanded = false },
|
||||
onSelectOption = { popupMapping = popupMappingIds[it] },
|
||||
appearance = JetPrefDropdownMenuDefaults.outlined(shape = ShapeDefaults.Small),
|
||||
)
|
||||
}
|
||||
SubtypePropertyDropdown(stringRes(R.string.settings__localization__subtype_characters_layout), LayoutType.CHARACTERS)
|
||||
@@ -405,19 +407,18 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
val nlpProviderMappingLabels = remember(nlpProviderMappings) {
|
||||
selectListValues + nlpProviderMappings.values.map { it }
|
||||
}
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
val selectedIndex = nlpProviderMappingIds.indexOf(nlpProviders.suggestion).coerceAtLeast(0)
|
||||
FlorisDropdownMenu(
|
||||
items = nlpProviderMappingLabels,
|
||||
JetPrefDropdown(
|
||||
options = nlpProviderMappingLabels,
|
||||
expanded = expanded,
|
||||
selectedIndex = selectedIndex,
|
||||
selectedOptionIndex = selectedIndex,
|
||||
isError = showSelectAsError && selectedIndex == 0,
|
||||
onSelectItem = { nlpProviders = SubtypeNlpProviderMap(
|
||||
onSelectOption = { nlpProviders = SubtypeNlpProviderMap(
|
||||
suggestion = nlpProviderMappingIds[it],
|
||||
spelling = nlpProviderMappingIds[it]
|
||||
) },
|
||||
onExpandRequest = { expanded = true },
|
||||
onDismissRequest = { expanded = false },
|
||||
appearance = JetPrefDropdownMenuDefaults.outlined(shape = ShapeDefaults.Small),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -433,15 +434,14 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
val composerNames = remember(composers) {
|
||||
selectListValues + composers.values.map { it.label }
|
||||
}
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
FlorisDropdownMenu(
|
||||
items = composerNames,
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
JetPrefDropdown(
|
||||
options = composerNames,
|
||||
expanded = expanded,
|
||||
selectedIndex = composerIds.indexOf(composer).coerceAtLeast(0),
|
||||
selectedOptionIndex = composerIds.indexOf(composer).coerceAtLeast(0),
|
||||
isError = showSelectAsError && composer == SelectComponentName,
|
||||
onSelectItem = { composer = composerIds[it] },
|
||||
onExpandRequest = { expanded = true },
|
||||
onDismissRequest = { expanded = false },
|
||||
onSelectOption = { composer = composerIds[it] },
|
||||
appearance = JetPrefDropdownMenuDefaults.outlined(shape = ShapeDefaults.Small),
|
||||
)
|
||||
}
|
||||
SubtypeProperty(stringRes(R.string.settings__localization__subtype_currency_set)) {
|
||||
@@ -451,15 +451,14 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
val currencySetNames = remember(currencySets) {
|
||||
selectListValues + currencySets.values.map { it.label }
|
||||
}
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
FlorisDropdownMenu(
|
||||
items = currencySetNames,
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
JetPrefDropdown(
|
||||
options = currencySetNames,
|
||||
expanded = expanded,
|
||||
selectedIndex = currencySetIds.indexOf(currencySet).coerceAtLeast(0),
|
||||
selectedOptionIndex = currencySetIds.indexOf(currencySet).coerceAtLeast(0),
|
||||
isError = showSelectAsError && currencySet == SelectComponentName,
|
||||
onSelectItem = { currencySet = currencySetIds[it] },
|
||||
onExpandRequest = { expanded = true },
|
||||
onDismissRequest = { expanded = false },
|
||||
onSelectOption = { currencySet = currencySetIds[it] },
|
||||
appearance = JetPrefDropdownMenuDefaults.outlined(shape = ShapeDefaults.Small),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -557,16 +556,15 @@ private fun SubtypeLayoutDropdown(
|
||||
val layoutIds = remember(layouts) { SelectListKeys + layouts.keys.toList() }
|
||||
val layoutLabels = remember(layouts) { selectListValues + layouts.values.map { it.label } }
|
||||
val layoutId = remember(layoutMap) { layoutMap[layoutType] }
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
val selectedIndex = layoutIds.indexOf(layoutId).coerceAtLeast(0)
|
||||
FlorisDropdownMenu(
|
||||
items = layoutLabels,
|
||||
JetPrefDropdown(
|
||||
options = layoutLabels,
|
||||
expanded = expanded,
|
||||
selectedIndex = selectedIndex,
|
||||
selectedOptionIndex = selectedIndex,
|
||||
isError = showSelectAsError && selectedIndex == 0,
|
||||
onSelectItem = { onLayoutMapChanged(layoutMap.copy(layoutType, layoutIds[it])!!) },
|
||||
onExpandRequest = { expanded = true },
|
||||
onDismissRequest = { expanded = false },
|
||||
onSelectOption = { onLayoutMapChanged(layoutMap.copy(layoutType = layoutType, componentName = layoutIds[it])!!) },
|
||||
appearance = JetPrefDropdownMenuDefaults.outlined(shape = ShapeDefaults.Small),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,15 +27,13 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistory
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistoryHelper
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSkinTone
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSuggestionType
|
||||
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.ListPreference
|
||||
@@ -44,6 +42,8 @@ import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.compose.pluralsRes
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@OptIn(ExperimentalJetPrefDatastoreUi::class)
|
||||
@Composable
|
||||
@@ -52,7 +52,7 @@ fun MediaScreen() = FlorisScreen {
|
||||
previewFieldVisible = true
|
||||
iconSpaceReserved = true
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
|
||||
var shouldDelete by remember { mutableStateOf<ShouldDelete?>(null) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
@@ -24,10 +24,10 @@ import dev.patrickgold.florisboard.ime.smartbar.CandidatesDisplayMode
|
||||
import dev.patrickgold.florisboard.ime.smartbar.ExtendedActionsPlacement
|
||||
import dev.patrickgold.florisboard.ime.smartbar.SmartbarLayout
|
||||
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
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
fun SmartbarScreen() = FlorisScreen {
|
||||
@@ -68,7 +68,7 @@ fun SmartbarScreen() = FlorisScreen {
|
||||
// TODO: schedule to remove this preference in the future, but keep it for now so users
|
||||
// know why the setting is not available anymore. Also force enable it for UI display.
|
||||
SideEffect {
|
||||
prefs.smartbar.sharedActionsAutoExpandCollapse.set(true)
|
||||
// prefs.smartbar.sharedActionsAutoExpandCollapse.set(true)
|
||||
}
|
||||
SwitchPreference(
|
||||
prefs.smartbar.sharedActionsAutoExpandCollapse,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.lib.compose
|
||||
package dev.patrickgold.florisboard.app.settings.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceSerializer
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.settings.theme
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
@@ -76,13 +77,7 @@ import dev.patrickgold.florisboard.app.ext.FONTS
|
||||
import dev.patrickgold.florisboard.app.ext.IMAGES
|
||||
import dev.patrickgold.florisboard.lib.ValidationResult
|
||||
import dev.patrickgold.florisboard.lib.cache.CacheManager
|
||||
import dev.patrickgold.florisboard.lib.compose.DpSizeSaver
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisChip
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisTextButton
|
||||
import dev.patrickgold.florisboard.lib.compose.Validation
|
||||
import dev.patrickgold.florisboard.lib.compose.florisVerticalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionValidation
|
||||
import dev.patrickgold.florisboard.lib.rememberValidationResult
|
||||
import dev.patrickgold.jetpref.material.ui.ColorRepresentation
|
||||
@@ -93,7 +88,14 @@ import dev.patrickgold.jetpref.material.ui.JetPrefDropdown
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import dev.patrickgold.jetpref.material.ui.rememberJetPrefColorPickerState
|
||||
import java.io.File
|
||||
import org.florisboard.lib.color.ColorPalette
|
||||
import org.florisboard.lib.compose.DpSizeSaver
|
||||
import org.florisboard.lib.compose.FlorisChip
|
||||
import org.florisboard.lib.compose.FlorisIconButton
|
||||
import org.florisboard.lib.compose.FlorisTextButton
|
||||
import org.florisboard.lib.compose.florisVerticalScroll
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.kotlin.curlyFormat
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.toStringWithoutDotZero
|
||||
@@ -130,7 +132,6 @@ import org.florisboard.lib.snygg.value.SnyggUndefinedValue
|
||||
import org.florisboard.lib.snygg.value.SnyggUriValue
|
||||
import org.florisboard.lib.snygg.value.SnyggValue
|
||||
import org.florisboard.lib.snygg.value.SnyggValueEncoder
|
||||
import java.io.File
|
||||
|
||||
internal val SnyggEmptyPropertyInfoForAdding = PropertyInfo(
|
||||
rule = SnyggEmptyRuleForAdding,
|
||||
@@ -655,7 +656,7 @@ private fun PropertyValueEditor(
|
||||
JetPrefListItem(
|
||||
modifier = Modifier.clickable {
|
||||
val relPath = file.path.removePrefix(workspace.extDir.path)
|
||||
inputStr = "flex:$relPath"
|
||||
inputStr = "flex:" + Uri.encode(relPath, "/")
|
||||
onValueChange(SnyggUriValue(inputStr))
|
||||
showSelectFileDialog = false
|
||||
},
|
||||
|
||||
@@ -69,6 +69,7 @@ import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.input.InputKeyEventReceiver
|
||||
import dev.patrickgold.florisboard.ime.input.InputShiftState
|
||||
import dev.patrickgold.florisboard.ime.keyboard.ComputingEvaluator
|
||||
@@ -84,24 +85,27 @@ import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.NATIVE_NULLPTR
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisChip
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisHyperlinkText
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.util.InputMethodUtils
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefDropdown
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefTextFieldDefaults
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.android.stringRes
|
||||
import org.florisboard.lib.compose.FlorisChip
|
||||
import org.florisboard.lib.compose.FlorisIconButton
|
||||
import org.florisboard.lib.compose.florisHorizontalScroll
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.kotlin.curlyFormat
|
||||
import org.florisboard.lib.snygg.SnyggAnnotationRule
|
||||
import org.florisboard.lib.snygg.SnyggAttributes
|
||||
import org.florisboard.lib.snygg.SnyggElementRule
|
||||
import org.florisboard.lib.snygg.SnyggRule
|
||||
import org.florisboard.lib.snygg.SnyggSelector
|
||||
import org.florisboard.lib.snygg.ui.NonNullSaver
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
private val TransparentTextSelectionColors = TextSelectionColors(
|
||||
handleColor = Color.Transparent,
|
||||
@@ -240,11 +244,13 @@ internal fun EditRuleDialog(
|
||||
}
|
||||
}
|
||||
DialogProperty(text = stringRes(R.string.settings__theme_editor__rule_selectors)) {
|
||||
Row(modifier = Modifier.florisHorizontalScroll()) {
|
||||
//TODO: LazyRow
|
||||
Row(
|
||||
modifier = Modifier.florisHorizontalScroll(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
// TODO: avoid code duplication
|
||||
FlorisChip(
|
||||
onClick = { updateCurrentRule(SnyggSelector.PRESSED) },
|
||||
modifier = Modifier.padding(end = 4.dp),
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> SnyggSelector.PRESSED.id
|
||||
else -> stringRes(R.string.snygg__rule_selector__pressed)
|
||||
@@ -253,7 +259,6 @@ internal fun EditRuleDialog(
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { updateCurrentRule(SnyggSelector.FOCUS) },
|
||||
modifier = Modifier.padding( end = 4.dp),
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> SnyggSelector.FOCUS.id
|
||||
else -> stringRes(R.string.snygg__rule_selector__focus)
|
||||
@@ -262,7 +267,6 @@ internal fun EditRuleDialog(
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { updateCurrentRule(SnyggSelector.HOVER) },
|
||||
modifier = Modifier.padding( end = 4.dp),
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> SnyggSelector.HOVER.id
|
||||
else -> stringRes(R.string.snygg__rule_selector__hover)
|
||||
@@ -312,22 +316,17 @@ internal fun EditRuleDialog(
|
||||
)
|
||||
},
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(vertical = 4.dp),
|
||||
text = stringRes(
|
||||
if (codes.isEmpty()) {
|
||||
R.string.settings__theme_editor__no_codes_defined
|
||||
} else {
|
||||
R.string.settings__theme_editor__codes_defined
|
||||
}
|
||||
),
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
FlowRow {
|
||||
if (codes.isEmpty()) {
|
||||
Text(
|
||||
text = stringRes(R.string.settings__theme_editor__no_codes_defined),
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
for (code in codes) {
|
||||
FlorisChip(
|
||||
onClick = { editCodeDialogValue = code },
|
||||
text = code.toString(),
|
||||
text = code,
|
||||
selected = editCodeDialogValue == code,
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
)
|
||||
@@ -335,78 +334,23 @@ internal fun EditRuleDialog(
|
||||
}
|
||||
}
|
||||
|
||||
val shiftStateUnshifted = remember(currentRule) {
|
||||
attributes[FlorisImeUi.Attr.ShiftState]?.contains(InputShiftState.UNSHIFTED.attrName()) == true
|
||||
}
|
||||
val shiftStateShiftedManual = remember(currentRule) {
|
||||
attributes[FlorisImeUi.Attr.ShiftState]?.contains(InputShiftState.SHIFTED_MANUAL.attrName()) == true
|
||||
}
|
||||
val shiftStateShiftedAutomatic = remember(currentRule) {
|
||||
attributes[FlorisImeUi.Attr.ShiftState]?.contains(InputShiftState.SHIFTED_AUTOMATIC.attrName()) == true
|
||||
}
|
||||
val shiftStateCapsLock = remember(currentRule) {
|
||||
attributes[FlorisImeUi.Attr.ShiftState]?.contains(InputShiftState.CAPS_LOCK.attrName()) == true
|
||||
}
|
||||
DialogProperty(text = stringRes(R.string.settings__theme_editor__rule_shift_states)) {
|
||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
FlorisChip(
|
||||
onClick = {
|
||||
currentRule = copy(
|
||||
attributes = attributes.toggling(
|
||||
FlorisImeUi.Attr.ShiftState to InputShiftState.UNSHIFTED.attrName()
|
||||
)
|
||||
)
|
||||
},
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> InputShiftState.UNSHIFTED.attrName()
|
||||
else -> stringRes(R.string.enum__input_shift_state__unshifted)
|
||||
},
|
||||
selected = shiftStateUnshifted,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = {
|
||||
currentRule = copy(
|
||||
attributes = attributes.toggling(
|
||||
FlorisImeUi.Attr.ShiftState to InputShiftState.SHIFTED_MANUAL.attrName()
|
||||
)
|
||||
)
|
||||
},
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> InputShiftState.SHIFTED_MANUAL.attrName()
|
||||
else -> stringRes(R.string.enum__input_shift_state__shifted_manual)
|
||||
},
|
||||
selected = shiftStateShiftedManual,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = {
|
||||
currentRule = copy(
|
||||
attributes = attributes.toggling(
|
||||
FlorisImeUi.Attr.ShiftState to InputShiftState.SHIFTED_AUTOMATIC.attrName()
|
||||
)
|
||||
)
|
||||
},
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> InputShiftState.SHIFTED_AUTOMATIC.attrName()
|
||||
else -> stringRes(R.string.enum__input_shift_state__shifted_automatic)
|
||||
},
|
||||
selected = shiftStateShiftedAutomatic,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = {
|
||||
currentRule = copy(
|
||||
attributes = attributes.toggling(
|
||||
FlorisImeUi.Attr.ShiftState to InputShiftState.CAPS_LOCK.attrName()
|
||||
)
|
||||
)
|
||||
},
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> InputShiftState.CAPS_LOCK.attrName()
|
||||
else -> stringRes(R.string.enum__input_shift_state__caps_lock)
|
||||
},
|
||||
selected = shiftStateCapsLock,
|
||||
)
|
||||
}
|
||||
}
|
||||
EnumLikeAttributeBox(
|
||||
text = stringRes(R.string.settings__theme_editor__rule_modes),
|
||||
enumClass = KeyboardMode::class,
|
||||
attribute = FlorisImeUi.Attr.Mode,
|
||||
attributes = attributes,
|
||||
setAttributes = { currentRule = copy(attributes = it) },
|
||||
level = level,
|
||||
)
|
||||
|
||||
EnumLikeAttributeBox(
|
||||
text = stringRes(R.string.settings__theme_editor__rule_shift_states),
|
||||
enumClass = InputShiftState::class,
|
||||
attribute = FlorisImeUi.Attr.ShiftState,
|
||||
attributes = attributes,
|
||||
setAttributes = { currentRule = copy(attributes = it) },
|
||||
level = level,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,7 +407,7 @@ private fun EditCodeValueDialog(
|
||||
}
|
||||
if (!isFlorisBoardEnabled || !isFlorisBoardSelected) {
|
||||
lastRecordingToast?.cancel()
|
||||
lastRecordingToast = context.showShortToast(
|
||||
lastRecordingToast = context.showShortToastSync(
|
||||
R.string.settings__theme_editor__code_recording_requires_default_ime_floris,
|
||||
"app_name" to context.stringRes(R.string.floris_app_name),
|
||||
)
|
||||
@@ -489,12 +433,12 @@ private fun EditCodeValueDialog(
|
||||
val defaultReceiver = keyboardManager.inputEventDispatcher.keyEventReceiver
|
||||
keyboardManager.inputEventDispatcher.keyEventReceiver = receiver
|
||||
lastRecordingToast?.cancel()
|
||||
lastRecordingToast = context.showShortToast(R.string.settings__theme_editor__code_recording_started)
|
||||
lastRecordingToast = context.showShortToastSync(R.string.settings__theme_editor__code_recording_started)
|
||||
focusRequester.requestFocus()
|
||||
onDispose {
|
||||
keyboardManager.inputEventDispatcher.keyEventReceiver = defaultReceiver
|
||||
lastRecordingToast?.cancel()
|
||||
lastRecordingToast = context.showShortToast(R.string.settings__theme_editor__code_recording_stopped)
|
||||
lastRecordingToast = context.showShortToastSync(R.string.settings__theme_editor__code_recording_stopped)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -715,3 +659,79 @@ private fun TextKeyDataPreviewBox(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun <V : Any> EnumLikeAttributeBox(
|
||||
text: String,
|
||||
enumClass: KClass<V>,
|
||||
attribute: String,
|
||||
attributes: SnyggAttributes,
|
||||
setAttributes: (SnyggAttributes) -> Unit,
|
||||
level: SnyggLevel,
|
||||
) {
|
||||
val allEntries = enumDisplayEntriesOf(enumClass)
|
||||
val (alreadyAddedEntries, notYetAddedEntries) = remember(attributes, attribute) {
|
||||
allEntries.partition { entry ->
|
||||
attributes[attribute]?.contains(entry.key.toString()) == true
|
||||
}
|
||||
}
|
||||
var showAddDialog by remember { mutableStateOf(false) }
|
||||
|
||||
DialogProperty(
|
||||
text = text,
|
||||
trailingIconTitle = {
|
||||
FlorisIconButton(
|
||||
onClick = { showAddDialog = true },
|
||||
modifier = Modifier.offset(x = 12.dp),
|
||||
icon = Icons.Default.Add,
|
||||
)
|
||||
},
|
||||
) {
|
||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
for (entry in alreadyAddedEntries) {
|
||||
FlorisChip(
|
||||
onClick = {
|
||||
setAttributes(attributes.excluding(attribute to entry.key.toString()))
|
||||
},
|
||||
text = entry.label,
|
||||
)
|
||||
}
|
||||
if (alreadyAddedEntries.isEmpty()) {
|
||||
Text(
|
||||
text = stringRes(R.string.settings__theme_editor__no_codes_defined),
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showAddDialog) {
|
||||
JetPrefAlertDialog(
|
||||
title = stringRes(R.string.action__add),
|
||||
dismissLabel = stringRes(R.string.action__cancel),
|
||||
onDismiss = { showAddDialog = false },
|
||||
) {
|
||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
for (entry in notYetAddedEntries) {
|
||||
FlorisChip(
|
||||
onClick = {
|
||||
setAttributes(attributes.including(attribute to entry.key.toString()))
|
||||
showAddDialog = false
|
||||
},
|
||||
text = when (level) {
|
||||
SnyggLevel.DEVELOPER -> entry.key.toString()
|
||||
else -> entry.label
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
if (notYetAddedEntries.isEmpty()) {
|
||||
Text(
|
||||
text = stringRes(R.string.settings__theme_editor__no_enum_value_to_add_anymore),
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,13 +20,13 @@ import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceLayout
|
||||
import dev.patrickgold.jetpref.material.ui.ColorRepresentation
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
private val FineTuneContentPadding = PaddingValues(horizontal = 8.dp)
|
||||
|
||||
@@ -37,7 +37,7 @@ fun FineTuneDialog(onDismiss: () -> Unit) {
|
||||
onDismiss = onDismiss,
|
||||
contentPadding = FineTuneContentPadding,
|
||||
) {
|
||||
PreferenceLayout(florisPreferenceModel(), iconSpaceReserved = false) {
|
||||
PreferenceLayout(FlorisPreferenceStore, iconSpaceReserved = false) {
|
||||
ListPreference(
|
||||
listPref = prefs.theme.editorLevel,
|
||||
title = stringRes(R.string.settings__theme_editor__fine_tune__level),
|
||||
|
||||
@@ -30,7 +30,6 @@ import androidx.compose.material.icons.automirrored.filled.FormatAlignLeft
|
||||
import androidx.compose.material.icons.automirrored.filled.FormatAlignRight
|
||||
import androidx.compose.material.icons.automirrored.filled.WrapText
|
||||
import androidx.compose.material.icons.filled.AttachFile
|
||||
import androidx.compose.material.icons.filled.CheckBox
|
||||
import androidx.compose.material.icons.filled.CheckBoxOutlineBlank
|
||||
import androidx.compose.material.icons.filled.FontDownload
|
||||
import androidx.compose.material.icons.filled.FormatAlignCenter
|
||||
@@ -57,7 +56,7 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.snygg.value.SnyggCutCornerDpShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDefinedVarValue
|
||||
@@ -121,7 +120,7 @@ internal fun SnyggValueIcon(
|
||||
modifier: Modifier = Modifier,
|
||||
spec: SnyggValueIcon.Spec = SnyggValueIcon.Normal,
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val accentColor by prefs.theme.accentColor.observeAsState()
|
||||
|
||||
|
||||
@@ -70,9 +70,9 @@ import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.apptheme.Shapes
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionComponentView
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponentEditor
|
||||
@@ -80,16 +80,10 @@ import dev.patrickgold.florisboard.ime.theme.ThemeExtensionEditor
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeManager
|
||||
import dev.patrickgold.florisboard.ime.theme.extPreviewTheme
|
||||
import dev.patrickgold.florisboard.lib.cache.CacheManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.PreviewKeyboardField
|
||||
import dev.patrickgold.florisboard.lib.compose.Validation
|
||||
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.florisVerticalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.rememberPreviewFieldController
|
||||
import dev.patrickgold.florisboard.lib.compose.rippleClickable
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionValidation
|
||||
import dev.patrickgold.florisboard.lib.rememberValidationResult
|
||||
import dev.patrickgold.florisboard.themeManager
|
||||
@@ -100,6 +94,13 @@ import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.compose.FlorisIconButton
|
||||
import org.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.florisVerticalScroll
|
||||
import org.florisboard.lib.compose.rippleClickable
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import org.florisboard.lib.snygg.SnyggAnnotationRule
|
||||
import org.florisboard.lib.snygg.SnyggElementRule
|
||||
@@ -145,7 +146,7 @@ fun ThemeEditorScreen(
|
||||
title = stringRes(R.string.ext__editor__edit_component__title_theme)
|
||||
scrollable = false
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
val themeManager by context.themeManager()
|
||||
@@ -186,7 +187,7 @@ fun ThemeEditorScreen(
|
||||
}.also { editor.stylesheetEditor = it }
|
||||
}
|
||||
|
||||
val definedVariables = remember(stylesheetEditor.rules) {
|
||||
val definedVariables = remember(stylesheetEditor.rules, workspace.version) {
|
||||
stylesheetEditor.rules.firstNotNullOfOrNull { (rule, propertySet) ->
|
||||
if (rule is SnyggAnnotationRule.Defines && propertySet is SnyggSinglePropertySetEditor) {
|
||||
propertySet.properties
|
||||
@@ -196,7 +197,7 @@ fun ThemeEditorScreen(
|
||||
} ?: emptyMap()
|
||||
}
|
||||
|
||||
val fontNames = remember(stylesheetEditor.rules) {
|
||||
val fontNames = remember(stylesheetEditor.rules, workspace.version) {
|
||||
stylesheetEditor.rules.mapNotNull { (rule, _) ->
|
||||
if (rule is SnyggAnnotationRule.Font) {
|
||||
rule.fontName
|
||||
@@ -309,13 +310,14 @@ fun ThemeEditorScreen(
|
||||
}
|
||||
|
||||
DisposableEffect(workspace.version) {
|
||||
themeManager.previewThemeInfo = ThemeManager.ThemeInfo.DEFAULT.copy(
|
||||
themeManager.previewThemeInfo.value = ThemeManager.ThemeInfo.DEFAULT.copy(
|
||||
name = extPreviewTheme(System.currentTimeMillis().toString()),
|
||||
config = editor.build(),
|
||||
stylesheet = stylesheetEditor.build(),
|
||||
loadedDir = workspace.extDir,
|
||||
)
|
||||
onDispose {
|
||||
themeManager.previewThemeInfo = null
|
||||
themeManager.previewThemeInfo.value = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,7 +634,7 @@ private fun ComponentMetaEditorDialog(
|
||||
if (!allFieldsValid) {
|
||||
showValidationErrors = true
|
||||
} else if (id != editor.id && (workspace.editor as? ThemeExtensionEditor)?.themes?.find { it.id == id.trim() } != null) {
|
||||
context.showLongToast("A theme with this ID already exists!")
|
||||
context.showLongToastSync("A theme with this ID already exists!")
|
||||
} else {
|
||||
workspace.update {
|
||||
editor.id = id.trim()
|
||||
@@ -730,7 +732,6 @@ private fun SnyggRuleRow(
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = LocalContentColor.current.copy(alpha = 0.56f),
|
||||
fontFamily = FontFamily.Monospace,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
@@ -750,7 +751,7 @@ private fun SnyggRuleRow(
|
||||
text = context.translateElementName(rule, level),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
maxLines = 1,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
|
||||
@@ -29,22 +29,24 @@ import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
|
||||
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.ExtensionComponentName
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.florisboard.themeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import org.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import org.florisboard.lib.compose.rippleClickable
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
enum class ThemeManagerScreenAction(val id: String) {
|
||||
SELECT_DAY("select-day"),
|
||||
@@ -60,10 +62,11 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
})
|
||||
previewFieldVisible = true
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val extensionManager by context.extensionManager()
|
||||
val themeManager by context.themeManager()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val indexedThemeExtensions by extensionManager.themes.observeAsNonNullState()
|
||||
val extGroupedThemes = remember(indexedThemeExtensions) {
|
||||
@@ -83,7 +86,7 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
val extComponentName = ExtensionComponentName(extId, componentId)
|
||||
when (action) {
|
||||
ThemeManagerScreenAction.SELECT_DAY,
|
||||
ThemeManagerScreenAction.SELECT_NIGHT -> {
|
||||
ThemeManagerScreenAction.SELECT_NIGHT -> scope.launch {
|
||||
getThemeIdPref().set(extComponentName)
|
||||
}
|
||||
}
|
||||
@@ -96,9 +99,9 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
|
||||
content {
|
||||
DisposableEffect(activeThemeId) {
|
||||
themeManager.previewThemeId = activeThemeId
|
||||
themeManager.previewThemeId.value = activeThemeId
|
||||
onDispose {
|
||||
themeManager.previewThemeId = null
|
||||
themeManager.previewThemeId.value = null
|
||||
}
|
||||
}
|
||||
val grayColor = LocalContentColor.current.copy(alpha = 0.56f)
|
||||
|
||||
@@ -16,19 +16,18 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.settings.theme
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Brightness2
|
||||
import androidx.compose.material.icons.filled.BrightnessAuto
|
||||
import androidx.compose.material.icons.filled.ColorLens
|
||||
import androidx.compose.material.icons.filled.DarkMode
|
||||
import androidx.compose.material.icons.filled.LightMode
|
||||
import androidx.compose.material.icons.filled.WbTwilight
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
@@ -37,17 +36,17 @@ import dev.patrickgold.florisboard.app.ext.AddonManagementReferenceBox
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionListScreenType
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeManager
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeMode
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisInfoCard
|
||||
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.themeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.ColorPickerPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.LocalTimePickerPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.isMaterialYou
|
||||
import org.florisboard.lib.color.ColorMappings
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
fun ThemeScreen() = FlorisScreen {
|
||||
@@ -60,41 +59,21 @@ fun ThemeScreen() = FlorisScreen {
|
||||
|
||||
@Composable
|
||||
fun ThemeManager.getThemeLabel(id: ExtensionComponentName): String {
|
||||
val configs by indexedThemeConfigs.observeAsState()
|
||||
configs?.get(id)?.let { return it.label }
|
||||
val configs by indexedThemeConfigs.collectAsState()
|
||||
configs.first[id]?.let { return it.label }
|
||||
return id.toString()
|
||||
}
|
||||
|
||||
content {
|
||||
val themeMode by prefs.theme.mode.observeAsState()
|
||||
val dayThemeId by prefs.theme.dayThemeId.observeAsState()
|
||||
val nightThemeId by prefs.theme.nightThemeId.observeAsState()
|
||||
|
||||
/*Card(modifier = Modifier.padding(8.dp)) {
|
||||
Column(modifier = Modifier.padding(8.dp)) {
|
||||
Text("If you want to give feedback on the new stylesheet editor and theme engine, please do so in below linked feedback thread:\n")
|
||||
Button(onClick = {
|
||||
context.launchUrl("https://github.com/florisboard/florisboard/discussions/1531")
|
||||
}) {
|
||||
Text("Open Feedback Thread")
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
ListPreference(
|
||||
prefs.theme.mode,
|
||||
icon = Icons.Default.BrightnessAuto,
|
||||
title = stringRes(R.string.pref__theme__mode__label),
|
||||
entries = enumDisplayEntriesOf(ThemeMode::class),
|
||||
)
|
||||
if (themeMode == ThemeMode.FOLLOW_TIME) {
|
||||
FlorisInfoCard(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
text = """
|
||||
The theme mode "Follow time" is not available in this beta release.
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
Preference(
|
||||
icon = Icons.Default.LightMode,
|
||||
title = stringRes(R.string.pref__theme__day),
|
||||
@@ -113,6 +92,18 @@ fun ThemeScreen() = FlorisScreen {
|
||||
navController.navigate(Routes.Settings.ThemeManager(ThemeManagerScreenAction.SELECT_NIGHT))
|
||||
},
|
||||
)
|
||||
LocalTimePickerPreference(
|
||||
pref = prefs.theme.sunriseTime,
|
||||
title = stringRes(R.string.pref__theme__sunrise_time__label),
|
||||
icon = Icons.Default.WbTwilight,
|
||||
enabledIf = { prefs.theme.mode isEqualTo ThemeMode.FOLLOW_TIME },
|
||||
)
|
||||
LocalTimePickerPreference(
|
||||
pref = prefs.theme.sunsetTime,
|
||||
title = stringRes(R.string.pref__theme__sunset_time__label),
|
||||
icon = Icons.Default.Brightness2,
|
||||
enabledIf = { prefs.theme.mode isEqualTo ThemeMode.FOLLOW_TIME },
|
||||
)
|
||||
ColorPickerPreference(
|
||||
pref = prefs.theme.accentColor,
|
||||
title = stringRes(R.string.pref__theme__theme_accent_color__label),
|
||||
|
||||
@@ -34,14 +34,14 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisCanvasIcon
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisErrorCard
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisSimpleCard
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisWarningCard
|
||||
import dev.patrickgold.florisboard.lib.compose.observeAsState
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.util.launchActivity
|
||||
import org.florisboard.lib.android.AndroidSettings
|
||||
import org.florisboard.lib.compose.FlorisCanvasIcon
|
||||
import org.florisboard.lib.compose.FlorisErrorCard
|
||||
import org.florisboard.lib.compose.FlorisSimpleCard
|
||||
import org.florisboard.lib.compose.FlorisWarningCard
|
||||
import org.florisboard.lib.compose.observeAsState
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
fun SpellCheckerServiceSelector(florisSpellCheckerEnabled: MutableState<Boolean>) {
|
||||
|
||||
@@ -30,6 +30,8 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
@@ -37,19 +39,17 @@ import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.keyboard.IncognitoMode
|
||||
import dev.patrickgold.florisboard.ime.nlp.SpellingLanguageMode
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisErrorCard
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisHyperlinkText
|
||||
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.DialogSliderPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.vectorResource
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.compose.FlorisErrorCard
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@OptIn(ExperimentalJetPrefDatastoreUi::class)
|
||||
@Composable
|
||||
@@ -89,7 +89,7 @@ fun TypingScreen() = FlorisScreen {
|
||||
)
|
||||
ListPreference(
|
||||
prefs.suggestion.incognitoMode,
|
||||
icon = vectorResource(id = R.drawable.ic_incognito),
|
||||
icon = ImageVector.vectorResource(id = R.drawable.ic_incognito),
|
||||
title = stringRes(R.string.pref__suggestion__incognito_mode__label),
|
||||
entries = enumDisplayEntriesOf(IncognitoMode::class),
|
||||
)
|
||||
|
||||
@@ -33,6 +33,7 @@ import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -40,26 +41,27 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.AppPrefs
|
||||
import dev.patrickgold.florisboard.app.FlorisAppActivity
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisBulletSpacer
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreenScope
|
||||
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.lib.util.InputMethodUtils
|
||||
import dev.patrickgold.florisboard.lib.util.launchActivity
|
||||
import dev.patrickgold.florisboard.lib.util.launchUrl
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceUiScope
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
|
||||
import org.florisboard.lib.compose.FlorisBulletSpacer
|
||||
import org.florisboard.lib.compose.FlorisStep
|
||||
import org.florisboard.lib.compose.FlorisStepLayout
|
||||
import org.florisboard.lib.compose.FlorisStepState
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
fun SetupScreen() = FlorisScreen {
|
||||
@@ -70,7 +72,8 @@ fun SetupScreen() = FlorisScreen {
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val isFlorisBoardEnabled by InputMethodUtils.observeIsFlorisboardEnabled(foregroundOnly = true)
|
||||
val isFlorisBoardSelected by InputMethodUtils.observeIsFlorisboardSelected(foregroundOnly = true)
|
||||
@@ -78,10 +81,12 @@ fun SetupScreen() = FlorisScreen {
|
||||
|
||||
val requestNotification =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||
if (isGranted) {
|
||||
prefs.internal.notificationPermissionState.set(NotificationPermissionState.GRANTED)
|
||||
} else {
|
||||
prefs.internal.notificationPermissionState.set(NotificationPermissionState.DENIED)
|
||||
scope.launch {
|
||||
if (isGranted) {
|
||||
prefs.internal.notificationPermissionState.set(NotificationPermissionState.GRANTED)
|
||||
} else {
|
||||
prefs.internal.notificationPermissionState.set(NotificationPermissionState.DENIED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +96,8 @@ fun SetupScreen() = FlorisScreen {
|
||||
context,
|
||||
navController,
|
||||
requestNotification,
|
||||
hasNotificationPermission
|
||||
hasNotificationPermission,
|
||||
scope,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -103,6 +109,7 @@ private fun FlorisScreenScope.content(
|
||||
navController: NavController,
|
||||
requestNotification: ManagedActivityResultLauncher<String, Boolean>,
|
||||
hasNotificationPermission: NotificationPermissionState,
|
||||
scope: CoroutineScope,
|
||||
) {
|
||||
|
||||
val stepState = rememberSaveable(saver = FlorisStepState.Saver) {
|
||||
@@ -158,7 +165,7 @@ private fun FlorisScreenScope.content(
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
},
|
||||
steps = steps(
|
||||
context, navController, requestNotification
|
||||
context, navController, requestNotification, scope
|
||||
),
|
||||
footer = {
|
||||
footer(context)
|
||||
@@ -189,10 +196,11 @@ private fun footer(context: Context) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PreferenceUiScope<AppPrefs>.steps(
|
||||
private fun PreferenceUiScope<FlorisPreferenceModel>.steps(
|
||||
context: Context,
|
||||
navController: NavController,
|
||||
requestNotification: ManagedActivityResultLauncher<String, Boolean>,
|
||||
scope: CoroutineScope,
|
||||
): List<FlorisStep> {
|
||||
|
||||
return listOfNotNull(
|
||||
@@ -232,7 +240,7 @@ private fun PreferenceUiScope<AppPrefs>.steps(
|
||||
StepText(stringRes(R.string.setup__finish_up__description_p1))
|
||||
StepText(stringRes(R.string.setup__finish_up__description_p2))
|
||||
StepButton(label = stringRes(R.string.setup__finish_up__finish_btn)) {
|
||||
this@steps.prefs.internal.isImeSetUp.set(true)
|
||||
scope.launch { this@steps.prefs.internal.isImeSetUp.set(true) }
|
||||
navController.navigate(Routes.Settings.Home) {
|
||||
popUpTo(Routes.Setup.Screen) {
|
||||
inclusive = true
|
||||
|
||||
@@ -22,9 +22,9 @@ import android.media.MediaMetadataRetriever
|
||||
import android.media.ThumbnailUtils
|
||||
import android.provider.MediaStore
|
||||
import android.util.Size
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
@@ -41,35 +41,54 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridScope
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.items
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.outlined.Backspace
|
||||
import androidx.compose.material.icons.filled.ClearAll
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.DeleteSweep
|
||||
import androidx.compose.material.icons.filled.FilterList
|
||||
import androidx.compose.material.icons.filled.FilterListOff
|
||||
import androidx.compose.material.icons.filled.Image
|
||||
import androidx.compose.material.icons.filled.Link
|
||||
import androidx.compose.material.icons.filled.Movie
|
||||
import androidx.compose.material.icons.filled.Phone
|
||||
import androidx.compose.material.icons.filled.TextFields
|
||||
import androidx.compose.material.icons.filled.ToggleOff
|
||||
import androidx.compose.material.icons.filled.ToggleOn
|
||||
import androidx.compose.material.icons.filled.Videocam
|
||||
import androidx.compose.material.icons.outlined.ContentPaste
|
||||
import androidx.compose.material.icons.outlined.Email
|
||||
import androidx.compose.material.icons.outlined.PushPin
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.mutableStateSetOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.ime.ImeUiMode
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
|
||||
@@ -77,23 +96,33 @@ import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardItem
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ItemType
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.media.KeyboardLikeButton
|
||||
import dev.patrickgold.florisboard.ime.smartbar.AnimationDuration
|
||||
import dev.patrickgold.florisboard.ime.smartbar.VerticalEnterTransition
|
||||
import dev.patrickgold.florisboard.ime.smartbar.VerticalExitTransition
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisStaggeredVerticalGrid
|
||||
import dev.patrickgold.florisboard.lib.compose.autoMirrorForRtl
|
||||
import dev.patrickgold.florisboard.lib.compose.florisVerticalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.rippleClickable
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.florisboard.lib.observeAsTransformingState
|
||||
import dev.patrickgold.florisboard.lib.util.NetworkUtils
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import java.time.Instant
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.AndroidKeyguardManager
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.android.systemService
|
||||
import org.florisboard.lib.compose.LocalLocalizedDateTimeFormatter
|
||||
import org.florisboard.lib.compose.autoMirrorForRtl
|
||||
import org.florisboard.lib.compose.florisHorizontalScroll
|
||||
import org.florisboard.lib.compose.florisVerticalScroll
|
||||
import org.florisboard.lib.compose.rippleClickable
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.snygg.SnyggQueryAttributes
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggButton
|
||||
import org.florisboard.lib.snygg.ui.SnyggChip
|
||||
import org.florisboard.lib.snygg.ui.SnyggColumn
|
||||
import org.florisboard.lib.snygg.ui.SnyggIcon
|
||||
import org.florisboard.lib.snygg.ui.SnyggIconButton
|
||||
@@ -103,11 +132,14 @@ import org.florisboard.lib.snygg.ui.SnyggText
|
||||
private val ItemWidth = 200.dp
|
||||
private val DialogWidth = 240.dp
|
||||
|
||||
const val CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO: Int = 0
|
||||
|
||||
@Composable
|
||||
fun ClipboardInputLayout(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val scope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
val clipboardManager by context.clipboardManager()
|
||||
val keyboardManager by context.keyboardManager()
|
||||
@@ -115,13 +147,38 @@ fun ClipboardInputLayout(
|
||||
|
||||
val deviceLocked = androidKeyguardManager.let { it.isDeviceLocked || it.isKeyguardLocked }
|
||||
val historyEnabled by prefs.clipboard.historyEnabled.observeAsState()
|
||||
val history by clipboardManager.history.observeAsNonNullState()
|
||||
|
||||
var popupItem by remember(history) { mutableStateOf<ClipboardItem?>(null) }
|
||||
var isFilterRowShown by remember { mutableStateOf(false) }
|
||||
val activeFilterTypes = remember { mutableStateSetOf<ItemType>() }
|
||||
|
||||
val unfilteredHistory by clipboardManager.history.observeAsNonNullState()
|
||||
val filteredHistory = remember(unfilteredHistory, activeFilterTypes.toSet()) {
|
||||
if (activeFilterTypes.isEmpty()) {
|
||||
unfilteredHistory
|
||||
} else {
|
||||
unfilteredHistory.all
|
||||
.filter { activeFilterTypes.contains(it.type) }
|
||||
.let { ClipboardManager.ClipboardHistory(it) }
|
||||
}
|
||||
}
|
||||
|
||||
val gridState = rememberLazyStaggeredGridState()
|
||||
var popupItem by remember(filteredHistory) { mutableStateOf<ClipboardItem?>(null) }
|
||||
var showClearAllHistory by remember { mutableStateOf(false) }
|
||||
|
||||
fun isPopupSurfaceActive() = popupItem != null || showClearAllHistory
|
||||
|
||||
LaunchedEffect(isFilterRowShown) {
|
||||
delay(AnimationDuration.toLong())
|
||||
if (!isFilterRowShown) {
|
||||
activeFilterTypes.clear()
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(activeFilterTypes.toSet()) {
|
||||
gridState.scrollToItem(0)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HeaderRow() {
|
||||
SnyggRow(FlorisImeUi.ClipboardHeader.elementName,
|
||||
@@ -149,7 +206,7 @@ fun ClipboardInputLayout(
|
||||
)
|
||||
SnyggIconButton(
|
||||
elementName = FlorisImeUi.ClipboardHeaderButton.elementName,
|
||||
onClick = { prefs.clipboard.historyEnabled.set(!historyEnabled) },
|
||||
onClick = { scope.launch { prefs.clipboard.historyEnabled.set(!historyEnabled) } },
|
||||
modifier = sizeModifier.autoMirrorForRtl(),
|
||||
enabled = !deviceLocked && !isPopupSurfaceActive(),
|
||||
) {
|
||||
@@ -165,22 +222,24 @@ fun ClipboardInputLayout(
|
||||
elementName = FlorisImeUi.ClipboardHeaderButton.elementName,
|
||||
onClick = { showClearAllHistory = true },
|
||||
modifier = sizeModifier.autoMirrorForRtl(),
|
||||
enabled = !deviceLocked && historyEnabled && history.all.isNotEmpty() && !isPopupSurfaceActive(),
|
||||
enabled = !deviceLocked && historyEnabled && filteredHistory.all.isNotEmpty() && !isPopupSurfaceActive(),
|
||||
) {
|
||||
SnyggIcon(
|
||||
imageVector = Icons.Default.ClearAll,
|
||||
imageVector = Icons.Default.DeleteSweep,
|
||||
)
|
||||
}
|
||||
SnyggIconButton(
|
||||
elementName = FlorisImeUi.ClipboardHeaderButton.elementName,
|
||||
onClick = {
|
||||
context.showShortToast("TODO: implement inline clip item editing")
|
||||
},
|
||||
onClick = { isFilterRowShown = !isFilterRowShown },
|
||||
modifier = sizeModifier,
|
||||
enabled = !deviceLocked && historyEnabled && !isPopupSurfaceActive(),
|
||||
enabled = !deviceLocked && historyEnabled && unfilteredHistory.all.isNotEmpty() && !isPopupSurfaceActive(),
|
||||
) {
|
||||
SnyggIcon(
|
||||
imageVector = Icons.Default.Edit,
|
||||
imageVector = if (!isFilterRowShown) {
|
||||
Icons.Default.FilterList
|
||||
} else {
|
||||
Icons.Default.FilterListOff
|
||||
},
|
||||
)
|
||||
}
|
||||
KeyboardLikeButton(
|
||||
@@ -194,14 +253,19 @@ fun ClipboardInputLayout(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun ClipItemView(
|
||||
elementName: String,
|
||||
item: ClipboardItem,
|
||||
contentScrollInsteadOfClip: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
SnyggBox(FlorisImeUi.ClipboardItem.elementName,
|
||||
val attributes = remember(item) {
|
||||
mapOf("type" to item.type.toString().lowercase())
|
||||
}
|
||||
SnyggBox(
|
||||
elementName = elementName,
|
||||
attributes = attributes,
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
clickAndSemanticsModifier = Modifier.combinedClickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
@@ -284,7 +348,11 @@ fun ClipboardInputLayout(
|
||||
} else {
|
||||
val text = item.stringRepresentation()
|
||||
Column {
|
||||
ClipTextItemDescription(text)
|
||||
ClipTextItemDescription(
|
||||
elementName = FlorisImeUi.ClipboardItemDescription.elementName,
|
||||
attributes = attributes,
|
||||
text = text,
|
||||
)
|
||||
SnyggText(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -302,43 +370,121 @@ fun ClipboardInputLayout(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
val historyAlpha by animateFloatAsState(targetValue = if (isPopupSurfaceActive()) 0.12f else 1f)
|
||||
SnyggColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.alpha(historyAlpha)
|
||||
.florisVerticalScroll(),
|
||||
val staggeredGridCells by prefs.clipboard.numHistoryGridColumns()
|
||||
.observeAsTransformingState { numGridColumns ->
|
||||
if (numGridColumns == CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO) {
|
||||
StaggeredGridCells.Adaptive(160.dp)
|
||||
} else {
|
||||
StaggeredGridCells.Fixed(numGridColumns)
|
||||
}
|
||||
}
|
||||
|
||||
fun LazyStaggeredGridScope.clipboardItems(
|
||||
items: List<ClipboardItem>,
|
||||
key: String,
|
||||
@StringRes title: Int,
|
||||
) {
|
||||
if (history.pinned.isNotEmpty()) {
|
||||
ClipCategoryTitle(
|
||||
text = stringRes(R.string.clipboard__group_pinned),
|
||||
)
|
||||
FlorisStaggeredVerticalGrid(maxColumnWidth = ItemWidth) {
|
||||
for (item in history.pinned) {
|
||||
ClipItemView(item, contentScrollInsteadOfClip = false)
|
||||
}
|
||||
if (items.isNotEmpty()) {
|
||||
item(key, span = StaggeredGridItemSpan.FullLine) {
|
||||
ClipCategoryTitle(text = stringRes(title))
|
||||
}
|
||||
}
|
||||
if (history.recent.isNotEmpty()) {
|
||||
ClipCategoryTitle(
|
||||
text = stringRes(R.string.clipboard__group_recent),
|
||||
)
|
||||
FlorisStaggeredVerticalGrid(maxColumnWidth = ItemWidth) {
|
||||
for (item in history.recent) {
|
||||
ClipItemView(item, contentScrollInsteadOfClip = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (history.other.isNotEmpty()) {
|
||||
ClipCategoryTitle(
|
||||
text = stringRes(R.string.clipboard__group_other),
|
||||
)
|
||||
FlorisStaggeredVerticalGrid(maxColumnWidth = ItemWidth) {
|
||||
for (item in history.other) {
|
||||
ClipItemView(item, contentScrollInsteadOfClip = false)
|
||||
}
|
||||
items(items) { item ->
|
||||
ClipItemView(
|
||||
elementName = FlorisImeUi.ClipboardItem.elementName,
|
||||
item = item,
|
||||
contentScrollInsteadOfClip = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.alpha(historyAlpha),
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = isFilterRowShown,
|
||||
enter = VerticalEnterTransition,
|
||||
exit = VerticalExitTransition,
|
||||
) {
|
||||
SnyggRow(
|
||||
elementName = FlorisImeUi.ClipboardFilterRow.elementName,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
clickAndSemanticsModifier = Modifier.florisHorizontalScroll(),
|
||||
) {
|
||||
@Composable
|
||||
fun FilterChip(
|
||||
imageVector: ImageVector,
|
||||
text: String,
|
||||
itemType: ItemType,
|
||||
) {
|
||||
val active = activeFilterTypes.contains(itemType)
|
||||
val attributes = remember(active) {
|
||||
mapOf(
|
||||
"state" to if (active) "active" else "inactive",
|
||||
"type" to itemType.toString().lowercase(),
|
||||
)
|
||||
}
|
||||
SnyggChip(
|
||||
elementName = FlorisImeUi.ClipboardFilterChip.elementName,
|
||||
attributes = attributes,
|
||||
onClick = {
|
||||
if (!activeFilterTypes.add(itemType)) {
|
||||
activeFilterTypes.remove(itemType)
|
||||
}
|
||||
},
|
||||
imageVector = imageVector,
|
||||
text = text,
|
||||
)
|
||||
}
|
||||
|
||||
FilterChip(
|
||||
imageVector = Icons.Default.TextFields,
|
||||
text = "Text",
|
||||
itemType = ItemType.TEXT,
|
||||
)
|
||||
FilterChip(
|
||||
imageVector = Icons.Default.Image,
|
||||
text = "Images",
|
||||
itemType = ItemType.IMAGE,
|
||||
)
|
||||
FilterChip(
|
||||
imageVector = Icons.Default.Movie,
|
||||
text = "Videos",
|
||||
itemType = ItemType.VIDEO,
|
||||
)
|
||||
}
|
||||
}
|
||||
SnyggBox(FlorisImeUi.ClipboardGrid.elementName,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
) {
|
||||
LazyVerticalStaggeredGrid(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = gridState,
|
||||
columns = staggeredGridCells,
|
||||
) {
|
||||
clipboardItems(
|
||||
items = filteredHistory.pinned,
|
||||
key = "pinned-header",
|
||||
title = R.string.clipboard__group_pinned,
|
||||
)
|
||||
clipboardItems(
|
||||
items = filteredHistory.recent,
|
||||
key = "recent-header",
|
||||
title = R.string.clipboard__group_recent,
|
||||
)
|
||||
clipboardItems(
|
||||
items = filteredHistory.other,
|
||||
key = "other-header",
|
||||
title = R.string.clipboard__group_other,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (popupItem != null) {
|
||||
SnyggRow(
|
||||
modifier = Modifier
|
||||
@@ -349,44 +495,57 @@ fun ClipboardInputLayout(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceAround,
|
||||
) {
|
||||
ClipItemView(
|
||||
modifier = Modifier.widthIn(max = ItemWidth),
|
||||
item = popupItem!!,
|
||||
contentScrollInsteadOfClip = true,
|
||||
)
|
||||
SnyggColumn(FlorisImeUi.ClipboardItemPopup.elementName) {
|
||||
PopupAction(
|
||||
iconId = R.drawable.ic_pin,
|
||||
text = stringRes(if (popupItem!!.isPinned) {
|
||||
R.string.clip__unpin_item
|
||||
} else {
|
||||
R.string.clip__pin_item
|
||||
}),
|
||||
) {
|
||||
if (popupItem!!.isPinned) {
|
||||
clipboardManager.unpinClip(popupItem!!)
|
||||
} else {
|
||||
clipboardManager.pinClip(popupItem!!)
|
||||
SnyggColumn(modifier = Modifier.weight(0.5f)) {
|
||||
ClipItemView(
|
||||
elementName = FlorisImeUi.ClipboardItemPopup.elementName,
|
||||
modifier = Modifier.widthIn(max = ItemWidth),
|
||||
item = popupItem!!,
|
||||
contentScrollInsteadOfClip = true,
|
||||
)
|
||||
SnyggBox(FlorisImeUi.ClipboardItemTimestamp.elementName) {
|
||||
val formatter = LocalLocalizedDateTimeFormatter.current
|
||||
SnyggText(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = formatter.format(Instant.ofEpochMilli(popupItem!!.creationTimestampMs)),
|
||||
)
|
||||
}
|
||||
}
|
||||
SnyggColumn(modifier = Modifier.weight(0.5f)) {
|
||||
SnyggColumn(FlorisImeUi.ClipboardItemActions.elementName) {
|
||||
PopupAction(
|
||||
icon = Icons.Outlined.PushPin,
|
||||
text = stringRes(if (popupItem!!.isPinned) {
|
||||
R.string.clip__unpin_item
|
||||
} else {
|
||||
R.string.clip__pin_item
|
||||
}),
|
||||
) {
|
||||
if (popupItem!!.isPinned) {
|
||||
clipboardManager.unpinClip(popupItem!!)
|
||||
} else {
|
||||
clipboardManager.pinClip(popupItem!!)
|
||||
}
|
||||
popupItem = null
|
||||
}
|
||||
PopupAction(
|
||||
icon = Icons.Default.Delete,
|
||||
text = stringRes(R.string.clip__delete_item),
|
||||
) {
|
||||
clipboardManager.deleteClip(popupItem!!)
|
||||
popupItem = null
|
||||
}
|
||||
PopupAction(
|
||||
icon = Icons.Outlined.ContentPaste,
|
||||
text = stringRes(R.string.clip__paste_item),
|
||||
) {
|
||||
clipboardManager.pasteItem(popupItem!!)
|
||||
popupItem = null
|
||||
}
|
||||
popupItem = null
|
||||
}
|
||||
PopupAction(
|
||||
iconId = R.drawable.ic_delete,
|
||||
text = stringRes(R.string.clip__delete_item),
|
||||
) {
|
||||
clipboardManager.deleteClip(popupItem!!)
|
||||
popupItem = null
|
||||
}
|
||||
PopupAction(
|
||||
iconId = R.drawable.ic_content_paste,
|
||||
text = stringRes(R.string.clip__paste_item),
|
||||
) {
|
||||
clipboardManager.pasteItem(popupItem!!)
|
||||
popupItem = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showClearAllHistory) {
|
||||
SnyggRow(
|
||||
modifier = Modifier
|
||||
@@ -407,7 +566,13 @@ fun ClipboardInputLayout(
|
||||
) {
|
||||
SnyggText(
|
||||
elementName = FlorisImeUi.ClipboardClearAllDialogMessage.elementName,
|
||||
text = stringRes(R.string.clipboard__confirm_clear_history__message),
|
||||
text = stringRes(
|
||||
if (isFilterRowShown) {
|
||||
R.string.clipboard__confirm_clear_filtered_history__message
|
||||
} else {
|
||||
R.string.clipboard__confirm_clear_unfiltered_history__message
|
||||
}
|
||||
),
|
||||
)
|
||||
SnyggRow(FlorisImeUi.ClipboardClearAllDialogButtons.elementName) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
@@ -426,8 +591,8 @@ fun ClipboardInputLayout(
|
||||
elementName = FlorisImeUi.ClipboardClearAllDialogButton.elementName,
|
||||
attributes = mapOf("action" to "yes"),
|
||||
onClick = {
|
||||
clipboardManager.clearHistory()
|
||||
context.showShortToast(R.string.clipboard__cleared_history)
|
||||
clipboardManager.clearExactHistory(filteredHistory.unpinned)
|
||||
context.showShortToastSync(R.string.clipboard__cleared_history)
|
||||
showClearAllHistory = false
|
||||
},
|
||||
) {
|
||||
@@ -472,7 +637,7 @@ fun ClipboardInputLayout(
|
||||
text = stringRes(R.string.clipboard__disabled__message),
|
||||
)
|
||||
SnyggButton(FlorisImeUi.ClipboardHistoryDisabledButton.elementName,
|
||||
onClick = { prefs.clipboard.historyEnabled.set(true) },
|
||||
onClick = { scope.launch { prefs.clipboard.historyEnabled.set(true) } },
|
||||
modifier = Modifier.align(Alignment.End),
|
||||
) {
|
||||
SnyggText(
|
||||
@@ -509,7 +674,7 @@ fun ClipboardInputLayout(
|
||||
HistoryLockedView()
|
||||
} else {
|
||||
if (historyEnabled) {
|
||||
if (history.all.isNotEmpty()) {
|
||||
if (filteredHistory.all.isNotEmpty() || !activeFilterTypes.isEmpty()) {
|
||||
HistoryMainView()
|
||||
} else {
|
||||
HistoryEmptyView()
|
||||
@@ -534,36 +699,40 @@ private fun ClipCategoryTitle(
|
||||
|
||||
@Composable
|
||||
private fun ClipTextItemDescription(
|
||||
elementName: String,
|
||||
attributes: SnyggQueryAttributes,
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
): Unit = with(LocalDensity.current) {
|
||||
val iconId: Int?
|
||||
val icon: ImageVector?
|
||||
val description: String?
|
||||
when {
|
||||
NetworkUtils.isEmailAddress(text) -> {
|
||||
iconId = R.drawable.ic_email
|
||||
icon = Icons.Outlined.Email
|
||||
description = stringRes(R.string.clipboard__item_description_email)
|
||||
}
|
||||
NetworkUtils.isUrl(text) -> {
|
||||
iconId = R.drawable.ic_link
|
||||
icon = Icons.Default.Link
|
||||
description = stringRes(R.string.clipboard__item_description_url)
|
||||
}
|
||||
NetworkUtils.isPhoneNumber(text) -> {
|
||||
iconId = R.drawable.ic_phone
|
||||
icon = Icons.Default.Phone
|
||||
description = stringRes(R.string.clipboard__item_description_phone)
|
||||
}
|
||||
else -> {
|
||||
iconId = null
|
||||
icon = null
|
||||
description = null
|
||||
}
|
||||
}
|
||||
if (iconId != null && description != null) {
|
||||
if (icon != null && description != null) {
|
||||
SnyggRow(
|
||||
elementName = elementName,
|
||||
attributes = attributes,
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
SnyggIcon(
|
||||
painter = painterResource(id = iconId),
|
||||
imageVector = icon,
|
||||
)
|
||||
SnyggText(
|
||||
modifier = Modifier.weight(1f),
|
||||
@@ -575,19 +744,19 @@ private fun ClipTextItemDescription(
|
||||
|
||||
@Composable
|
||||
private fun PopupAction(
|
||||
@DrawableRes iconId: Int,
|
||||
icon: ImageVector,
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
SnyggRow(FlorisImeUi.ClipboardItemPopupAction.elementName,
|
||||
SnyggRow(FlorisImeUi.ClipboardItemAction.elementName,
|
||||
modifier = modifier.rippleClickable(onClick = onClick),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
SnyggIcon(FlorisImeUi.ClipboardItemPopupActionIcon.elementName,
|
||||
painter = painterResource(iconId),
|
||||
SnyggIcon(FlorisImeUi.ClipboardItemActionIcon.elementName,
|
||||
imageVector = icon,
|
||||
)
|
||||
SnyggText(FlorisImeUi.ClipboardItemPopupActionText.elementName,
|
||||
SnyggText(FlorisImeUi.ClipboardItemActionText.elementName,
|
||||
modifier = Modifier.weight(1f),
|
||||
text = text,
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user