Compare commits
116 Commits
v0.4.0-bet
...
feat/flest
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f41385ae75 | ||
|
|
5c85be61d9 | ||
|
|
639beb9e64 | ||
|
|
453fb0253a | ||
|
|
13fc7679a2 | ||
|
|
2421d13038 | ||
|
|
7dedfd4f7a | ||
|
|
ef37194900 | ||
|
|
58134b1ceb | ||
|
|
53cfbad404 | ||
|
|
d6f724e518 | ||
|
|
6c4aa36b06 | ||
|
|
edc38b6c2c | ||
|
|
891a2c6bac | ||
|
|
229237153b | ||
|
|
290fbb5239 | ||
|
|
409d4f9348 | ||
|
|
82938cda5b | ||
|
|
f7b0a30271 | ||
|
|
575f359a85 | ||
|
|
22591163b3 | ||
|
|
8104ae60ca | ||
|
|
165b682732 | ||
|
|
eb770fac6c | ||
|
|
39c27426a4 | ||
|
|
228d5055cc | ||
|
|
b400e04560 | ||
|
|
27c1bbf039 | ||
|
|
f61b655f7d | ||
|
|
f82af63e97 | ||
|
|
0fbd950f6e | ||
|
|
e97b5f54ac | ||
|
|
b611360dd5 | ||
|
|
1b9d260020 | ||
|
|
d74fe62bc0 | ||
|
|
fe6f61a282 | ||
|
|
8b4239d9be | ||
|
|
a0c7cf2794 | ||
|
|
7480d14a0f | ||
|
|
7274228a46 | ||
|
|
a38f6a2c76 | ||
|
|
eda6c09538 | ||
|
|
9e42d16cb0 | ||
|
|
11ba51c354 | ||
|
|
51f5196b8a | ||
|
|
56bbe9d13c | ||
|
|
4d1ae52dc0 | ||
|
|
1e1916194b | ||
|
|
bae3c8ec9d | ||
|
|
9ff7d86a8d | ||
|
|
89ab0731d2 | ||
|
|
887a75a482 | ||
|
|
e52bea2456 | ||
|
|
2171e16346 | ||
|
|
566b6fbae3 | ||
|
|
5215227793 | ||
|
|
671f97eddb | ||
|
|
b6c9469826 | ||
|
|
77e4414467 | ||
|
|
db85e05714 | ||
|
|
51890c93d4 | ||
|
|
1f16ac2c3b | ||
|
|
199211fdbf | ||
|
|
c5ee414ec6 | ||
|
|
f1c5b1802b | ||
|
|
0450c8c7a1 | ||
|
|
6da6da74fc | ||
|
|
d7fca0aad1 | ||
|
|
1af519e01d | ||
|
|
989d2884b1 | ||
|
|
d137155ab0 | ||
|
|
270ab4fe5f | ||
|
|
be4cd4d897 | ||
|
|
e46d53291b | ||
|
|
8292f9d4cc | ||
|
|
192412b6dd | ||
|
|
a7f8980d35 | ||
|
|
f5d80a5818 | ||
|
|
d9b940f4f3 | ||
|
|
d86fc13cff | ||
|
|
5b7727b884 | ||
|
|
b0649b1b7e | ||
|
|
6244198795 | ||
|
|
bd9f7750aa | ||
|
|
c909d3ad7d | ||
|
|
736411e4f3 | ||
|
|
9c7d980b3b | ||
|
|
4d04eb1bb5 | ||
|
|
68c55d66be | ||
|
|
e1550d813b | ||
|
|
f780ef0213 | ||
|
|
025620a262 | ||
|
|
4b83c907c3 | ||
|
|
5542a131b9 | ||
|
|
eb50498890 | ||
|
|
e6a408fbc0 | ||
|
|
9d76b684be | ||
|
|
b97cc52958 | ||
|
|
aac7134433 | ||
|
|
f1d60d9958 | ||
|
|
e520a9c335 | ||
|
|
30294b02b4 | ||
|
|
f1bdf216fc | ||
|
|
82d43a53cc | ||
|
|
c51a787ac4 | ||
|
|
f116e20829 | ||
|
|
baf2cbcd13 | ||
|
|
1edb90b0f7 | ||
|
|
4c0c3f52e7 | ||
|
|
d23575375d | ||
|
|
e95bbf5192 | ||
|
|
0a4a4418ca | ||
|
|
2c653853e2 | ||
|
|
6f169997e9 | ||
|
|
f2e76cc72a | ||
|
|
3da59cc94b |
@@ -9,7 +9,7 @@ insert_final_newline = true
|
||||
max_line_length = 120
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[{*.har,*.json,*yml}]
|
||||
[{*.har,*.json,*yml,*.sh}]
|
||||
indent_size = 2
|
||||
|
||||
[*.kt]
|
||||
|
||||
17
.github/workflows/android.yml
vendored
17
.github/workflows/android.yml
vendored
@@ -2,7 +2,7 @@ name: FlorisBoard CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ main ]
|
||||
paths-ignore:
|
||||
- ".github/ISSUE_TEMPLATE/**"
|
||||
- ".github/FUNDING.yml"
|
||||
@@ -14,31 +14,28 @@ on:
|
||||
- "README.md"
|
||||
- "ROADMAP.md"
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- uses: gradle/actions/wrapper-validation@v3
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
cache: gradle
|
||||
- name: Set up CMake and Ninja
|
||||
uses: lukka/get-cmake@latest
|
||||
- name: Build with Gradle
|
||||
# MUST call gradlew separately because of an OSS license plugin issue.
|
||||
# See https://github.com/google/play-services-plugins/issues/199
|
||||
run: ./gradlew clean && ./gradlew assembleDebug
|
||||
- uses: actions/upload-artifact@v3
|
||||
run: ./gradlew clean assembleDebug
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: app-debug.apk
|
||||
path: app/build/outputs/apk/debug/app-debug.apk
|
||||
|
||||
6
.github/workflows/crowdin-upload.yml
vendored
6
.github/workflows/crowdin-upload.yml
vendored
@@ -2,7 +2,7 @@ name: Crowdin Upload Sources
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- "app/src/main/res/values/strings.xml"
|
||||
- ".github/workflows/crowdin-upload.yml"
|
||||
@@ -13,9 +13,9 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
- name: Upload
|
||||
uses: crowdin/github-action@1.4.0
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
config: "crowdin.yml"
|
||||
upload_sources: true
|
||||
|
||||
61
.github/workflows/validate-strings-no-translations.yml
vendored
Normal file
61
.github/workflows/validate-strings-no-translations.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
name: Validate no translated strings.xml included
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Precheck if validation is required
|
||||
id: precheck
|
||||
run: |
|
||||
pr_author="${{ github.event.pull_request.user.login }}"
|
||||
if [[ "$pr_author" == "florisboard-bot" ]]; then
|
||||
echo "PR is by florisboard-bot, skipping validation!"
|
||||
echo "require_validation=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "PR is not by florisboard-bot, requiring validation!"
|
||||
echo "require_validation=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Fetch PR changed files manually
|
||||
id: fetch_changed_files
|
||||
if: steps.precheck.outputs.require_validation == 'true'
|
||||
run: |
|
||||
pr_files="$(curl -sSf https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files?per_page=1000)" || exit 11
|
||||
changed_files="$(jq -r '.[].filename' <<< "$pr_files")" || exit 12
|
||||
illegal_changes_list="$(grep -E '^app/src/main/res/values-.+/strings.xml$' <<< "$changed_files")" || true
|
||||
if [ -n "$illegal_changes_list" ]; then
|
||||
echo -e "Illegal changes detected:\n$illegal_changes_list"
|
||||
else
|
||||
echo "No illegal changes detected"
|
||||
fi
|
||||
echo "illegal_changes_list<<EOF" >> "$GITHUB_OUTPUT"
|
||||
echo "$illegal_changes_list" >> "$GITHUB_OUTPUT"
|
||||
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Create comment if illegal files detected
|
||||
uses: peter-evans/create-or-update-comment@v4
|
||||
if: steps.precheck.outputs.require_validation == 'true' && steps.fetch_changed_files.outputs.illegal_changes_list != ''
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body: |
|
||||
⚠️ Illegal changes detected
|
||||
|
||||
Hey there!
|
||||
|
||||
We detected illegal changes that disobey the [contribution guidelines](https://github.com/florisboard/florisboard/blob/main/CONTRIBUTING.md#translation). This is a kind reminder that pull requests must not contain translated `strings.xml` files, as those are exclusively managed from Crowdin.
|
||||
|
||||
Please remove changes to the following files:
|
||||
```
|
||||
${{ steps.fetch_changed_files.outputs.illegal_changes_list }}
|
||||
```
|
||||
|
||||
- name: Fail workflow if illegal files detected
|
||||
if: steps.precheck.outputs.require_validation == 'true' && steps.fetch_changed_files.outputs.illegal_changes_list != ''
|
||||
run: echo -e "Illegal changes detected:\n${{ steps.fetch_changed_files.outputs.illegal_changes_list }}" && exit 1
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,7 +1,11 @@
|
||||
# Built application files
|
||||
*.apk
|
||||
*.aab
|
||||
*.ap_
|
||||
|
||||
# dotenv
|
||||
.env
|
||||
|
||||
# Files for the ART/Dalvik VM
|
||||
*.dex
|
||||
|
||||
@@ -54,6 +58,5 @@ result
|
||||
# Rust
|
||||
debug/
|
||||
target/
|
||||
Cargo.lock
|
||||
**/*.rs.bk
|
||||
*.pdb
|
||||
|
||||
226
.idea/icon.svg
generated
Normal file
226
.idea/icon.svg
generated
Normal file
@@ -0,0 +1,226 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="vector"
|
||||
width="108"
|
||||
height="108"
|
||||
viewBox="0 0 108 108"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs10">
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="20.594"
|
||||
cx="54.141998"
|
||||
cy="48.769001"
|
||||
id="gradient_0"
|
||||
gradientTransform="matrix(2.193116,0,0,2.193116,-64.427669,-59.442216)">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#55E032FF"
|
||||
id="stop1" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#227C53FF"
|
||||
id="stop2" />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="20.594"
|
||||
cx="54.141998"
|
||||
cy="48.769001"
|
||||
id="gradient_1"
|
||||
gradientTransform="matrix(2.193116,0,0,2.193116,-64.427669,-59.442216)">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#55E032FF"
|
||||
id="stop3" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#227C53FF"
|
||||
id="stop4" />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="12.849"
|
||||
cx="54.029999"
|
||||
cy="39.144001"
|
||||
id="gradient_2"
|
||||
gradientTransform="matrix(2.193116,0,0,2.193116,-64.427669,-59.442216)">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#FE7901FF"
|
||||
id="stop5" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#FEBE01FF"
|
||||
id="stop6" />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="20.594"
|
||||
cx="54.141998"
|
||||
cy="48.769001"
|
||||
id="gradient_3"
|
||||
gradientTransform="matrix(2.193116,0,0,2.193116,-64.427669,-59.442216)">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#55E032FF"
|
||||
id="stop7" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#227C53FF"
|
||||
id="stop8" />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="17.913"
|
||||
cx="54.131001"
|
||||
cy="49.819"
|
||||
id="gradient_4"
|
||||
gradientTransform="matrix(2.193116,0,0,2.193116,-64.427669,-59.442216)">
|
||||
<stop
|
||||
offset="0"
|
||||
stop-color="#FE7901FF"
|
||||
id="stop9" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#FEBE01FF"
|
||||
id="stop10" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
<path
|
||||
fill="url(#gradient_0)"
|
||||
d="m 12.291915,34.438502 0.434238,0.0066 c 20.014377,0.256595 36.184221,16.58873 36.184221,36.666707 v 6.504782 C 46.434346,77.090222 43.999987,76.362108 41.642388,75.436613 27.045007,69.710386 16.044337,56.538532 13.081437,41.131892 12.655973,38.923423 12.392799,36.686445 12.289722,34.440694 Z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_0"
|
||||
style="fill:url(#gradient_0);stroke-width:2.19311" />
|
||||
<path
|
||||
fill="url(#gradient_1)"
|
||||
d="m 41.438428,33.83978 c 1.451843,-7.875479 5.219616,-15.404446 11.305513,-21.490344 0.497838,-0.497837 1.004447,-0.980322 1.524216,-1.449649 0.517575,0.469327 1.026378,0.951812 1.526409,1.449649 6.074931,6.077125 9.842705,13.595127 11.29674,21.459641 -5.296375,4.153762 -9.689186,9.412854 -12.829729,15.428571 -3.136156,-5.974047 -7.53774,-11.23314 -12.823149,-15.395674 z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_1"
|
||||
style="fill:url(#gradient_1);stroke-width:2.19311" />
|
||||
<path
|
||||
fill="url(#gradient_2)"
|
||||
d="M 33.758135,28.900884 C 35.892037,20.613098 40.199317,12.774901 46.684361,6.2876637 48.478331,4.4936948 50.377569,2.8642095 52.362338,1.4035943 L 54.268157,0 56.176168,1.4035943 c 1.984769,1.4628084 3.884009,3.0901005 5.677977,4.8840694 6.480658,6.4806583 10.790132,14.3210483 12.924033,22.6022543 -2.015473,1.030764 -3.949802,2.199695 -5.783247,3.49802 C 67.332549,24.486141 63.437574,16.961559 57.307815,10.833993 56.548998,10.072982 55.768249,9.3492537 54.965567,8.6584222 L 54.265964,8.0553153 53.56636,8.6584222 C 52.765872,9.3492537 51.985123,10.072982 51.226304,10.833993 45.089966,16.970332 41.190606,24.503686 39.53261,32.414255 37.694779,31.111545 35.764837,29.933841 33.753749,28.900884 Z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_2"
|
||||
style="fill:url(#gradient_2);stroke-width:2.19311" />
|
||||
<path
|
||||
fill="url(#gradient_3)"
|
||||
d="m 96.244398,34.438502 -0.434237,0.0066 c -20.01657,0.256595 -36.186415,16.58873 -36.186415,36.666707 v 6.506976 C 79.890332,73.322449 95.279427,55.74901 96.244398,34.440694 Z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_3"
|
||||
style="fill:url(#gradient_3);stroke-width:2.19311" />
|
||||
<path
|
||||
fill="url(#gradient_4)"
|
||||
d="m 12.68887,25.874383 0.149132,0.0022 c 4.506853,0.05702 8.996162,0.787328 13.290282,2.168991 11.959063,3.851112 21.891685,12.759549 27.168322,24.058483 l 0.973744,2.083461 0.969357,-2.085654 C 62.347596,36.789522 77.776168,26.104661 95.702698,25.876577 l 0.149133,-0.0022 c 0.291684,2.098811 0.443009,4.241486 0.445201,6.421443 l -0.510995,0.0066 C 74.600536,32.576546 57.485458,49.860493 57.485458,71.111788 v 6.917088 c -1.048309,0.155711 -2.046178,0.188608 -3.079135,0.192995 h -0.13378 c -1.068047,0 -2.144867,-0.07238 -3.212915,-0.219313 v -6.89077 c 0,-21.251295 -17.117271,-38.535242 -38.300578,-38.809382 l -0.510997,-0.0066 c 0,-2.147061 0.149133,-4.296315 0.445203,-6.421443 z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_4"
|
||||
style="fill:url(#gradient_4);stroke-width:2.19311" />
|
||||
<path
|
||||
fill="#53cd53"
|
||||
d="m 101.9772,99.716601 c 0,-2.46287 -1.997925,-4.462992 -4.460795,-4.462992 H 85.243728 c -2.460676,0 -4.460798,1.997929 -4.460798,4.462992 v 3.822599 c 0,2.46068 1.997928,4.4608 4.460798,4.4608 h 12.272677 c 2.460676,0 4.460795,-1.99793 4.460795,-4.4608 z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_5"
|
||||
style="stroke-width:2.19311" />
|
||||
<path
|
||||
fill="#ffffff"
|
||||
d="m 78.620517,84.818763 c 0,-2.460676 -1.997928,-4.460798 -4.460797,-4.460798 h -6.096864 c -2.460676,0 -4.460798,1.997929 -4.460798,4.460798 v 3.824795 c 0,2.460676 1.997929,4.460798 4.460798,4.460798 h 6.096864 c 2.460676,0 4.460797,-1.997929 4.460797,-4.460798 z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_6"
|
||||
style="stroke-width:2.19311" />
|
||||
<path
|
||||
fill="#ffffff"
|
||||
d="m 61.450612,84.818763 c 0,-2.460676 -1.997929,-4.460798 -4.460798,-4.460798 h -6.09467 c -2.460676,0 -4.460798,1.997929 -4.460798,4.460798 v 3.824795 c 0,2.460676 1.997929,4.460798 4.460798,4.460798 h 6.09467 c 2.460676,0 4.460798,-1.997929 4.460798,-4.460798 z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_7"
|
||||
style="stroke-width:2.19311" />
|
||||
<path
|
||||
fill="#ffffff"
|
||||
d="m 27.11738,84.818763 c 0,-2.460676 -1.997929,-4.460798 -4.460798,-4.460798 h -6.096863 c -2.460676,0 -4.460798,1.997929 -4.460798,4.460798 v 3.824795 c 0,2.460676 1.997929,4.460798 4.460798,4.460798 h 6.096863 c 2.460676,0 4.460798,-1.997929 4.460798,-4.460798 z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_8"
|
||||
style="stroke-width:2.19311" />
|
||||
<path
|
||||
fill="#ffffff"
|
||||
d="m 95.78823,84.818763 c 0,-2.460676 -1.997929,-4.460798 -4.460798,-4.460798 h -6.09467 c -2.462869,0 -4.462991,1.997929 -4.462991,4.460798 v 3.824795 c 0,2.460676 1.997928,4.460798 4.462991,4.460798 h 6.09467 c 2.460676,0 4.460798,-1.997929 4.460798,-4.460798 z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_9"
|
||||
style="stroke-width:2.19311" />
|
||||
<path
|
||||
fill="#ffffff"
|
||||
d="m 21.038062,69.923119 c 0,-2.46287 -1.997928,-4.462991 -4.460797,-4.462991 h -6.09467 c -2.4606765,0 -4.4607984,1.997929 -4.4607984,4.462991 v 3.822601 c 0,2.460676 1.9979288,4.460798 4.4607984,4.460798 h 6.09467 c 2.460676,0 4.460797,-1.997928 4.460797,-4.460798 z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_10"
|
||||
style="stroke-width:2.19311" />
|
||||
<path
|
||||
fill="#ffffff"
|
||||
d="m 101.9772,69.923119 c 0,-2.46287 -1.997925,-4.462991 -4.460795,-4.462991 h -6.09467 c -2.460676,0 -4.460797,1.997929 -4.460797,4.462991 v 3.822601 c 0,2.460676 1.997928,4.460798 4.460797,4.460798 h 6.09467 c 2.460676,0 4.460795,-1.997928 4.460795,-4.460798 z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_11"
|
||||
style="stroke-width:2.19311" />
|
||||
<path
|
||||
fill="#ffffff"
|
||||
d="m 44.282899,84.818763 c 0,-2.460676 -1.997928,-4.460798 -4.460798,-4.460798 h -6.09467 c -2.462869,0 -4.462991,1.997929 -4.462991,4.460798 v 3.824795 c 0,2.460676 1.99793,4.460798 4.462991,4.460798 h 6.09467 c 2.460676,0 4.460798,-1.997929 4.460798,-4.460798 z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_12"
|
||||
style="stroke-width:2.19311" />
|
||||
<path
|
||||
fill="#fe9801"
|
||||
d="m 27.21607,99.716601 c 0,-2.46287 -1.997928,-4.462992 -4.460798,-4.462992 H 10.482595 c -2.4606765,0 -4.4607984,1.997929 -4.4607984,4.462992 v 3.822599 c 0,2.46068 1.9979288,4.4608 4.4607984,4.4608 h 12.272677 c 2.460676,0 4.460798,-1.99793 4.460798,-4.4608 z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_13"
|
||||
style="stroke-width:2.19311" />
|
||||
<path
|
||||
fill="#ffffff"
|
||||
d="m 83.756795,67.041365 c 0.655742,0.776363 1.054889,1.783003 1.054889,2.881754 v 3.822601 c 0,2.460676 -1.997928,4.460798 -4.460798,4.460798 h -6.096863 c -1.557112,0 -2.927809,-0.798293 -3.723911,-2.006701 4.91258,-2.307158 9.377765,-5.414803 13.226683,-9.158452 z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_14"
|
||||
style="stroke-width:2.19311" />
|
||||
<path
|
||||
fill="#ffffff"
|
||||
d="m 24.529503,66.738714 c 3.796284,3.76558 8.20664,6.919282 13.066585,9.263722 -0.774169,1.320257 -2.210661,2.204082 -3.848919,2.204082 h -6.096862 c -2.460676,0 -4.460798,-1.997928 -4.460798,-4.460798 v -3.822601 c 0,-1.247883 0.510996,-2.375144 1.337801,-3.186598 z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_15"
|
||||
style="stroke-width:2.19311" />
|
||||
<path
|
||||
fill="#ffffff"
|
||||
d="m 14.015705,51.362778 c 1.868535,4.322632 4.359915,8.346999 7.366677,11.950289 h -4.822663 c -2.460676,0 -4.460798,-1.997928 -4.460798,-4.460797 v -3.824795 c 0,-1.517637 0.758819,-2.85763 1.916784,-3.664697 z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_16"
|
||||
style="stroke-width:2.19311" />
|
||||
<path
|
||||
fill="#ffffff"
|
||||
d="m 94.369284,51.764118 c 0.87286,0.813646 1.418946,1.975997 1.418946,3.263357 v 3.824795 c 0,2.460676 -1.997929,4.460797 -4.460798,4.460797 h -4.116479 c 2.897106,-3.471703 5.313921,-7.355711 7.156137,-11.548949 z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_17"
|
||||
style="stroke-width:2.19311" />
|
||||
<path
|
||||
fill="#ffffff"
|
||||
d="m 78.782808,99.819677 c 0,-2.432165 -1.971611,-4.40597 -4.403777,-4.40597 H 33.738397 c -2.432166,0 -4.405969,1.973805 -4.405969,4.40597 v 3.774353 c 0,2.43217 1.973803,4.40378 4.405969,4.40378 h 40.640634 c 2.432166,0 4.403777,-1.97161 4.403777,-4.40378 z"
|
||||
stroke-linejoin="round"
|
||||
fill-rule="evenodd"
|
||||
id="path_18"
|
||||
style="stroke-width:2.19311" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
50
README.md
50
README.md
@@ -1,21 +1,19 @@
|
||||
<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) 
|
||||
# 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** is a free and open-source keyboard for Android 7.0+
|
||||
devices. It aims at being modern, user-friendly and customizable while
|
||||
fully respecting your privacy. Currently in early-beta state.
|
||||
|
||||
*Note: Due to various reasons development has been quite stuck in the past year, but things are slowly improving again and new releases/features will follow in the near future, please see the [roadmap](ROADMAP.md) for details!*
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th align="center" width="50%">
|
||||
<h3>Stable <a href="https://github.com/florisboard/florisboard/releases/latest"><img alt="Latest stable release" src="https://img.shields.io/github/v/release/florisboard/florisboard"></a></h3>
|
||||
<h3>Stable <a href="https://github.com/florisboard/florisboard/releases/latest"><img alt="Latest stable release" src="https://img.shields.io/github/v/release/florisboard/florisboard?sort=semver&display_name=tag&color=28a745"></a></h3>
|
||||
</th>
|
||||
<th align="center" width="50%">
|
||||
<h3>Beta <a href="https://github.com/florisboard/florisboard/releases"><img alt="Latest beta release" src="https://img.shields.io/github/v/release/florisboard/florisboard?include_prereleases"></a></h3>
|
||||
<h3>Preview <a href="https://github.com/florisboard/florisboard/releases"><img alt="Latest preview release" src="https://img.shields.io/github/v/release/florisboard/florisboard?include_prereleases&sort=semver&display_name=tag&color=fd7e14"></a></h3>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -23,7 +21,7 @@ fully respecting your privacy. Currently in early-beta state.
|
||||
<p><i>Major versions only</i><br><br>Updates are more polished, new features are matured and tested through to ensure a stable experience.</p>
|
||||
</td>
|
||||
<td valign="top">
|
||||
<p><i>Alpha/Beta versions</i><br><br>Updates contain new features that may not be fully matured yet and bugs are more likely to occur. Allows you to give early feedback.</p>
|
||||
<p><i>Major + Alpha/Beta/Rc versions</i><br><br>Updates contain new features that may not be fully matured yet and bugs are more likely to occur. Allows you to give early feedback.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -36,6 +34,11 @@ fully respecting your privacy. Currently in early-beta state.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
**Obtainium**: [Auto-import stable config][obtainium_stable]
|
||||
|
||||
</p>
|
||||
<p>
|
||||
|
||||
**Manual**: Download and install the APK from the release page.
|
||||
|
||||
</p>
|
||||
@@ -44,7 +47,12 @@ 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 [beta 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-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))
|
||||
|
||||
</p>
|
||||
<p>
|
||||
|
||||
**Obtainium**: [Auto-import preview config][obtainium_preview]
|
||||
|
||||
</p>
|
||||
<p>
|
||||
@@ -56,16 +64,17 @@ fully respecting your privacy. Currently in early-beta state.
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Beginning with v0.4.0 FlorisBoard will follow [SemVer](https://semver.org/#summary) versioning scheme.
|
||||
Beginning with v0.6.0 FlorisBoard will enter the public beta on Google Play.
|
||||
|
||||
## Highlighted features
|
||||
- Integrated clipboard manager / history
|
||||
- Advanced theming support and customization
|
||||
- Integrated extension support (still evolving)
|
||||
- Emoji keyboard
|
||||
- Emoji keyboard / history / suggestions
|
||||
|
||||
Word suggestions/spell checking are not included in the current releases and are a major goal for the v0.5.0 milestone.
|
||||
> [!IMPORTANT]
|
||||
> Word suggestions/spell checking are not included in the current releases
|
||||
> and are a major goal for the v0.5 milestone.
|
||||
|
||||
Feature roadmap: See [ROADMAP.md](ROADMAP.md)
|
||||
|
||||
@@ -73,6 +82,16 @@ Feature roadmap: See [ROADMAP.md](ROADMAP.md)
|
||||
Want to contribute to FlorisBoard? That's great to hear! There are lots of
|
||||
different ways to help out, please see the [contribution guidelines](CONTRIBUTING.md) for more info.
|
||||
|
||||
## Addons Store
|
||||
The official [Addons Store](https://beta.addons.florisboard.org) offers the possibility for the community to share and download FlorisBoard extensions.
|
||||
Instructions on how to publish addons can be found [here](https://github.com/florisboard/florisboard/wiki/How-to-publish-on-FlorisBoard-Addons).
|
||||
|
||||
Many thanks to Ali ([@4H1R](https://github.com/4H1R)) for implementing the store!
|
||||
|
||||
> [!NOTE]
|
||||
> During the initial beta release phase, the Addons Store _will_ only accept theme extensions.
|
||||
> Later on we plan to add support for language packs and keyboard extensions.
|
||||
|
||||
## List of permissions FlorisBoard requests
|
||||
Please refer to this [page](https://github.com/florisboard/florisboard/wiki/List-of-permissions-FlorisBoard-requests)
|
||||
to get more information on this topic.
|
||||
@@ -80,8 +99,6 @@ to get more information on this topic.
|
||||
## Used libraries, components and icons
|
||||
* [AndroidX libraries](https://github.com/androidx/androidx) by
|
||||
[Android Jetpack](https://github.com/androidx)
|
||||
* [Accompanist Compose UI libraries](https://github.com/google/accompanist/) by
|
||||
[Google](https://github.com/google)
|
||||
* [AboutLibraries](https://github.com/mikepenz/AboutLibraries) by
|
||||
[mikepenz](https://github.com/mikepenz)
|
||||
* [Google Material icons](https://github.com/google/material-design-icons) by
|
||||
@@ -92,8 +109,6 @@ to get more information on this topic.
|
||||
[Kotlin](https://github.com/Kotlin)
|
||||
* [KotlinX serialization library](https://github.com/Kotlin/kotlinx.serialization) by
|
||||
[Kotlin](https://github.com/Kotlin)
|
||||
* [ICU4C](https://github.com/unicode-org/icu) by
|
||||
[The Unicode Consortium](https://github.com/unicode-org)
|
||||
|
||||
Many thanks to [Nikolay Anzarov](https://www.behance.net/nikolayanzarov) ([@BloodRaven0](https://github.com/BloodRaven0)) for designing and providing the main app icons to this project!
|
||||
|
||||
@@ -113,3 +128,10 @@ 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.
|
||||
```
|
||||
|
||||
<!-- BEGIN SECTION: obtainium_links -->
|
||||
<!-- auto-generated link templates, do NOT edit by hand -->
|
||||
<!-- see fastlane/update-readme.sh -->
|
||||
[obtainium_preview]: https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/%7B%22id%22%3A%22dev.patrickgold.florisboard.beta%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fflorisboard%2Fflorisboard%22%2C%22author%22%3A%22florisboard%22%2C%22name%22%3A%22FlorisBoard%20Preview%22%2C%22additionalSettings%22%3A%22%7B%5C%22includePrereleases%5C%22%3Atrue%2C%5C%22fallbackToOlderReleases%5C%22%3Atrue%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22preview%5C%22%7D%22%7D%0A
|
||||
[obtainium_stable]: https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/%7B%22id%22%3A%22dev.patrickgold.florisboard%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fflorisboard%2Fflorisboard%22%2C%22author%22%3A%22florisboard%22%2C%22name%22%3A%22FlorisBoard%20Stable%22%2C%22additionalSettings%22%3A%22%7B%5C%22includePrereleases%5C%22%3Afalse%2C%5C%22fallbackToOlderReleases%5C%22%3Atrue%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22stable%5C%22%7D%22%7D%0A
|
||||
<!-- END SECTION: obtainium_links -->
|
||||
|
||||
34
ROADMAP.md
34
ROADMAP.md
@@ -2,22 +2,7 @@
|
||||
|
||||
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.
|
||||
|
||||
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 beta tracks.
|
||||
|
||||
## 0.4
|
||||
|
||||
**Main focus**: Getting the project back on track, see [this announcement](https://github.com/florisboard/florisboard/discussions/2314) for details. Note that this has also replaced the previous roadmap, however this step is necessary for getting the project back on track again.
|
||||
|
||||
This includes, but is not exclusive to:
|
||||
- Fixing the most reported bugs/issues
|
||||
- Merging in the Material You theme PR -> Adds Material You support (v0.4.0-alpha05)
|
||||
- Merging in other external PRs as best as possible
|
||||
- Reworking the Settings UI warning boxes and hiding any UI for features related to word suggestions until they are ready
|
||||
- Remove existing glide/swipe typing (see 0.5 milestone)
|
||||
- Improvements in clipboard / emoji functionality (v0.4.0-beta01/beta02)
|
||||
- Prepare project to have native code implemented in [Rust](https://www.rust-lang.org/) (v0.4.0-beta02)
|
||||
|
||||
Note that the previous versioning scheme has been dropped in favor of using a major.minor.patch versioning scheme, so versions like `0.3.16` are a thing of the past :)
|
||||
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
|
||||
|
||||
@@ -26,39 +11,36 @@ Note that the previous versioning scheme has been dropped in favor of using a ma
|
||||
- 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
|
||||
- RFC document with technical details will be released later
|
||||
- New text processing logic (maybe moved back to 0.6)
|
||||
- RFC document with technical details will be released later
|
||||
- Add Tablet mode / Optimizations for landscape input based on new keyboard layout engine
|
||||
- Reimplementation of glide typing with the new layout engine and predictive text core
|
||||
- Add support for importing extensions via system file handler APIs (relevant for Addons store)
|
||||
- Add support for any remaining new features introduced with Android 13
|
||||
|
||||
## 0.6
|
||||
|
||||
- Complete rework of the Emoji panel
|
||||
- Recently used / Emoji history (already implemented with 0.3.14)
|
||||
- Emoji search
|
||||
- Emoji suggestions when using :emoji_name: syntax (already implemented with v0.4.0-beta02)
|
||||
- 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
|
||||
- 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)
|
||||
|
||||
## Backlog / Planned (unassigned)
|
||||
|
||||
**Features that MAY be added (even in versions mentioned above) or dismissed**
|
||||
|
||||
- Upgrade Settings UI to Material 3
|
||||
- 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
|
||||
- Floating keyboard
|
||||
- Stickers/GIFs
|
||||
- Kaomoji panel implementation
|
||||
- FlorisBoard landing web page for presentation
|
||||
|
||||
@@ -14,11 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.agp.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.plugin.compose)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
alias(libs.plugins.ksp)
|
||||
alias(libs.plugins.mannodermaus.android.junit5)
|
||||
@@ -32,7 +34,7 @@ val projectBuildToolsVersion: String by project
|
||||
val projectNdkVersion: String by project
|
||||
val projectVersionCode: String by project
|
||||
val projectVersionName: String by project
|
||||
val projectVersionNameSuffix: String by project
|
||||
val projectVersionNameSuffix = projectVersionName.substringAfter("-", "")
|
||||
|
||||
android {
|
||||
namespace = "dev.patrickgold.florisboard"
|
||||
@@ -48,7 +50,6 @@ android {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
freeCompilerArgs = listOf(
|
||||
"-Xallow-result-return-type",
|
||||
"-opt-in=kotlin.contracts.ExperimentalContracts",
|
||||
"-Xjvm-default=all-compatibility",
|
||||
)
|
||||
@@ -59,11 +60,13 @@ android {
|
||||
minSdk = projectMinSdk.toInt()
|
||||
targetSdk = projectTargetSdk.toInt()
|
||||
versionCode = projectVersionCode.toInt()
|
||||
versionName = projectVersionName
|
||||
versionName = projectVersionName.substringBefore("-")
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
buildConfigField("String", "BUILD_COMMIT_HASH", "\"${getGitCommitHash()}\"")
|
||||
buildConfigField("String", "FLADDONS_API_VERSION", "\"v~draft2\"")
|
||||
buildConfigField("String", "FLADDONS_STORE_URL", "\"beta.addons.florisboard.org\"")
|
||||
|
||||
ksp {
|
||||
arg("room.schemaLocation", "$projectDir/schemas")
|
||||
@@ -97,14 +100,10 @@ android {
|
||||
compose = true
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get()
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
named("debug") {
|
||||
applicationIdSuffix = ".debug"
|
||||
versionNameSuffix = "-debug-${getGitCommitHash(short = true)}"
|
||||
versionNameSuffix = "-debug+${getGitCommitHash(short = true)}"
|
||||
|
||||
isDebuggable = true
|
||||
isJniDebuggable = false
|
||||
@@ -163,23 +162,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()
|
||||
}
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.accompanist.systemuicontroller)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.activity.ktx)
|
||||
implementation(libs.androidx.autofill)
|
||||
implementation(libs.androidx.collection.ktx)
|
||||
implementation(libs.androidx.compose.material)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
implementation(libs.androidx.compose.runtime.livedata)
|
||||
implementation(libs.androidx.compose.ui)
|
||||
@@ -204,8 +201,10 @@ dependencies {
|
||||
implementation(libs.patrickgold.jetpref.datastore.ui)
|
||||
implementation(libs.patrickgold.jetpref.material.ui)
|
||||
|
||||
implementation(project(":lib:android"))
|
||||
implementation(project(":lib:kotlin"))
|
||||
implementation(project(":lib:native"))
|
||||
implementation(project(":lib:snygg"))
|
||||
|
||||
testImplementation(libs.equalsverifier)
|
||||
testImplementation(libs.kotest.assertions.core)
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 3,
|
||||
"identityHash": "282a1b421e498fd0e21c055b6a4315e0",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "clipboard_history",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` INTEGER NOT NULL, `text` TEXT, `uri` TEXT, `creationTimestampMs` INTEGER NOT NULL, `isPinned` INTEGER NOT NULL, `mimeTypes` TEXT NOT NULL, `isSensitive` INTEGER NOT NULL, `isRemoteDevice` INTEGER NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "type",
|
||||
"columnName": "type",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "text",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uri",
|
||||
"columnName": "uri",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "creationTimestampMs",
|
||||
"columnName": "creationTimestampMs",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isPinned",
|
||||
"columnName": "isPinned",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mimeTypes",
|
||||
"columnName": "mimeTypes",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isSensitive",
|
||||
"columnName": "isSensitive",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isRemoteDevice",
|
||||
"columnName": "isRemoteDevice",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_clipboard_history__id",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_clipboard_history__id` ON `${TABLE_NAME}` (`_id`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '282a1b421e498fd0e21c055b6a4315e0')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -42,10 +42,11 @@
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/floris_app_icon"
|
||||
android:label="@string/floris_app_name"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
android:roundIcon="@mipmap/floris_app_icon_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/FlorisAppTheme"
|
||||
tools:targetApi="s">
|
||||
tools:targetApi="tiramisu">
|
||||
|
||||
<!-- Allow app to be profiled for benchmarking and baseline profile generation -->
|
||||
<profileable android:shell="true"/>
|
||||
@@ -84,9 +85,25 @@
|
||||
android:roundIcon="@mipmap/floris_app_icon_round"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:theme="@style/FlorisAppTheme.Splash"
|
||||
android:exported="false">
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<data android:scheme="florisboard" android:host="app-ui"/>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="ui" android:host="florisboard" android:pathPrefix="/" />
|
||||
</intent-filter>
|
||||
<intent-filter android:label="Import Extension">
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:scheme="content"/>
|
||||
<data android:mimeType="application/vnd.florisboard.extension+zip"/>
|
||||
<data android:mimeType="application/octet-stream"/><!-- Firefox looking at you :eyes: -->
|
||||
</intent-filter>
|
||||
<intent-filter android:label="Import Extension">
|
||||
<action android:name="android.intent.action.SEND"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="application/vnd.florisboard.extension+zip"/>
|
||||
<data android:mimeType="application/octet-stream"/><!-- Firefox looking at you :eyes: -->
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
@@ -106,24 +123,6 @@
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
|
||||
<!-- Import File Bridging Activity -->
|
||||
<activity
|
||||
android:name="dev.patrickgold.florisboard.app.ext.ImportFileActivity"
|
||||
android:icon="@mipmap/floris_app_icon"
|
||||
android:label="@string/settings__title"
|
||||
android:launchMode="singleTask"
|
||||
android:roundIcon="@mipmap/floris_app_icon_round"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:theme="@style/FlorisAppTheme"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:scheme="*" android:host="*" android:pathPattern=".*\\.flex"/>
|
||||
<data android:scheme="*" android:host="*" android:pathPattern=".*\\.xpi"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Crash Dialog Activity -->
|
||||
<activity
|
||||
android:name="dev.patrickgold.florisboard.lib.crashutility.CrashDialogActivity"
|
||||
@@ -133,7 +132,7 @@
|
||||
|
||||
<!-- Copy to Clipboard Activity -->
|
||||
<activity
|
||||
android:name="dev.patrickgold.florisboard.FlorisCopyToClipboardActivity"
|
||||
android:name="dev.patrickgold.florisboard.ime.clipboard.FlorisCopyToClipboardActivity"
|
||||
android:theme="@style/FlorisAppTheme.Transparent"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
|
||||
@@ -57,12 +57,89 @@
|
||||
"ừz": "ư", "ửz": "ư", "ữz": "ư", "ứz": "ư", "ựz": "ư",
|
||||
"ỳz": "y", "ỷz": "y", "ỹz": "y", "ýz": "y", "ỵz": "y",
|
||||
|
||||
"áf": "à", "ár": "ả", "áx": "ã", "áj": "ạ",
|
||||
"às": "á", "àr": "ả", "àx": "ã", "àj": "ạ",
|
||||
"ảs": "á", "ảf": "à", "ảx": "ã", "ảj": "ạ",
|
||||
"ãs": "á", "ãf": "à", "ãr": "ả", "ãj": "ạ",
|
||||
"ạs": "á", "ạf": "à", "ạr": "ả", "ạx": "ã",
|
||||
"ấf": "ầ", "ấr": "ẩ", "ấx": "ẫ", "ấj": "ậ",
|
||||
"ầs": "ấ", "ầr": "ẩ", "ầx": "ẫ", "ầj": "ậ",
|
||||
"ẩs": "ấ", "ẩf": "ầ", "ẩx": "ẫ", "ẩj": "ậ",
|
||||
"ẫs": "ấ", "ẫf": "ầ", "ẫr": "ẩ", "ẫj": "ậ",
|
||||
"ậs": "ấ", "ậf": "ầ", "ậr": "ẩ", "ậx": "ẫ",
|
||||
"ắf": "ằ", "ắr": "ẳ", "ắx": "ẵ", "ắj": "ặ",
|
||||
"ằs": "ắ", "ằr": "ẳ", "ằx": "ẵ", "ằj": "ặ",
|
||||
"ẳs": "ắ", "ẳf": "ằ", "ẳx": "ẵ", "ẳj": "ặ",
|
||||
"ẵs": "ắ", "ẵf": "ằ", "ẵr": "ẳ", "ẵj": "ặ",
|
||||
"ặs": "ắ", "ặf": "ằ", "ặr": "ẳ", "ặx": "ẵ",
|
||||
"éf": "è", "ér": "ẻ", "éx": "ẽ", "éj": "ẹ",
|
||||
"ès": "é", "èr": "ẻ", "èx": "ẽ", "èj": "ẹ",
|
||||
"ẻs": "é", "ẻf": "è", "ẻx": "ẽ", "ẻj": "ẹ",
|
||||
"ẽs": "é", "ẽf": "è", "ẽr": "ẻ", "ẽj": "ẹ",
|
||||
"ẹs": "é", "ẹf": "è", "ẹr": "ẻ", "ẹx": "ẽ",
|
||||
"ếf": "ề", "ếr": "ể", "ếx": "ễ", "ếj": "ệ",
|
||||
"ềs": "ế", "ềr": "ể", "ềx": "ễ", "ềj": "ệ",
|
||||
"ểs": "ế", "ểf": "ề", "ểx": "ễ", "ểj": "ệ",
|
||||
"ễs": "ế", "ễf": "ề", "ễr": "ể", "ễj": "ệ",
|
||||
"ệs": "ế", "ệf": "ề", "ệr": "ể", "ệx": "ễ",
|
||||
"íf": "ì", "ír": "ỉ", "íx": "ĩ", "íj": "ị",
|
||||
"ìs": "í", "ìr": "ỉ", "ìx": "ĩ", "ìj": "ị",
|
||||
"ỉs": "í", "ỉf": "ì", "ỉx": "ĩ", "ỉj": "ị",
|
||||
"ĩs": "í", "ĩf": "ì", "ĩr": "ỉ", "ĩj": "ị",
|
||||
"ịs": "í", "ịf": "ì", "ịr": "ỉ", "ịx": "ĩ",
|
||||
"óf": "ò", "ór": "ỏ", "óx": "õ", "ój": "ọ",
|
||||
"òs": "ó", "òr": "ỏ", "òx": "õ", "òj": "ọ",
|
||||
"ỏs": "ó", "ỏf": "ò", "ỏx": "õ", "ỏj": "ọ",
|
||||
"õs": "ó", "õf": "ò", "õr": "ỏ", "õj": "ọ",
|
||||
"ọs": "ó", "ọf": "ò", "ọr": "ỏ", "ọx": "õ",
|
||||
"ốf": "ồ", "ốr": "ổ", "ốx": "ỗ", "ốj": "ộ",
|
||||
"ồs": "ố", "ồr": "ổ", "ồx": "ỗ", "ồj": "ộ",
|
||||
"ổs": "ố", "ổf": "ồ", "ổx": "ỗ", "ổj": "ộ",
|
||||
"ỗs": "ố", "ỗf": "ồ", "ỗr": "ổ", "ỗj": "ộ",
|
||||
"ộs": "ố", "ộf": "ồ", "ộr": "ổ", "ộx": "ỗ",
|
||||
"ớf": "ờ", "ớr": "ở", "ớx": "ỡ", "ớj": "ợ",
|
||||
"ờs": "ớ", "ờr": "ở", "ờx": "ỡ", "ờj": "ợ",
|
||||
"ởs": "ớ", "ởf": "ờ", "ởx": "ỡ", "ởj": "ợ",
|
||||
"ỡs": "ớ", "ỡf": "ờ", "ỡr": "ở", "ỡj": "ợ",
|
||||
"ợs": "ớ", "ợf": "ờ", "ợr": "ở", "ợx": "ỡ",
|
||||
"úf": "ù", "úr": "ủ", "úx": "ũ", "új": "ụ",
|
||||
"ùs": "ú", "ùr": "ủ", "ùx": "ũ", "ùj": "ụ",
|
||||
"ủs": "ú", "ủf": "ù", "ủx": "ũ", "ủj": "ụ",
|
||||
"ũs": "ú", "ũf": "ù", "ũr": "ủ", "ũj": "ụ",
|
||||
"ụs": "ú", "ụf": "ù", "ụr": "ủ", "ụx": "ũ",
|
||||
"ứf": "ừ", "ứr": "ử", "ứx": "ữ", "ứj": "ự",
|
||||
"ừs": "ứ", "ừr": "ử", "ừx": "ữ", "ừj": "ự",
|
||||
"ửs": "ứ", "ửf": "ừ", "ửx": "ữ", "ửj": "ự",
|
||||
"ữs": "ứ", "ữf": "ừ", "ữr": "ử", "ữj": "ự",
|
||||
"ựs": "ứ", "ựf": "ừ", "ựr": "ử", "ựx": "ữ",
|
||||
"ýf": "ỳ", "ýr": "ỷ", "ýx": "ỹ", "ýj": "ỵ",
|
||||
"ỳs": "ý", "ỳr": "ỷ", "ỳx": "ỹ", "ỳj": "ỵ",
|
||||
"ỷs": "ý", "ỷf": "ỳ", "ỷx": "ỹ", "ỷj": "ỵ",
|
||||
"ỹs": "ý", "ỹf": "ỳ", "ỹr": "ỷ", "ỹj": "ỵ",
|
||||
"ỵs": "ý", "ỵf": "ỳ", "ỵr": "ỷ", "ỵx": "ỹ",
|
||||
|
||||
"gias": "giá", "giaf": "già", "giar": "giả", "giax": "giã", "giaj": "giạ",
|
||||
"gía": "giá", "gìa": "già", "gỉa": "giả", "gĩa": "giã", "gịa": "giạ",
|
||||
"gíă": "giắ", "gìă": "giằ", "gỉă": "giẳ", "gĩă": "giẵ", "gịă": "giặ",
|
||||
"gíâ": "giấ", "gìâ": "giầ", "gỉâ": "giẩ", "gĩẫ": "giẫ", "gịâ": "giậ",
|
||||
"gíe": "gié", "gìe": "giè", "gỉe": "giẻ", "gĩe": "giẽ", "gịe": "giẹ",
|
||||
"gíê": "giế", "gìê": "giề", "gỉê": "giể", "gĩê": "giễ", "gịê": "giệ",
|
||||
"gío": "gió", "gìo": "giò", "gỉo": "giỏ", "gĩo": "giõ", "gịo": "giọ",
|
||||
"gíô": "giố", "gìô": "giồ", "gỉô": "giổ", "gĩô": "giỗ", "gịô": "giộ",
|
||||
"gíơ": "giớ", "gìơ": "giờ", "gỉơ": "giở", "gĩơ": "giỡ", "gịơ": "giợ",
|
||||
"gius": "giú", "giuf": "giù", "giur": "giủ", "giux": "giũ", "giuj": "giụ",
|
||||
"gíu": "giú", "gìu": "giù", "gỉu": "giủ", "gĩu": "giũ", "gịu": "giụ",
|
||||
"gíư": "giứ", "gìư": "giừ", "gỉư": "giử", "gĩư": "giữ", "gịư": "giự",
|
||||
|
||||
"ghíê": "ghiế", "ghìê": "ghiề", "ghỉê": "ghiể", "ghĩê": "ghiễ", "ghịê": "ghiệ",
|
||||
|
||||
|
||||
"acw": "ăc", "amw": "ăm", "anw": "ăn", "apw": "ăp", "atw": "ăt", "angw": "ăng",
|
||||
"aca": "âc", "ama": "âm", "ana": "ân", "apa": "âp", "ata": "ât", "aua": "âu", "aya": "ây", "anga": "âng",
|
||||
"eme": "êm", "ene": "ên", "epe": "êp", "ete": "êt", "enhe": "ênh",
|
||||
"oio": "ôi","omo": "ôm", "ono": "ôn", "opo": "ôp", "oto": "ôt", "ongo": "ông",
|
||||
"oiw": "ơi", "omw": "ơm", "onw": "ơn", "opw": "ơp", "otw": "ơt",
|
||||
"uaw": "ưa", "uiw": "ưi", "umw": "ưm", "unw": "ưn", "utw": "ưt", "uuw": "ưu", "ungw": "ưng",
|
||||
"uaw": "ưa", "ucw": "ưc", "uiw": "ưi", "umw": "ưm", "unw": "ưn", "utw": "ưt", "uuw": "ưu", "ungw": "ưng",
|
||||
"ieme": "iêm", "iene": "iên", "iepe": "iêp", "iete": "iêt", "ieue": "iêu", "ienge": "iêng",
|
||||
"uocw": "ươc", "uoiw": "ươi", "uomw": "ươm", "uonw": "ươn", "uotw": "ươt", "uongw": "ương",
|
||||
"uoco": "uôc", "uoio": "uôi", "uomo": "uôm", "uono": "uôn", "uoto": "uôt", "uongo": "uông",
|
||||
@@ -79,7 +156,7 @@
|
||||
"ême": "eme", "êne": "ene", "êpe": "epe", "ête": "ete",
|
||||
"ôio": "oio", "ômo": "omo", "ôno": "ono", "ôpo": "opo", "ôto": "oto", "ôngo": "ongo",
|
||||
"ơmw": "omw", "ơnw": "onw", "ơpw": "opw", "ơtw": "otw",
|
||||
"ưaw": "uaw", "ưiw": "uiw", "ưmw": "umw", "ưnw": "unw" , "ưtw": "utw", "ưuw": "uuw", "ưngw": "ungw",
|
||||
"ưaw": "uaw", "ưcw": "ucw", "ưiw": "uiw", "ưmw": "umw", "ưnw": "unw" , "ưtw": "utw", "ưuw": "uuw", "ưngw": "ungw",
|
||||
"iême": "ieme", "iêne": "iene", "iêpe": "iepe", "iête": "iete", "iêue": "ieue", "iênge": "ienge",
|
||||
"ươcw": "uocw", "ươiw": "uoiw", "ươmw": "uomw", "ươnw": "uonw", "ươtw": "uotw", "ươngw": "uongw",
|
||||
"uyêne": "uyene", "uyêt": "uyete",
|
||||
@@ -138,6 +215,7 @@
|
||||
"inhs": "ính", "inhf": "ình", "inhr": "ỉnh", "inhx": "ĩnh", "inhj": "ịnh",
|
||||
|
||||
"ías": "ias", "ìaf": "iaf", "ỉar": "iar", "ĩax": "iax", "ịaj": "iaj",
|
||||
"iás": "ias", "iàf": "iaf", "iảr": "iar", "iãx": "iax", "iạj": "iaj",
|
||||
"ícs": "ics", "ịcj": "icj",
|
||||
"íms": "ims", "ìmf": "imf", "ỉmr": "imr", "ĩmx": "imx", "ịmj": "imj",
|
||||
"íns": "ins", "ìnf": "inf", "ỉnr": "inr", "ĩnx": "inx", "ịnj": "inj",
|
||||
@@ -385,6 +463,8 @@
|
||||
|
||||
"ưas": "ứa", "ưaf": "ừa", "ưar": "ửa", "ưax": "ữa", "ưaj": "ựa",
|
||||
"úaw": "ứa", "ùaw": "ừa", "ủaw": "ửa", "ũaw": "ữa", "ụaw": "ựa",
|
||||
"ưcs": "ức", "ưcj": "ực",
|
||||
"úcw": "ức", "ụcw": "ực",
|
||||
"ưis": "ứi", "ưif": "ừi", "ưir": "ửi", "ưix": "ữi", "ưij": "ựi",
|
||||
"úiw": "ứi", "ùiw": "ừi", "ủiw": "ửi", "ũiw": "ữi", "ụiw": "ựi",
|
||||
"ưms": "ứm", "ưmf": "ừm", "ưmr": "ửm", "ưmx": "ữm", "ưmj": "ựm",
|
||||
@@ -403,6 +483,8 @@
|
||||
"ửar": "ưar", "ửaw": "ủaw", "ưarw": "uarw", "ủawr": "uawr",
|
||||
"ữax": "ưax", "ữaw": "ũaw", "ưaxw": "uaxw", "ũawx": "uawx",
|
||||
"ựaj": "ưaj", "ựaw": "ụaw", "ưajw": "uajw", "ụawj": "uawj",
|
||||
"ứcs": "ưcs", "ứcw": "úcw", "ưcsw": "ucsw", "úcws": "ucws",
|
||||
"ựcj": "ưcj", "ựcw": "ụcw", "ưcjw": "ucjw", "ụcwj": "ucwj",
|
||||
"ứis": "ưis", "ứiw": "úiw", "ưisw": "uisw", "úiws": "uiws",
|
||||
"ừif": "ưif", "ừiw": "ùiw", "ưifw": "uifw", "ùiwf": "uiwf",
|
||||
"ửir": "ưir", "ửiw": "ủiw", "ưirw": "uirw", "ủiwr": "uiwr",
|
||||
@@ -450,6 +532,11 @@
|
||||
"iénge": "iếng", "iènge": "iềng", "iẻnge": "iểng", "iẽnge": "iễng", "iẹnge": "iệng",
|
||||
"iêngs": "iếng", "iêngf": "iềng", "iêngr": "iểng", "iêngx": "iễng", "iêngj": "iệng",
|
||||
|
||||
"iếs": "iês", "iếe": "iée", "iêse": "iese", "iées": "iees",
|
||||
"iềf": "iêf", "iềe": "ièe", "iêfe": "iefe", "ièef": "ieef",
|
||||
"iểr": "iêr", "iểe": "iẻe", "iêre": "iere", "iẻer": "ieer",
|
||||
"iễx": "iêx", "iễe": "iẽe", "iêxe": "iexe", "iẽex": "ieex",
|
||||
"iệj": "iêj", "iệe": "iẹe", "iêje": "ieje", "iẹej": "ieej",
|
||||
"iếms": "iêms", "iếme": "iéme", "iêmse": "iemse", "iémes": "iemes",
|
||||
"iềmf": "iêmf", "iềme": "ième", "iêmfe": "iemfe", "ièmef": "iemef",
|
||||
"iểmr": "iêmr", "iểme": "iẻme", "iêmre": "iemre", "iẻmer": "iemer",
|
||||
|
||||
@@ -104,12 +104,25 @@
|
||||
"authors": [ "blucin" ],
|
||||
"direction": "ltr"
|
||||
},
|
||||
{
|
||||
"id": "colemak_dhm",
|
||||
"label": "ColemakDHm",
|
||||
"authors": [ "SteveP", "oneilljp" ],
|
||||
"direction": "ltr"
|
||||
},
|
||||
{
|
||||
"id": "danish",
|
||||
"label": "Danish (QWERTY)",
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr"
|
||||
},
|
||||
{
|
||||
"id": "diktor",
|
||||
"label": "Diktor",
|
||||
"authors": [ "kuroya2mouse" ],
|
||||
"direction": "ltr",
|
||||
"modifier": "org.florisboard.layouts:diktor"
|
||||
},
|
||||
{
|
||||
"id": "dvorak",
|
||||
"label": "Dvorak",
|
||||
@@ -379,6 +392,12 @@
|
||||
"authors": [ "patrickgold" ],
|
||||
"direction": "ltr"
|
||||
},
|
||||
{
|
||||
"id": "tamil",
|
||||
"label": "Tamil",
|
||||
"authors": [ "Clem0908" ],
|
||||
"direction": "ltr"
|
||||
},
|
||||
{
|
||||
"id": "thai_kedmanee",
|
||||
"label": "Thai Kedmanee",
|
||||
@@ -433,6 +452,12 @@
|
||||
"label": "Workman",
|
||||
"authors": [ "icyphox" ],
|
||||
"direction": "ltr"
|
||||
},
|
||||
{
|
||||
"id": "jcuken_interslavic",
|
||||
"label": "Interslavic (ЈЦУКЕН)",
|
||||
"authors": [ "victorbnl" ],
|
||||
"direction": "ltr"
|
||||
}
|
||||
],
|
||||
"charactersMod": [
|
||||
@@ -454,6 +479,12 @@
|
||||
"authors": [ "HeiWiper" ],
|
||||
"direction": "rtl"
|
||||
},
|
||||
{
|
||||
"id": "diktor",
|
||||
"label": "Diktor",
|
||||
"authors": [ "kuroya2mouse" ],
|
||||
"direction": "ltr"
|
||||
},
|
||||
{
|
||||
"id": "dvorak",
|
||||
"label": "Dvorak",
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
[
|
||||
[
|
||||
{ "$": "auto_text_key", "code": 1383, "label": "է" },
|
||||
{ "$": "auto_text_key", "code": 1385, "label": "թ" },
|
||||
{ "$": "auto_text_key", "code": 1411, "label": "փ" },
|
||||
{ "$": "auto_text_key", "code": 1393, "label": "ձ" },
|
||||
{ "$": "auto_text_key", "code": 1403, "label": "ջ" },
|
||||
{ "$": "auto_text_key", "code": 1408, "label": "ր" },
|
||||
{ "$": "auto_text_key", "code": 1401, "label": "չ" },
|
||||
{ "$": "auto_text_key", "code": 1395, "label": "ճ" },
|
||||
{ "$": "auto_text_key", "code": 1386, "label": "ժ" },
|
||||
{ "$": "auto_text_key", "code": 1390, "label": "ծ" }
|
||||
],
|
||||
[
|
||||
{ "$": "auto_text_key", "code": 1412, "label": "ք" },
|
||||
{ "$": "auto_text_key", "code": 1400, "label": "ո" },
|
||||
{ "$": "auto_text_key", "code": 1381, "label": "ե" },
|
||||
{ "$": "auto_text_key", "code": 1404, "label": "ռ" },
|
||||
{ "$": "auto_text_key", "code": 1407, "label": "տ" },
|
||||
{ "$": "auto_text_key", "code": 1384, "label": "ը" },
|
||||
{ "$": "auto_text_key", "code": 1410, "label": "ւ" },
|
||||
{ "$": "auto_text_key", "code": 1387, "label": "ի" },
|
||||
{ "$": "auto_text_key", "code": 1413, "label": "օ" },
|
||||
{ "$": "auto_text_key", "code": 1402, "label": "պ" }
|
||||
],
|
||||
[
|
||||
{ "$": "auto_text_key", "code": 1377, "label": "ա" },
|
||||
{ "$": "auto_text_key", "code": 1405, "label": "ս" },
|
||||
{ "$": "auto_text_key", "code": 1380, "label": "դ" },
|
||||
{ "$": "auto_text_key", "code": 1414, "label": "ֆ" },
|
||||
{ "$": "auto_text_key", "code": 1379, "label": "գ" },
|
||||
{ "$": "auto_text_key", "code": 1392, "label": "հ" },
|
||||
{ "$": "auto_text_key", "code": 1397, "label": "յ" },
|
||||
{ "$": "auto_text_key", "code": 1391, "label": "կ" },
|
||||
{ "$": "auto_text_key", "code": 1388, "label": "լ" },
|
||||
{ "$": "auto_text_key", "code": 1389, "label": "խ" }
|
||||
],
|
||||
[
|
||||
{ "$": "auto_text_key", "code": 1382, "label": "զ" },
|
||||
{ "$": "auto_text_key", "code": 1394, "label": "ղ" },
|
||||
{ "$": "auto_text_key", "code": 1409, "label": "ց" },
|
||||
{ "$": "auto_text_key", "code": 1406, "label": "վ" },
|
||||
{ "$": "auto_text_key", "code": 1378, "label": "բ" },
|
||||
{ "$": "auto_text_key", "code": 1398, "label": "ն" },
|
||||
{ "$": "auto_text_key", "code": 1396, "label": "մ" },
|
||||
{ "$": "auto_text_key", "code": 1399, "label": "շ" }
|
||||
]
|
||||
]
|
||||
|
||||
@@ -42,9 +42,9 @@
|
||||
}
|
||||
],
|
||||
[
|
||||
{ "$": "case_selector",
|
||||
{ "$": "case_selector",
|
||||
"lower": { "code": 2499, "label": "ৃ" },
|
||||
"upper": { "$": "multi_text_key", "codePoints": [2480, 2509], "label": "র্" }
|
||||
"upper": { "code": 2435, "label": "ঃ" }
|
||||
},
|
||||
{ "$": "case_selector",
|
||||
"lower": { "code": 2497, "label": "ু" },
|
||||
@@ -109,4 +109,4 @@
|
||||
"upper": { "code": 2486, "label": "শ" }
|
||||
}
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
[
|
||||
[
|
||||
{ "$": "auto_text_key", "code": 113, "label": "q" },
|
||||
{ "$": "auto_text_key", "code": 119, "label": "w" },
|
||||
{ "$": "auto_text_key", "code": 102, "label": "f" },
|
||||
{ "$": "auto_text_key", "code": 112, "label": "p" },
|
||||
{ "$": "auto_text_key", "code": 98, "label": "b" },
|
||||
{ "$": "auto_text_key", "code": 106, "label": "j" },
|
||||
{ "$": "auto_text_key", "code": 108, "label": "l" },
|
||||
{ "$": "auto_text_key", "code": 117, "label": "u" },
|
||||
{ "$": "auto_text_key", "code": 121, "label": "y" },
|
||||
{ "$": "case_selector",
|
||||
"lower": { "code": 59, "label": ";", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 58, "label": ":" }
|
||||
]
|
||||
} },
|
||||
"upper": { "code": 58, "label": ":", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 59, "label": ";" }
|
||||
]
|
||||
} }
|
||||
}
|
||||
],
|
||||
[
|
||||
{ "$": "auto_text_key", "code": 97, "label": "a" },
|
||||
{ "$": "auto_text_key", "code": 114, "label": "r" },
|
||||
{ "$": "auto_text_key", "code": 115, "label": "s" },
|
||||
{ "$": "auto_text_key", "code": 116, "label": "t" },
|
||||
{ "$": "auto_text_key", "code": 103, "label": "g" },
|
||||
{ "$": "auto_text_key", "code": 109, "label": "m" },
|
||||
{ "$": "auto_text_key", "code": 110, "label": "n" },
|
||||
{ "$": "auto_text_key", "code": 101, "label": "e" },
|
||||
{ "$": "auto_text_key", "code": 105, "label": "i" },
|
||||
{ "$": "auto_text_key", "code": 111, "label": "o" }
|
||||
],
|
||||
[
|
||||
{ "$": "auto_text_key", "code": 122, "label": "z" },
|
||||
{ "$": "auto_text_key", "code": 120, "label": "x" },
|
||||
{ "$": "auto_text_key", "code": 99, "label": "c" },
|
||||
{ "$": "auto_text_key", "code": 100, "label": "d" },
|
||||
{ "$": "auto_text_key", "code": 118, "label": "v" },
|
||||
{ "$": "auto_text_key", "code": 107, "label": "k" },
|
||||
{ "$": "auto_text_key", "code": 104, "label": "h" }
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,49 @@
|
||||
[
|
||||
[
|
||||
{ "$": "auto_text_key", "code": 1094, "label": "ц" },
|
||||
{ "$": "auto_text_key", "code": 1100, "label": "ь" },
|
||||
{ "$": "auto_text_key", "code": 1103, "label": "я" },
|
||||
{ "$": "case_selector",
|
||||
"lower": { "code": 44, "label": ",", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 46, "label": "." }
|
||||
]
|
||||
} },
|
||||
"upper": { "code": 46, "label": ".", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 44, "label": "," }
|
||||
]
|
||||
} }
|
||||
},
|
||||
{ "$": "auto_text_key", "code": 1079, "label": "з" },
|
||||
{ "$": "auto_text_key", "code": 1074, "label": "в" },
|
||||
{ "$": "auto_text_key", "code": 1082, "label": "к" },
|
||||
{ "$": "auto_text_key", "code": 1076, "label": "д" },
|
||||
{ "$": "auto_text_key", "code": 1095, "label": "ч" },
|
||||
{ "$": "auto_text_key", "code": 1096, "label": "ш" },
|
||||
{ "$": "auto_text_key", "code": 1097, "label": "щ" }
|
||||
],
|
||||
[
|
||||
{ "$": "auto_text_key", "code": 1091 , "label": "у" },
|
||||
{ "$": "auto_text_key", "code": 1080 , "label": "и" },
|
||||
{ "$": "auto_text_key", "code": 1077 , "label": "е" },
|
||||
{ "$": "auto_text_key", "code": 1086 , "label": "о" },
|
||||
{ "$": "auto_text_key", "code": 1072 , "label": "а" },
|
||||
{ "$": "auto_text_key", "code": 1083 , "label": "л" },
|
||||
{ "$": "auto_text_key", "code": 1085 , "label": "н" },
|
||||
{ "$": "auto_text_key", "code": 1090 , "label": "т" },
|
||||
{ "$": "auto_text_key", "code": 1089 , "label": "с" },
|
||||
{ "$": "auto_text_key", "code": 1088 , "label": "р" },
|
||||
{ "$": "auto_text_key", "code": 1081 , "label": "й" }
|
||||
],
|
||||
[
|
||||
{ "$": "auto_text_key", "code": 1101 , "label": "э" },
|
||||
{ "$": "auto_text_key", "code": 1093 , "label": "х" },
|
||||
{ "$": "auto_text_key", "code": 1099 , "label": "ы" },
|
||||
{ "$": "auto_text_key", "code": 1102 , "label": "ю" },
|
||||
{ "$": "auto_text_key", "code": 1073 , "label": "б" },
|
||||
{ "$": "auto_text_key", "code": 1084 , "label": "м" },
|
||||
{ "$": "auto_text_key", "code": 1087 , "label": "п" },
|
||||
{ "$": "auto_text_key", "code": 1075 , "label": "г" }
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,36 @@
|
||||
[
|
||||
[
|
||||
{ "$": "auto_text_key", "code": 1112, "label": "ј" },
|
||||
{ "$": "auto_text_key", "code": 1094, "label": "ц" },
|
||||
{ "$": "auto_text_key", "code": 1091, "label": "у" },
|
||||
{ "$": "auto_text_key", "code": 1082, "label": "к" },
|
||||
{ "$": "auto_text_key", "code": 1077, "label": "е" },
|
||||
{ "$": "auto_text_key", "code": 1085, "label": "н" },
|
||||
{ "$": "auto_text_key", "code": 1114, "label": "њ" },
|
||||
{ "$": "auto_text_key", "code": 1075, "label": "г" },
|
||||
{ "$": "auto_text_key", "code": 1096, "label": "ш" },
|
||||
{ "$": "auto_text_key", "code": 1079, "label": "з" },
|
||||
{ "$": "auto_text_key", "code": 1093, "label": "х" }
|
||||
],
|
||||
[
|
||||
{ "$": "auto_text_key", "code": 1092 , "label": "ф" },
|
||||
{ "$": "auto_text_key", "code": 1099 , "label": "ы" },
|
||||
{ "$": "auto_text_key", "code": 1074 , "label": "в" },
|
||||
{ "$": "auto_text_key", "code": 1072 , "label": "а" },
|
||||
{ "$": "auto_text_key", "code": 1087 , "label": "п" },
|
||||
{ "$": "auto_text_key", "code": 1088 , "label": "р" },
|
||||
{ "$": "auto_text_key", "code": 1086 , "label": "о" },
|
||||
{ "$": "auto_text_key", "code": 1083 , "label": "л" },
|
||||
{ "$": "auto_text_key", "code": 1113 , "label": "љ" },
|
||||
{ "$": "auto_text_key", "code": 1078 , "label": "ж" }
|
||||
],
|
||||
[
|
||||
{ "$": "auto_text_key", "code": 1108 , "label": "є" },
|
||||
{ "$": "auto_text_key", "code": 1095 , "label": "ч" },
|
||||
{ "$": "auto_text_key", "code": 1089 , "label": "с" },
|
||||
{ "$": "auto_text_key", "code": 1084 , "label": "м" },
|
||||
{ "$": "auto_text_key", "code": 1080 , "label": "и" },
|
||||
{ "$": "auto_text_key", "code": 1090 , "label": "т" },
|
||||
{ "$": "auto_text_key", "code": 1073 , "label": "б" }
|
||||
]
|
||||
]
|
||||
@@ -9,11 +9,7 @@
|
||||
{ "$": "auto_text_key", "code": 1075, "label": "г" },
|
||||
{ "$": "auto_text_key", "code": 1096, "label": "ш" },
|
||||
{ "$": "auto_text_key", "code": 1097, "label": "щ" },
|
||||
{ "code": 1079, "label": "з", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 1247, "label": "ӟ" }
|
||||
]
|
||||
} },
|
||||
{ "$": "auto_text_key", "code": 1079, "label": "з" },
|
||||
{ "$": "auto_text_key", "code": 1093, "label": "х" }
|
||||
],
|
||||
[
|
||||
@@ -23,34 +19,18 @@
|
||||
{ "$": "auto_text_key", "code": 1072 , "label": "а" },
|
||||
{ "$": "auto_text_key", "code": 1087 , "label": "п" },
|
||||
{ "$": "auto_text_key", "code": 1088 , "label": "р" },
|
||||
{ "$": "auto_text_key", "code": 1086 , "label": "о", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 1255, "label": "ӧ" }
|
||||
]
|
||||
} },
|
||||
{ "$": "auto_text_key", "code": 1086 , "label": "о" },
|
||||
{ "$": "auto_text_key", "code": 1083 , "label": "л" },
|
||||
{ "$": "auto_text_key", "code": 1076 , "label": "д" },
|
||||
{ "$": "auto_text_key", "code": 1078 , "label": "ж", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 1245, "label": "ӝ" }
|
||||
]
|
||||
} },
|
||||
{ "$": "auto_text_key", "code": 1078 , "label": "ж" },
|
||||
{ "$": "auto_text_key", "code": 1101 , "label": "э" }
|
||||
],
|
||||
[
|
||||
{ "$": "auto_text_key", "code": 1103 , "label": "я" },
|
||||
{ "$": "auto_text_key", "code": 1095 , "label": "ч", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 1269, "label": "ӵ" }
|
||||
]
|
||||
} },
|
||||
{ "$": "auto_text_key", "code": 1095 , "label": "ч" },
|
||||
{ "$": "auto_text_key", "code": 1089 , "label": "с" },
|
||||
{ "$": "auto_text_key", "code": 1084 , "label": "м" },
|
||||
{ "$": "auto_text_key", "code": 1080 , "label": "и", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 1253, "label": "ӥ" }
|
||||
]
|
||||
} },
|
||||
{ "$": "auto_text_key", "code": 1080 , "label": "и" },
|
||||
{ "$": "auto_text_key", "code": 1090 , "label": "т" },
|
||||
{ "$": "auto_text_key", "code": 1100 , "label": "ь" },
|
||||
{ "$": "auto_text_key", "code": 1073 , "label": "б" },
|
||||
|
||||
@@ -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": 1092, "label": "ф", "groupId": 1 },
|
||||
{ "code": -227, "label": "language_switch", "type": "system_gui" },
|
||||
{ "code": -212, "label": "ime_ui_mode_media", "type": "system_gui" },
|
||||
{ "code": 32, "label": "space" },
|
||||
{ "$": "auto_text_key", "code": 1078, "label": "ж", "groupId": 2 },
|
||||
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
|
||||
]
|
||||
]
|
||||
@@ -8,15 +8,13 @@
|
||||
},
|
||||
"half": { "code": 64, "label": "@" }
|
||||
},
|
||||
{ "code": 12306, "label": "〒", "popup": {
|
||||
"main": { "code": 12320, "label": "〠" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"$": "char_width_selector",
|
||||
"full": { "code": 65283, "label": "#", "popup": {
|
||||
"main": { "code": 35, "label": "#" },
|
||||
"relevant": [
|
||||
{ "code": 12306, "label": "〒"},
|
||||
{ "code": 12320, "label": "〠" },
|
||||
{ "code": 8470, "label": "№" }
|
||||
]
|
||||
}
|
||||
@@ -24,6 +22,8 @@
|
||||
"half": { "code": 35, "label": "#", "popup": {
|
||||
"main": { "code": 65283, "label": "#" },
|
||||
"relevant": [
|
||||
{ "code": 12306, "label": "〒"},
|
||||
{ "code": 12320, "label": "〠" },
|
||||
{ "code": 8470, "label": "№" }
|
||||
]
|
||||
}
|
||||
@@ -99,14 +99,18 @@
|
||||
"full": { "code": 65291, "label": "+", "popup": {
|
||||
"main": { "code": 43, "label": "+" },
|
||||
"relevant": [
|
||||
{ "code": 177, "label": "±" }
|
||||
{ "code": 177, "label": "±" },
|
||||
{ "code": 61, "label": "=" },
|
||||
{ "code": 65309, "label": "=" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"half": { "code": 43, "label": "+", "popup": {
|
||||
"main": { "code": 65291, "label": "+" },
|
||||
"relevant": [
|
||||
{ "code": 177, "label": "±" }
|
||||
{ "code": 177, "label": "±" },
|
||||
{ "code": 61, "label": "=" },
|
||||
{ "code": 65309, "label": "=" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -117,16 +121,20 @@
|
||||
"main": { "code": 12302, "label": "『" },
|
||||
"relevant": [
|
||||
{ "code": 12304, "label": "【" },
|
||||
{ "code": 12310, "label": "〖" }
|
||||
{ "code": 12310, "label": "〖" },
|
||||
{ "code": 65288, "label": "(" },
|
||||
{ "code": 65339, "label": "[" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"half": { "code": 65378, "label": "「", "popup": {
|
||||
"main": { "code": 12301, "label": "」" },
|
||||
"main": { "code": 12300, "label": "「" },
|
||||
"relevant": [
|
||||
{ "code": 12303, "label": "』" },
|
||||
{ "code": 12302, "label": "『" },
|
||||
{ "code": 12304, "label": "【" },
|
||||
{ "code": 12310, "label": "〖" }
|
||||
{ "code": 12310, "label": "〖" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 91, "label": "[" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -137,7 +145,9 @@
|
||||
"main": { "code": 12303, "label": "』" },
|
||||
"relevant": [
|
||||
{ "code": 12305, "label": "】" },
|
||||
{ "code": 12311, "label": "〗" }
|
||||
{ "code": 12311, "label": "〗" },
|
||||
{ "code": 65289, "label": ")" },
|
||||
{ "code": 65341, "label": "]" }
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -146,7 +156,9 @@
|
||||
"relevant": [
|
||||
{ "code": 12303, "label": "』" },
|
||||
{ "code": 12305, "label": "】" },
|
||||
{ "code": 12311, "label": "〗" }
|
||||
{ "code": 12311, "label": "〗" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 93, "label": "]" }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
{ "code": 11816, "label": "⸨" },
|
||||
{ "code": 10214, "label": "⟦" },
|
||||
{ "code": 10216, "label": "⟨" },
|
||||
{ "code": 10218, "label": "⟩" },
|
||||
{ "code": 10218, "label": "⟪" },
|
||||
{ "code": 123, "label": "{" }
|
||||
]
|
||||
} },
|
||||
@@ -72,7 +72,7 @@
|
||||
"relevant": [
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 11817, "label": "⸩" },
|
||||
{ "code": 10215, "label": "⟦" },
|
||||
{ "code": 10215, "label": "⟧" },
|
||||
{ "code": 10217, "label": "⟩" },
|
||||
{ "code": 10219, "label": "⟫" },
|
||||
{ "code": 125, "label": "}" }
|
||||
|
||||
@@ -54,7 +54,9 @@
|
||||
]
|
||||
} }
|
||||
},
|
||||
{ "code": 8730, "label": "√" },
|
||||
{ "code": 8730, "label": "√", "popup": {
|
||||
"main": { "code": 10003, "label": "✓" }
|
||||
} },
|
||||
{ "code": 960, "label": "π", "popup": {
|
||||
"main": { "code": 928, "label": "Π" },
|
||||
"relevant": [
|
||||
@@ -104,7 +106,7 @@
|
||||
{ "code": 61, "label": "=", "popup": {
|
||||
"main": { "code": 8800, "label": "≠" },
|
||||
"relevant": [
|
||||
{ "code": 61, "label": "=" },
|
||||
{ "code": 65309, "label": "=" },
|
||||
{ "code": 8734, "label": "∞" },
|
||||
{ "code": 8776, "label": "≈" }
|
||||
]
|
||||
|
||||
@@ -6,15 +6,28 @@
|
||||
],
|
||||
[
|
||||
{ "code": -201, "label": "view_characters", "type": "system_gui" },
|
||||
{ "code": 12296, "label": "〈", "popup": {
|
||||
"main": { "code": 12298, "label": "《" },
|
||||
"relevant": [
|
||||
{ "code": 8804, "label": "≤" },
|
||||
{ "code": 8249, "label": "‹" },
|
||||
{ "code": 10216, "label": "⟨" },
|
||||
{ "code": 65308, "label": "<" }
|
||||
]
|
||||
} },
|
||||
{ "$": "char_width_selector",
|
||||
"full": { "code": 12296, "label": "〈", "popup": {
|
||||
"main": { "code": 12298, "label": "《" },
|
||||
"relevant": [
|
||||
{ "code": 60, "label": "<" },
|
||||
{ "code": 8804, "label": "≤" },
|
||||
{ "code": 8249, "label": "‹" },
|
||||
{ "code": 10216, "label": "⟨" },
|
||||
{ "code": 65308, "label": "<" }
|
||||
]
|
||||
} },
|
||||
"half": { "code": 60, "label": "<", "popup": {
|
||||
"main": { "code": 12298, "label": "《" },
|
||||
"relevant": [
|
||||
{ "code": 12296, "label": "〈" },
|
||||
{ "code": 8804, "label": "≤" },
|
||||
{ "code": 8249, "label": "‹" },
|
||||
{ "code": 10216, "label": "⟨" },
|
||||
{ "code": 65308, "label": "<" }
|
||||
]
|
||||
} }
|
||||
},
|
||||
{ "code": -205, "label": "view_numeric_advanced", "type": "system_gui" },
|
||||
{ "code": 12288, "label": "空白" },
|
||||
{ "code": -9701, "label": "char_width_switcher", "type": "system_gui", "popup": {
|
||||
@@ -24,15 +37,28 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{ "code": 12297, "label": "〉", "popup": {
|
||||
"main": { "code": 12299, "label": "》" },
|
||||
"relevant": [
|
||||
{ "code": 62, "label": ">" },
|
||||
{ "code": 8805, "label": "≥" },
|
||||
{ "code": 10217, "label": "⟩" },
|
||||
{ "code": 65310, "label": ">" }
|
||||
]
|
||||
} },
|
||||
{ "$": "char_width_selector",
|
||||
"full": { "code": 12297, "label": "〉", "popup": {
|
||||
"main": { "code": 12299, "label": "》" },
|
||||
"relevant": [
|
||||
{ "code": 62, "label": ">" },
|
||||
{ "code": 8805, "label": "≥" },
|
||||
{ "code": 8250, "label": "›" },
|
||||
{ "code": 10217, "label": "⟩" },
|
||||
{ "code": 65310, "label": ">" }
|
||||
]
|
||||
} },
|
||||
"half": { "code": 62, "label": ">", "popup": {
|
||||
"main": { "code": 12299, "label": "》" },
|
||||
"relevant": [
|
||||
{ "code": 12297, "label": "〉" },
|
||||
{ "code": 8805, "label": "≥" },
|
||||
{ "code": 8250, "label": "›" },
|
||||
{ "code": 10217, "label": "⟩" },
|
||||
{ "code": 65310, "label": ">" }
|
||||
]
|
||||
} }
|
||||
},
|
||||
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
|
||||
]
|
||||
]
|
||||
|
||||
@@ -8,11 +8,17 @@
|
||||
{ "code": -201, "label": "view_characters", "type": "system_gui" },
|
||||
{ "$": "char_width_selector",
|
||||
"full": { "code": 12289, "label": "、", "popup": {
|
||||
"main": { "code": 65292, "label": "," }
|
||||
"main": { "code": 65292, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 44, "label": "," }
|
||||
]
|
||||
}
|
||||
},
|
||||
"half": { "code": 65380, "label": "、", "popup": {
|
||||
"main": { "code": 44, "label": "," }
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 65292, "label": "," }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
{ "id": "bg", "authors": [ "iorvethe" ] },
|
||||
{ "id": "bn-BD", "authors": [ "iamrasel" ] },
|
||||
{ "id": "ca", "authors": [ "mikelloc" ] },
|
||||
{ "id": "cjk", "authors": [ "moonbeamcelery" ] },
|
||||
{ "id": "ckb", "authors": [ "GoRaN" ] },
|
||||
{ "id": "cs", "authors": [ "stefan-misik" ] },
|
||||
{ "id": "da", "authors": [ "patrickgold" ] },
|
||||
@@ -72,6 +73,7 @@
|
||||
{ "id": "sr", "authors": [ "hedidnothingwrong", "GrbavaCigla" ] },
|
||||
{ "id": "sv", "authors": [ "patrickgold" ] },
|
||||
{ "id": "tr", "authors": [ "kisekinopureya", "patrickgold", "dvrnynr" ] },
|
||||
{ "id": "udm", "authors": [ "vorgoron" ] },
|
||||
{ "id": "uk", "authors": [ "williamtheaker", "33kk", "honsiorovskyi" ] },
|
||||
{ "id": "uk-cyr-ext", "authors": [ "williamtheaker", "33kk", "honsiorovskyi" ] },
|
||||
{ "id": "ur-PK", "authors": [ "mubashir-rehman", "mirfatif" ] },
|
||||
@@ -675,7 +677,7 @@
|
||||
"suggestion": "org.florisboard.nlp.providers.han.shape"
|
||||
},
|
||||
"currencySet": "org.florisboard.currencysets:yen",
|
||||
"popupMapping": "org.florisboard.localization:en",
|
||||
"popupMapping": "org.florisboard.localization:cjk",
|
||||
"preferred": {
|
||||
"characters": "org.florisboard.layouts:qwerty",
|
||||
"symbols": "org.florisboard.layouts:cjk",
|
||||
|
||||
@@ -30,102 +30,97 @@
|
||||
"ড়": {
|
||||
"main": { "$": "auto_text_key", "code": 2525, "label": "ঢ়" }
|
||||
},
|
||||
"ৃ": {
|
||||
"main": { "$": "auto_text_key", "code": 2443, "label": "ঋ" },
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 2500, "label": "ৄ" },
|
||||
{ "$": "auto_text_key", "code": 2528, "label": "ৠ" },
|
||||
{ "$": "auto_text_key", "code": 2529, "label": "ৡ" },
|
||||
{ "$": "auto_text_key", "code": 2530, "label": "ৢ" },
|
||||
{ "$": "auto_text_key", "code": 2531, "label": "ৣ" }
|
||||
]
|
||||
"ৃ": {
|
||||
"main": { "$": "auto_text_key", "code": 2443, "label": "ঋ" }
|
||||
},
|
||||
"ু": {
|
||||
"ু": {
|
||||
"main": { "$": "auto_text_key", "code": 2441, "label": "উ" }
|
||||
},
|
||||
"ি": {
|
||||
"ি": {
|
||||
"main": { "$": "auto_text_key", "code": 2439, "label": "ই" }
|
||||
},
|
||||
"া": {
|
||||
"া": {
|
||||
"main": { "$": "auto_text_key", "code": 2438, "label": "আ" },
|
||||
"relevant": [
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 2437, "label": "অ" }
|
||||
]
|
||||
},
|
||||
"্": {
|
||||
"্": {
|
||||
"main": { "$": "auto_text_key", "code": 2433, "label": "ঁ" }
|
||||
},
|
||||
"ব": {
|
||||
"ব": {
|
||||
"main": { "$": "auto_text_key", "code": 2477, "label": "ভ" }
|
||||
},
|
||||
"ক": {
|
||||
"ক": {
|
||||
"main": { "$": "auto_text_key", "code": 2454, "label": "খ" }
|
||||
},
|
||||
"ত": {
|
||||
"ত": {
|
||||
"main": { "$": "auto_text_key", "code": 2469, "label": "থ" },
|
||||
"relevant": [
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 2510, "label": "ৎ" }
|
||||
]
|
||||
},
|
||||
"দ": {
|
||||
"দ": {
|
||||
"main": { "$": "auto_text_key", "code": 2471, "label": "ধ" }
|
||||
},
|
||||
"ো": {
|
||||
"ো": {
|
||||
"main": { "$": "auto_text_key", "code": 2451, "label": "ও" }
|
||||
},
|
||||
"ে": {
|
||||
"ে": {
|
||||
"main": { "$": "auto_text_key", "code": 2447, "label": "এ" }
|
||||
},
|
||||
"র": {
|
||||
"র": {
|
||||
"main": { "$": "auto_text_key", "code": 2482, "label": "ল" },
|
||||
"relevant": [
|
||||
"relevant": [
|
||||
{ "code": -255, "label": "র্য" }
|
||||
]
|
||||
},
|
||||
"ন": {
|
||||
"ন": {
|
||||
"main": { "$": "auto_text_key", "code": 2467, "label": "ণ" }
|
||||
},
|
||||
"স": {
|
||||
"স": {
|
||||
"main": { "$": "auto_text_key", "code": 2487, "label": "ষ" }
|
||||
},
|
||||
"ম": {
|
||||
"ম": {
|
||||
"main": { "$": "auto_text_key", "code": 2486, "label": "শ" }
|
||||
},
|
||||
"ূ": {
|
||||
"ূ": {
|
||||
"main": { "$": "auto_text_key", "code": 2442, "label": "ঊ" }
|
||||
},
|
||||
"ী": {
|
||||
"ী": {
|
||||
"main": { "$": "auto_text_key", "code": 2440, "label": "ঈ" }
|
||||
},
|
||||
"ঁ": {
|
||||
"ঁ": {
|
||||
"main": { "$": "auto_text_key", "code": 2492, "label": "়" },
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 2493, "label": "ঽ" },
|
||||
{ "$": "auto_text_key", "code": 2544, "label": "ৰ" },
|
||||
{ "$": "auto_text_key", "code": 2545, "label": "ৱ" },
|
||||
{ "$": "auto_text_key", "code": 2492, "label": "়" },
|
||||
{ "$": "auto_text_key", "code": 2554, "label": "৺" },
|
||||
{ "$": "auto_text_key", "code": 2519, "label": "ৗ" }
|
||||
{ "$": "auto_text_key", "code": 2500, "label": "ৄ" },
|
||||
{ "$": "auto_text_key", "code": 2530, "label": "ৢ" },
|
||||
{ "$": "auto_text_key", "code": 2531, "label": "ৣ" },
|
||||
{ "$": "auto_text_key", "code": 2528, "label": "ৠ" },
|
||||
{ "$": "auto_text_key", "code": 2444, "label": "ঌ" },
|
||||
{ "$": "auto_text_key", "code": 2529, "label": "ৡ" },
|
||||
{ "$": "auto_text_key", "code": 2544, "label": "ৰ" },
|
||||
{ "$": "auto_text_key", "code": 2545, "label": "ৱ" },
|
||||
{ "$": "auto_text_key", "code": 2493, "label": "ঽ" },
|
||||
{ "$": "auto_text_key", "code": 2554, "label": "৺" },
|
||||
{ "$": "auto_text_key", "code": 2519, "label": "ৗ" }
|
||||
]
|
||||
},
|
||||
"ৌ": {
|
||||
"ৌ": {
|
||||
"main": { "$": "auto_text_key", "code": 2452, "label": "ঔ" }
|
||||
},
|
||||
"ৈ": {
|
||||
"ৈ": {
|
||||
"main": { "$": "auto_text_key", "code": 2448, "label": "ঐ" }
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 2404, "label": "।" },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 2435, "label": "ঃ" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "$": "layout_direction_selector",
|
||||
"ltr": { "code": 40, "label": "(" },
|
||||
"rtl": { "code": 41, "label": "(" }
|
||||
@@ -134,9 +129,13 @@
|
||||
"ltr": { "code": 41, "label": ")" },
|
||||
"rtl": { "code": 40, "label": ")" }
|
||||
},
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 8205, "label": ">⁞<" },
|
||||
{ "code": 8204, "label": "<⁞>" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
{ "code": 63, "label": "?" },
|
||||
{ "code": 2405, "label": "॥" }
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
{
|
||||
"all": {
|
||||
"a": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 257, "label": "ā" },
|
||||
{ "$": "auto_text_key", "code": 225, "label": "á" },
|
||||
{ "$": "auto_text_key", "code": 462, "label": "ǎ" },
|
||||
{ "$": "auto_text_key", "code": 224, "label": "à" },
|
||||
{ "$": "auto_text_key", "code": 230, "label": "æ" },
|
||||
{ "$": "auto_text_key", "code": 227, "label": "ã" },
|
||||
{ "$": "auto_text_key", "code": 229, "label": "å" },
|
||||
{ "$": "auto_text_key", "code": 226, "label": "â" },
|
||||
{ "$": "auto_text_key", "code": 228, "label": "ä" }
|
||||
]
|
||||
},
|
||||
"c": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 231, "label": "ç" }
|
||||
]
|
||||
},
|
||||
"e": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 275, "label": "ē" },
|
||||
{ "$": "auto_text_key", "code": 233, "label": "é" },
|
||||
{ "$": "auto_text_key", "code": 283, "label": "ě" },
|
||||
{ "$": "auto_text_key", "code": 232, "label": "è" },
|
||||
{ "$": "auto_text_key", "code": 234, "label": "ê" },
|
||||
{ "$": "auto_text_key", "code": 235, "label": "ë" }
|
||||
]
|
||||
},
|
||||
"i": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 299, "label": "ī" },
|
||||
{ "$": "auto_text_key", "code": 237, "label": "í" },
|
||||
{ "$": "auto_text_key", "code": 464, "label": "ǐ" },
|
||||
{ "$": "auto_text_key", "code": 236, "label": "ì" },
|
||||
{ "$": "auto_text_key", "code": 239, "label": "ï" },
|
||||
{ "$": "auto_text_key", "code": 238, "label": "î" }
|
||||
]
|
||||
},
|
||||
"n": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 241, "label": "ñ" },
|
||||
{ "$": "auto_text_key", "code": 324, "label": "ń" }
|
||||
]
|
||||
},
|
||||
"o": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 333, "label": "ō" },
|
||||
{ "$": "auto_text_key", "code": 243, "label": "ó" },
|
||||
{ "$": "auto_text_key", "code": 466, "label": "ǒ" },
|
||||
{ "$": "auto_text_key", "code": 242, "label": "ò" },
|
||||
{ "$": "auto_text_key", "code": 245, "label": "õ" },
|
||||
{ "$": "auto_text_key", "code": 339, "label": "œ" },
|
||||
{ "$": "auto_text_key", "code": 248, "label": "ø" },
|
||||
{ "$": "auto_text_key", "code": 246, "label": "ö" },
|
||||
{ "$": "auto_text_key", "code": 244, "label": "ô" }
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 223, "label": "ß" }
|
||||
]
|
||||
},
|
||||
"u": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 363, "label": "ū" },
|
||||
{ "$": "auto_text_key", "code": 250, "label": "ú" },
|
||||
{ "$": "auto_text_key", "code": 468, "label": "ǔ" },
|
||||
{ "$": "auto_text_key", "code": 249, "label": "ù" },
|
||||
{ "$": "auto_text_key", "code": 252, "label": "ü" },
|
||||
{ "$": "auto_text_key", "code": 470, "label": "ǖ" },
|
||||
{ "$": "auto_text_key", "code": 472, "label": "ǘ" },
|
||||
{ "$": "auto_text_key", "code": 474, "label": "ǚ" },
|
||||
{ "$": "auto_text_key", "code": 476, "label": "ǜ" },
|
||||
{ "$": "auto_text_key", "code": 251, "label": "û" }
|
||||
]
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 38, "label": "&" },
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 34, "label": "\"" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 39, "label": "'" },
|
||||
{ "code": 64, "label": "@" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "$": "layout_direction_selector",
|
||||
"ltr": { "code": 40, "label": "(" },
|
||||
"rtl": { "code": 41, "label": "(" }
|
||||
},
|
||||
{ "$": "layout_direction_selector",
|
||||
"ltr": { "code": 41, "label": ")" },
|
||||
"rtl": { "code": 40, "label": ")" }
|
||||
},
|
||||
{ "code": 35, "label": "#" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"all": {
|
||||
"ե": {
|
||||
"main": { "$": "auto_text_key", "code": 1415, "label": "և" }
|
||||
},
|
||||
"յ": {
|
||||
"main": { "$": "auto_text_key", "code": 1416, "label": "ֈ" }
|
||||
},
|
||||
"ա": {
|
||||
"main": { "$": "auto_text_key", "code": 1376, "label": "ՠ" }
|
||||
},
|
||||
"~right": {
|
||||
"main": { "code": 44, "label": "," },
|
||||
"relevant": [
|
||||
{ "code": 1417, "label": "։" },
|
||||
{ "code": 1418, "label": "֊" },
|
||||
{ "code": 1369, "label": "ՙ" },
|
||||
{ "code": 1370, "label": "՚" },
|
||||
{ "code": 1373, "label": "՝" },
|
||||
{ "code": 1371, "label": "՛" },
|
||||
{ "code": 1375, "label": "՟" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "code": 40, "label": "(" },
|
||||
{ "code": 41, "label": ")" },
|
||||
{ "code": 46, "label": "." },
|
||||
{ "code": 1372, "label": "՜" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" },
|
||||
{ "code": 1374, "label": "՞" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".gov" },
|
||||
{ "code": -255, "label": ".edu" },
|
||||
{ "code": -255, "label": ".gr" },
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"all": {
|
||||
"c": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 269, "label": "č" },
|
||||
{ "$": "auto_text_key", "code": 269, "label": "č" }
|
||||
]
|
||||
},
|
||||
"s": {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Auto-generated by emojicon.py using CLDR v45
|
||||
# DO NOT EDIT MANUALLY!
|
||||
|
||||
[smileys_emotion]
|
||||
😀;grinsendes Gesicht;Gesicht|grinsendes Gesicht|lol|lustig
|
||||
😃;grinsendes Gesicht mit großen Augen;Gesicht|grinsendes Gesicht mit großen Augen|lächeln|lol|lustig
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Auto-generated by emojicon.py using CLDR v45
|
||||
# DO NOT EDIT MANUALLY!
|
||||
|
||||
[smileys_emotion]
|
||||
😀;grinning face;face|grin|grinning face
|
||||
😃;grinning face with big eyes;face|grinning face with big eyes|mouth|open|smile
|
||||
@@ -3788,3 +3791,4 @@
|
||||
🏴;flag: England;flag|flag: England
|
||||
🏴;flag: Scotland;flag|flag: Scotland
|
||||
🏴;flag: Wales;flag|flag: Wales
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Auto-generated by emojicon.py using CLDR v45
|
||||
# DO NOT EDIT MANUALLY!
|
||||
|
||||
[smileys_emotion]
|
||||
😀;cara sonriendo;cara|cara sonriendo|divertido|feliz|sonrisa
|
||||
😃;cara sonriendo con ojos grandes;cara|cara sonriendo con ojos grandes|divertido|risa|sonriendo
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Auto-generated by emojicon.py using CLDR v45
|
||||
# DO NOT EDIT MANUALLY!
|
||||
|
||||
[smileys_emotion]
|
||||
😀;visage rieur;sourire|visage rieur
|
||||
😃;visage souriant avec de grands yeux;sourire|visage souriant avec de grands yeux
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Auto-generated by emojicon.py using CLDR v45
|
||||
# DO NOT EDIT MANUALLY!
|
||||
|
||||
[smileys_emotion]
|
||||
😀;faccina con un gran sorriso;faccina|faccina che sogghigna|faccina con un gran sorriso|risata|sogghignare
|
||||
😃;faccina con un gran sorriso e occhi spalancati;faccina|faccina con un gran sorriso e occhi spalancati|faccina sorridente|risata|sorridere
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Auto-generated by emojicon.py using CLDR v45
|
||||
# DO NOT EDIT MANUALLY!
|
||||
|
||||
[smileys_emotion]
|
||||
😀;rosto risonho;lol|rindo|risada|rosto|rosto risonho
|
||||
😃;rosto risonho com olhos bem abertos;aberto|boca|rosto|rosto risonho com olhos bem abertos|sorriso
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Auto-generated by emojicon.py using CLDR v45
|
||||
# DO NOT EDIT MANUALLY!
|
||||
|
||||
[smileys_emotion]
|
||||
😀;;
|
||||
😃;;
|
||||
@@ -3788,3 +3791,4 @@
|
||||
🏴;;
|
||||
🏴;;
|
||||
🏴;;
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
@@ -33,6 +33,7 @@ import dev.patrickgold.florisboard.ime.keyboard.KeyboardManager
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.FlorisEmojiCompat
|
||||
import dev.patrickgold.florisboard.ime.nlp.NlpManager
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.GlideTypingManager
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeManager
|
||||
import dev.patrickgold.florisboard.lib.cache.CacheManager
|
||||
import dev.patrickgold.florisboard.lib.crashutility.CrashUtility
|
||||
@@ -40,9 +41,8 @@ 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.florisboard.lib.io.AssetManager
|
||||
import dev.patrickgold.florisboard.lib.io.deleteContentsRecursively
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
|
||||
import org.florisboard.lib.kotlin.tryOrNull
|
||||
import org.florisboard.libnative.dummyAdd
|
||||
import java.lang.ref.WeakReference
|
||||
@@ -61,13 +61,13 @@ class FlorisApplication : Application() {
|
||||
System.loadLibrary("fl_native")
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
FlorisImeTheme.init()
|
||||
}
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val mainHandler by lazy { Handler(mainLooper) }
|
||||
|
||||
val assetManager = lazy { AssetManager(this) }
|
||||
val cacheManager = lazy { CacheManager(this) }
|
||||
val clipboardManager = lazy { ClipboardManager(this) }
|
||||
val editorInstance = lazy { EditorInstance(this) }
|
||||
@@ -144,8 +144,6 @@ private tailrec fun Context.florisApplication(): FlorisApplication {
|
||||
|
||||
fun Context.appContext() = lazyOf(this.florisApplication())
|
||||
|
||||
fun Context.assetManager() = this.florisApplication().assetManager
|
||||
|
||||
fun Context.cacheManager() = this.florisApplication().cacheManager
|
||||
|
||||
fun Context.clipboardManager() = this.florisApplication().clipboardManager
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
package dev.patrickgold.florisboard
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.BottomSheetValue
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.SwipeableDefaults
|
||||
import androidx.compose.material.rememberSwipeableState
|
||||
import androidx.compose.material.swipeable
|
||||
import androidx.compose.material3.BottomSheetDefaults
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.ime.sheet.BottomSheetHostUi
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.lib.android.AndroidClipboardManager
|
||||
import dev.patrickgold.florisboard.lib.android.stringRes
|
||||
import dev.patrickgold.florisboard.lib.android.systemService
|
||||
import dev.patrickgold.florisboard.lib.compose.ProvideLocalizedResources
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.snyggBackground
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.snyggClip
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.solidColor
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.spSize
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class FlorisCopyToClipboardActivity : ComponentActivity() {
|
||||
private var error: CopyToClipboardError? = null
|
||||
private var bitmap: Bitmap? = null
|
||||
|
||||
internal enum class CopyToClipboardError {
|
||||
UNKNOWN_ERROR,
|
||||
ANDROID_VERSION_TO_OLD_ERROR,
|
||||
TYPE_NOT_SUPPORTED_ERROR;
|
||||
|
||||
@Composable
|
||||
fun showError(): String {
|
||||
val textId = when (this) {
|
||||
UNKNOWN_ERROR -> R.string.send_to_clipboard__unknown_error
|
||||
TYPE_NOT_SUPPORTED_ERROR -> R.string.send_to_clipboard__type_not_supported_error
|
||||
ANDROID_VERSION_TO_OLD_ERROR -> R.string.send_to_clipboard__android_version_to_old_error
|
||||
}
|
||||
return stringRes(id = textId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
finish()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val systemClipboardManager = this.systemService(AndroidClipboardManager::class)
|
||||
val type = intent.type
|
||||
val action = intent.action
|
||||
|
||||
if (Intent.ACTION_SEND != action || type == null) {
|
||||
error = CopyToClipboardError.UNKNOWN_ERROR
|
||||
} else {
|
||||
if (type.startsWith("image/")) {
|
||||
val hasExtraStream = intent.hasExtra(Intent.EXTRA_STREAM)
|
||||
if (!hasExtraStream) {
|
||||
error = CopyToClipboardError.TYPE_NOT_SUPPORTED_ERROR
|
||||
} else {
|
||||
// pasting images via virtual keyboard only available since Android 7.1 (API 25)
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) {
|
||||
error = CopyToClipboardError.ANDROID_VERSION_TO_OLD_ERROR
|
||||
} else {
|
||||
val uri: Uri? =
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM)
|
||||
} else {
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
|
||||
}
|
||||
val clip = ClipData.newUri(contentResolver, "image", uri)
|
||||
systemClipboardManager.setPrimaryClip(clip)
|
||||
bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, uri)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error = CopyToClipboardError.TYPE_NOT_SUPPORTED_ERROR
|
||||
}
|
||||
}
|
||||
|
||||
setContent {
|
||||
ProvideLocalizedResources(this, forceLayoutDirection = LayoutDirection.Ltr) {
|
||||
FlorisImeTheme {
|
||||
BottomSheetHostUi(isShowing = true, onHide = { finish() }) {
|
||||
val panelStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarActionsEditor)
|
||||
val headerStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarActionsEditorHeader)
|
||||
val subheaderStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarActionsEditorSubheader)
|
||||
val context = LocalContext.current
|
||||
Swipable {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.snyggBackground(
|
||||
context,
|
||||
panelStyle,
|
||||
fallbackColor = FlorisImeTheme.fallbackSurfaceColor()
|
||||
)
|
||||
.snyggClip(panelStyle)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.snyggBackground(context, headerStyle),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Spacer(modifier = Modifier.weight(1F))
|
||||
BottomSheetDefaults.DragHandle(
|
||||
color = headerStyle.foreground.solidColor(
|
||||
context,
|
||||
default = FlorisImeTheme.fallbackContentColor()
|
||||
),
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1F))
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.snyggBackground(context, headerStyle),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = error?.showError()
|
||||
?: bitmap?.let { stringRes(id = R.string.send_to_clipboard__description__copied_image_to_clipboard) }
|
||||
?: stringRes(R.string.send_to_clipboard__unknown_error),
|
||||
color = headerStyle.foreground.solidColor(
|
||||
context,
|
||||
default = FlorisImeTheme.fallbackContentColor()
|
||||
),
|
||||
fontSize = headerStyle.fontSize.spSize(),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
Spacer(Modifier.height(48.dp))
|
||||
}
|
||||
bitmap?.let {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
bitmap = bitmap!!.asImageBitmap(),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
Button(
|
||||
onClick = { finish() },
|
||||
modifier = Modifier.align(alignment = Alignment.End),
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
//containerColor = buttonContainer.background.solidColor(context = context),
|
||||
contentColor = subheaderStyle.foreground.solidColor(context = context),
|
||||
)
|
||||
) {
|
||||
Text(text = stringRes(id = R.string.action__ok))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
internal fun Swipable(
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val swipeableState = rememberSwipeableState(
|
||||
initialValue = BottomSheetValue.Expanded,
|
||||
confirmStateChange = {
|
||||
if (it == BottomSheetValue.Collapsed) {
|
||||
finish()
|
||||
}
|
||||
true
|
||||
}
|
||||
)
|
||||
BoxWithConstraints {
|
||||
val constraintsScope = this
|
||||
val maxHeight = with(LocalDensity.current) {
|
||||
constraintsScope.maxHeight.toPx()
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.swipeable(
|
||||
state = swipeableState,
|
||||
orientation = Orientation.Vertical,
|
||||
anchors = mapOf(
|
||||
0f to BottomSheetValue.Expanded,
|
||||
maxHeight to BottomSheetValue.Collapsed,
|
||||
),
|
||||
resistance = SwipeableDefaults.resistanceConfig(
|
||||
anchors = setOf(0f, maxHeight),
|
||||
factorAtMin = 0F
|
||||
)
|
||||
)
|
||||
.offset {
|
||||
IntOffset(
|
||||
x = 0,
|
||||
y = swipeableState.offset.value.roundToInt()
|
||||
)
|
||||
}
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.SideEffect
|
||||
@@ -87,6 +87,7 @@ import dev.patrickgold.florisboard.ime.keyboard.ProvideKeyboardRowBaseHeight
|
||||
import dev.patrickgold.florisboard.ime.landscapeinput.LandscapeInputUiMode
|
||||
import dev.patrickgold.florisboard.ime.lifecycle.LifecycleInputMethodService
|
||||
import dev.patrickgold.florisboard.ime.media.MediaInputLayout
|
||||
import dev.patrickgold.florisboard.ime.nlp.NlpInlineAutofill
|
||||
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
|
||||
import dev.patrickgold.florisboard.ime.onehanded.OneHandedPanel
|
||||
import dev.patrickgold.florisboard.ime.sheet.BottomSheetHostUi
|
||||
@@ -97,14 +98,6 @@ import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionsEditorPa
|
||||
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.lib.android.AndroidInternalR
|
||||
import dev.patrickgold.florisboard.lib.android.AndroidVersion
|
||||
import dev.patrickgold.florisboard.lib.android.isOrientationLandscape
|
||||
import dev.patrickgold.florisboard.lib.android.isOrientationPortrait
|
||||
import dev.patrickgold.florisboard.lib.android.launchActivity
|
||||
import dev.patrickgold.florisboard.lib.android.setLocale
|
||||
import dev.patrickgold.florisboard.lib.android.showShortToast
|
||||
import dev.patrickgold.florisboard.lib.android.systemServiceOrNull
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisButton
|
||||
import dev.patrickgold.florisboard.lib.compose.ProvideLocalizedResources
|
||||
import dev.patrickgold.florisboard.lib.compose.SystemUiIme
|
||||
@@ -113,16 +106,23 @@ import dev.patrickgold.florisboard.lib.devtools.flogError
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogInfo
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogWarning
|
||||
import dev.patrickgold.florisboard.lib.observeAsTransformingState
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.SnyggSurface
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.shape
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.snyggBackground
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.snyggBorder
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.snyggShadow
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.solidColor
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.spSize
|
||||
import org.florisboard.lib.snygg.ui.SnyggSurface
|
||||
import org.florisboard.lib.snygg.ui.shape
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import org.florisboard.lib.snygg.ui.snyggBorder
|
||||
import org.florisboard.lib.snygg.ui.snyggShadow
|
||||
import org.florisboard.lib.snygg.ui.solidColor
|
||||
import org.florisboard.lib.snygg.ui.spSize
|
||||
import dev.patrickgold.florisboard.lib.util.ViewUtils
|
||||
import dev.patrickgold.florisboard.lib.util.debugSummarize
|
||||
import dev.patrickgold.florisboard.lib.util.launchActivity
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
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.systemServiceOrNull
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
@@ -273,9 +273,10 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
FlorisImeServiceReference = WeakReference(this)
|
||||
WindowCompat.setDecorFitsSystemWindows(window.window!!, false)
|
||||
subtypeManager.activeSubtypeFlow.collectLatestIn(lifecycleScope) { subtype ->
|
||||
val config = Configuration(resources.configuration)
|
||||
config.setLocale(subtype.primaryLocale)
|
||||
config.setLocale(subtype.primaryLocale.base)
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
}
|
||||
@@ -370,7 +371,7 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
flogInfo { "(no args)" }
|
||||
super.onFinishInput()
|
||||
editorInstance.handleFinishInput()
|
||||
nlpManager.clearInlineSuggestions()
|
||||
NlpInlineAutofill.clearInlineSuggestions()
|
||||
}
|
||||
|
||||
override fun onWindowShown() {
|
||||
@@ -402,6 +403,10 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
}
|
||||
|
||||
override fun onEvaluateFullscreenMode(): Boolean {
|
||||
val config = resources.configuration
|
||||
if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) {
|
||||
return false
|
||||
}
|
||||
return when (prefs.keyboard.landscapeInputUiMode.get()) {
|
||||
LandscapeInputUiMode.DYNAMICALLY_SHOW -> super.onEvaluateFullscreenMode()
|
||||
LandscapeInputUiMode.NEVER_SHOW -> false
|
||||
@@ -433,23 +438,26 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
override fun onCreateInlineSuggestionsRequest(uiExtras: Bundle): InlineSuggestionsRequest? {
|
||||
return if (prefs.smartbar.enabled.get() && prefs.suggestion.api30InlineSuggestionsEnabled.get()) {
|
||||
flogInfo(LogTopic.IMS_EVENTS) {
|
||||
"Creating inline suggestions request because Smartbar and inline suggestions are enabled."
|
||||
}
|
||||
val stylesBundle = themeManager.createInlineSuggestionUiStyleBundle(this)
|
||||
val spec = InlinePresentationSpec.Builder(InlineSuggestionUiSmallestSize, InlineSuggestionUiBiggestSize)
|
||||
.setStyle(stylesBundle)
|
||||
.build()
|
||||
InlineSuggestionsRequest.Builder(listOf(spec)).let { request ->
|
||||
request.setMaxSuggestionCount(InlineSuggestionsRequest.SUGGESTION_COUNT_UNLIMITED)
|
||||
request.build()
|
||||
}
|
||||
} else {
|
||||
if (!prefs.smartbar.enabled.get() || !prefs.suggestion.api30InlineSuggestionsEnabled.get()) {
|
||||
flogInfo(LogTopic.IMS_EVENTS) {
|
||||
"Ignoring inline suggestions request because Smartbar and/or inline suggestions are disabled."
|
||||
}
|
||||
null
|
||||
return null
|
||||
}
|
||||
|
||||
flogInfo(LogTopic.IMS_EVENTS) { "Creating inline suggestions request" }
|
||||
val stylesBundle = themeManager.createInlineSuggestionUiStyleBundle(this)
|
||||
val spec = InlinePresentationSpec.Builder(
|
||||
InlineSuggestionUiSmallestSize,
|
||||
InlineSuggestionUiBiggestSize,
|
||||
).run {
|
||||
setStyle(stylesBundle)
|
||||
build()
|
||||
}
|
||||
|
||||
return InlineSuggestionsRequest.Builder(listOf(spec)).run {
|
||||
setMaxSuggestionCount(InlineSuggestionsRequest.SUGGESTION_COUNT_UNLIMITED)
|
||||
build()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,8 +467,7 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
flogInfo(LogTopic.IMS_EVENTS) {
|
||||
"Received inline suggestions response with ${inlineSuggestions.size} suggestion(s) provided."
|
||||
}
|
||||
nlpManager.showInlineSuggestions(inlineSuggestions)
|
||||
return true
|
||||
return NlpInlineAutofill.showInlineSuggestions(this, inlineSuggestions)
|
||||
}
|
||||
|
||||
override fun onComputeInsets(outInsets: Insets?) {
|
||||
@@ -769,7 +776,7 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
?: "ACTION",
|
||||
shape = actionStyle.shape.shape(),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
backgroundColor = actionStyle.background.solidColor(context, FlorisImeTheme.fallbackContentColor()),
|
||||
containerColor = actionStyle.background.solidColor(context, FlorisImeTheme.fallbackContentColor()),
|
||||
contentColor = actionStyle.foreground.solidColor(context, FlorisImeTheme.fallbackSurfaceColor()),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-2024 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,12 +31,14 @@ import dev.patrickgold.florisboard.ime.keyboard.IncognitoMode
|
||||
import dev.patrickgold.florisboard.ime.keyboard.SpaceBarMode
|
||||
import dev.patrickgold.florisboard.ime.landscapeinput.LandscapeInputUiMode
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHairStyle
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiRecentlyUsedHelper
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistory
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSkinTone
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSuggestionType
|
||||
import dev.patrickgold.florisboard.ime.nlp.SpellingLanguageMode
|
||||
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
|
||||
import dev.patrickgold.florisboard.ime.smartbar.CandidatesDisplayMode
|
||||
import dev.patrickgold.florisboard.ime.smartbar.ExtendedActionsPlacement
|
||||
import dev.patrickgold.florisboard.ime.smartbar.IncognitoDisplayMode
|
||||
import dev.patrickgold.florisboard.ime.smartbar.SmartbarLayout
|
||||
import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionArrangement
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
|
||||
@@ -45,16 +47,18 @@ 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.ime.theme.extCoreTheme
|
||||
import dev.patrickgold.florisboard.lib.android.isOrientationPortrait
|
||||
import org.florisboard.lib.android.isOrientationPortrait
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
|
||||
import dev.patrickgold.florisboard.lib.observeAsTransformingState
|
||||
import dev.patrickgold.florisboard.lib.snygg.SnyggLevel
|
||||
import org.florisboard.lib.snygg.SnyggLevel
|
||||
import dev.patrickgold.florisboard.lib.util.VersionName
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceMigrationEntry
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceModel
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceType
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
fun florisPreferenceModel() = JetPref.getOrCreatePreferenceModel(AppPrefs::class, ::AppPrefs)
|
||||
|
||||
@@ -65,6 +69,10 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
key = "advanced__settings_theme",
|
||||
default = AppTheme.AUTO,
|
||||
)
|
||||
val useMaterialYou = boolean(
|
||||
key = "advanced__use_material_you",
|
||||
default = true,
|
||||
)
|
||||
val settingsLanguage = string(
|
||||
key = "advanced__settings_language",
|
||||
default = "auto",
|
||||
@@ -166,6 +174,10 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
key = "devtools__show_spelling_overlay",
|
||||
default = false,
|
||||
)
|
||||
val showInlineAutofillOverlay = boolean(
|
||||
key = "devtools__show_inline_autofill_overlay",
|
||||
default = false,
|
||||
)
|
||||
val showKeyTouchBoundaries = boolean(
|
||||
key = "devtools__show_touch_boundaries",
|
||||
default = false,
|
||||
@@ -188,6 +200,67 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
)
|
||||
}
|
||||
|
||||
val emoji = Emoji()
|
||||
inner class Emoji {
|
||||
val preferredSkinTone = enum(
|
||||
key = "emoji__preferred_skin_tone",
|
||||
default = EmojiSkinTone.DEFAULT,
|
||||
)
|
||||
val preferredHairStyle = enum(
|
||||
key = "emoji__preferred_hair_style",
|
||||
default = EmojiHairStyle.DEFAULT,
|
||||
)
|
||||
val historyEnabled = boolean(
|
||||
key = "emoji__history_enabled",
|
||||
default = true,
|
||||
)
|
||||
val historyData = custom(
|
||||
key = "emoji__history_data",
|
||||
default = EmojiHistory.Empty,
|
||||
serializer = EmojiHistory.Serializer,
|
||||
)
|
||||
val historyPinnedUpdateStrategy = enum(
|
||||
key = "emoji__history_pinned_update_strategy",
|
||||
default = EmojiHistory.UpdateStrategy.MANUAL_SORT_PREPEND,
|
||||
)
|
||||
val historyPinnedMaxSize = int(
|
||||
key = "emoji__history_pinned_max_size",
|
||||
default = EmojiHistory.MaxSizeUnlimited,
|
||||
)
|
||||
val historyRecentUpdateStrategy = enum(
|
||||
key = "emoji__history_recent_update_strategy",
|
||||
default = EmojiHistory.UpdateStrategy.AUTO_SORT_PREPEND,
|
||||
)
|
||||
val historyRecentMaxSize = int(
|
||||
key = "emoji__history_recent_max_size",
|
||||
default = 90,
|
||||
)
|
||||
val suggestionEnabled = boolean(
|
||||
key = "emoji__suggestion_enabled",
|
||||
default = true,
|
||||
)
|
||||
val suggestionType = enum(
|
||||
key = "emoji__suggestion_type",
|
||||
default = EmojiSuggestionType.LEADING_COLON,
|
||||
)
|
||||
val suggestionUpdateHistory = boolean(
|
||||
key = "emoji__suggestion_update_history",
|
||||
default = true,
|
||||
)
|
||||
val suggestionCandidateShowName = boolean(
|
||||
key = "emoji__suggestion_candidate_show_name",
|
||||
default = false,
|
||||
)
|
||||
val suggestionQueryMinLength = int(
|
||||
key = "emoji__suggestion_query_min_length",
|
||||
default = 3,
|
||||
)
|
||||
val suggestionCandidateMaxCount = int(
|
||||
key = "emoji__suggestion_candidate_max_count",
|
||||
default = 5,
|
||||
)
|
||||
}
|
||||
|
||||
val gestures = Gestures()
|
||||
inner class Gestures {
|
||||
val swipeUp = enum(
|
||||
@@ -471,6 +544,10 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
key = "keyboard__space_bar_switches_to_characters",
|
||||
default = true,
|
||||
)
|
||||
val incognitoDisplayMode = enum(
|
||||
key = "keyboard__incognito_indicator",
|
||||
default = IncognitoDisplayMode.DISPLAY_BEHIND_KEYBOARD,
|
||||
)
|
||||
|
||||
fun keyHintConfiguration(): KeyHintConfiguration {
|
||||
return KeyHintConfiguration(
|
||||
@@ -521,27 +598,6 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
)
|
||||
}
|
||||
|
||||
val media = Media()
|
||||
inner class Media {
|
||||
val emojiRecentlyUsed = custom(
|
||||
key = "media__emoji_recently_used",
|
||||
default = emptyList(),
|
||||
serializer = EmojiRecentlyUsedHelper.Serializer,
|
||||
)
|
||||
val emojiRecentlyUsedMaxSize = int(
|
||||
key = "media__emoji_recently_used_max_size",
|
||||
default = 90,
|
||||
)
|
||||
val emojiPreferredSkinTone = enum(
|
||||
key = "media__emoji_preferred_skin_tone",
|
||||
default = EmojiSkinTone.DEFAULT,
|
||||
)
|
||||
val emojiPreferredHairStyle = enum(
|
||||
key = "media__emoji_preferred_hair_style",
|
||||
default = EmojiHairStyle.DEFAULT,
|
||||
)
|
||||
}
|
||||
|
||||
val smartbar = Smartbar()
|
||||
inner class Smartbar {
|
||||
val enabled = boolean(
|
||||
@@ -565,6 +621,7 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
key = "smartbar__shared_actions_expanded",
|
||||
default = false,
|
||||
)
|
||||
@Deprecated("Always enabled due to UX issues")
|
||||
val sharedActionsAutoExpandCollapse = boolean(
|
||||
key = "smartbar__shared_actions_auto_expand_collapse",
|
||||
default = true,
|
||||
@@ -633,19 +690,11 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
key = "theme__mode",
|
||||
default = ThemeMode.FOLLOW_SYSTEM,
|
||||
)
|
||||
val dayThemeAdaptToApp = boolean(
|
||||
key = "theme__day_theme_adapt_to_app",
|
||||
default = false,
|
||||
)
|
||||
val dayThemeId = custom(
|
||||
key = "theme__day_theme_id",
|
||||
default = extCoreTheme("floris_day"),
|
||||
serializer = ExtensionComponentName.Serializer,
|
||||
)
|
||||
val nightThemeAdaptToApp = boolean(
|
||||
key = "theme__night_theme_adapt_to_app",
|
||||
default = false,
|
||||
)
|
||||
val nightThemeId = custom(
|
||||
key = "theme__night_theme_id",
|
||||
default = extCoreTheme("floris_night"),
|
||||
@@ -682,8 +731,7 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
"gestures__space_bar_swipe_right", "gestures__space_bar_long_press", "gestures__delete_key_swipe_left",
|
||||
"gestures__delete_key_long_press", "keyboard__hinted_number_row_mode", "keyboard__hinted_symbols_mode",
|
||||
"keyboard__utility_key_action", "keyboard__one_handed_mode", "keyboard__landscape_input_ui_mode",
|
||||
"localization__display_language_names_in", "media__emoji_preferred_skin_tone",
|
||||
"media__emoji_preferred_hair_style", "smartbar__primary_actions_row_type",
|
||||
"localization__display_language_names_in", "smartbar__primary_actions_row_type",
|
||||
"smartbar__secondary_actions_placement", "smartbar__secondary_actions_row_type", "spelling__language_mode",
|
||||
"suggestion__display_mode", "theme__mode", "theme__editor_display_colors_as",
|
||||
"theme__editor_display_kbd_after_dialogs", "theme__editor_level",
|
||||
@@ -705,6 +753,32 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate media prefs to emoji prefs
|
||||
// Keep migration rule until: 0.6 dev cycle
|
||||
"media__emoji_recently_used" -> {
|
||||
val emojiValues = entry.rawValue.split(";")
|
||||
val recent = emojiValues.map {
|
||||
dev.patrickgold.florisboard.ime.media.emoji.Emoji(it, "", emptyList())
|
||||
}
|
||||
val data = EmojiHistory(emptyList(), recent)
|
||||
entry.transform(key = "emoji__history_data", rawValue = Json.encodeToString(data))
|
||||
}
|
||||
"media__emoji_recently_used_max_size" -> {
|
||||
entry.transform(key = "emoji__history_recent_max_size")
|
||||
}
|
||||
"media__emoji_preferred_skin_tone" -> {
|
||||
entry.transform(
|
||||
key = "emoji__preferred_skin_tone",
|
||||
rawValue = entry.rawValue.uppercase(), // keep until: 0.5 dev cycle
|
||||
)
|
||||
}
|
||||
"media__emoji_preferred_hair_style" -> {
|
||||
entry.transform(
|
||||
key = "emoji__preferred_hair_style",
|
||||
rawValue = entry.rawValue.uppercase(), // keep until: 0.5 dev cycle
|
||||
)
|
||||
}
|
||||
|
||||
// Default: keep entry
|
||||
else -> entry.keepAsIs()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,637 @@
|
||||
package dev.patrickgold.florisboard.app
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.settings.theme.DisplayColorsAs
|
||||
import dev.patrickgold.florisboard.app.settings.theme.DisplayKbdAfterDialogs
|
||||
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
|
||||
import dev.patrickgold.florisboard.ime.input.CapitalizationBehavior
|
||||
import dev.patrickgold.florisboard.ime.input.HapticVibrationMode
|
||||
import dev.patrickgold.florisboard.ime.input.InputFeedbackActivationMode
|
||||
import dev.patrickgold.florisboard.ime.keyboard.IncognitoMode
|
||||
import dev.patrickgold.florisboard.ime.keyboard.SpaceBarMode
|
||||
import dev.patrickgold.florisboard.ime.landscapeinput.LandscapeInputUiMode
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistory
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSkinTone
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSuggestionType
|
||||
import dev.patrickgold.florisboard.ime.nlp.SpellingLanguageMode
|
||||
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
|
||||
import dev.patrickgold.florisboard.ime.smartbar.CandidatesDisplayMode
|
||||
import dev.patrickgold.florisboard.ime.smartbar.ExtendedActionsPlacement
|
||||
import dev.patrickgold.florisboard.ime.smartbar.IncognitoDisplayMode
|
||||
import dev.patrickgold.florisboard.ime.smartbar.SmartbarLayout
|
||||
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 org.florisboard.lib.snygg.SnyggLevel
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreferenceEntry
|
||||
import dev.patrickgold.jetpref.datastore.ui.listPrefEntries
|
||||
import org.florisboard.lib.kotlin.curlyFormat
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
private const val DEFAULT = ""
|
||||
|
||||
private val ENUM_DISPLAY_ENTRIES = mapOf<Pair<KClass<*>, String>, @Composable () -> List<ListPreferenceEntry<*>>>(
|
||||
AppTheme::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = AppTheme.AUTO,
|
||||
label = stringRes(R.string.settings__system_default),
|
||||
)
|
||||
entry(
|
||||
key = AppTheme.AUTO_AMOLED,
|
||||
label = stringRes(R.string.pref__advanced__settings_theme__auto_amoled),
|
||||
)
|
||||
entry(
|
||||
key = AppTheme.LIGHT,
|
||||
label = stringRes(R.string.pref__advanced__settings_theme__light),
|
||||
)
|
||||
entry(
|
||||
key = AppTheme.DARK,
|
||||
label = stringRes(R.string.pref__advanced__settings_theme__dark),
|
||||
)
|
||||
entry(
|
||||
key = AppTheme.AMOLED_DARK,
|
||||
label = stringRes(R.string.pref__advanced__settings_theme__amoled_dark),
|
||||
)
|
||||
}
|
||||
},
|
||||
CandidatesDisplayMode::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = CandidatesDisplayMode.CLASSIC,
|
||||
label = stringRes(R.string.enum__candidates_display_mode__classic),
|
||||
)
|
||||
entry(
|
||||
key = CandidatesDisplayMode.DYNAMIC,
|
||||
label = stringRes(R.string.enum__candidates_display_mode__dynamic),
|
||||
)
|
||||
entry(
|
||||
key = CandidatesDisplayMode.DYNAMIC_SCROLLABLE,
|
||||
label = stringRes(R.string.enum__candidates_display_mode__dynamic_scrollable),
|
||||
)
|
||||
}
|
||||
},
|
||||
CapitalizationBehavior::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = CapitalizationBehavior.CAPSLOCK_BY_DOUBLE_TAP,
|
||||
label = stringRes(R.string.enum__capitalization_behavior__capslock_by_double_tap),
|
||||
)
|
||||
entry(
|
||||
key = CapitalizationBehavior.CAPSLOCK_BY_CYCLE,
|
||||
label = stringRes(R.string.enum__capitalization_behavior__capslock_by_cycle),
|
||||
)
|
||||
}
|
||||
},
|
||||
DisplayColorsAs::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = DisplayColorsAs.HEX8,
|
||||
label = stringRes(R.string.enum__display_colors_as__hex8),
|
||||
description = stringRes(R.string.general__example_given).curlyFormat("example" to "#4caf50ff"),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = DisplayColorsAs.RGBA,
|
||||
label = stringRes(R.string.enum__display_colors_as__rgba),
|
||||
description = stringRes(R.string.general__example_given).curlyFormat("example" to "rgba(76,175,80,1.0)"),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
}
|
||||
},
|
||||
DisplayKbdAfterDialogs::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = DisplayKbdAfterDialogs.ALWAYS,
|
||||
label = stringRes(R.string.enum__display_kbd_after_dialogs__always),
|
||||
description = stringRes(R.string.enum__display_kbd_after_dialogs__always__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = DisplayKbdAfterDialogs.NEVER,
|
||||
label = stringRes(R.string.enum__display_kbd_after_dialogs__never),
|
||||
description = stringRes(R.string.enum__display_kbd_after_dialogs__never__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = DisplayKbdAfterDialogs.REMEMBER,
|
||||
label = stringRes(R.string.enum__display_kbd_after_dialogs__remember),
|
||||
description = stringRes(R.string.enum__display_kbd_after_dialogs__remember__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
}
|
||||
},
|
||||
DisplayLanguageNamesIn::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = DisplayLanguageNamesIn.SYSTEM_LOCALE,
|
||||
label = stringRes(R.string.enum__display_language_names_in__system_locale),
|
||||
description = stringRes(R.string.enum__display_language_names_in__system_locale__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = DisplayLanguageNamesIn.NATIVE_LOCALE,
|
||||
label = stringRes(R.string.enum__display_language_names_in__native_locale),
|
||||
description = stringRes(R.string.enum__display_language_names_in__native_locale__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
}
|
||||
},
|
||||
EmojiHistory.UpdateStrategy::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = EmojiHistory.UpdateStrategy.AUTO_SORT_PREPEND,
|
||||
label = stringRes(R.string.enum__emoji_history_update_strategy__auto_sort_prepend),
|
||||
description = stringRes(R.string.enum__emoji_history_update_strategy__auto_sort_prepend__description),
|
||||
)
|
||||
entry(
|
||||
key = EmojiHistory.UpdateStrategy.AUTO_SORT_APPEND,
|
||||
label = stringRes(R.string.enum__emoji_history_update_strategy__auto_sort_append),
|
||||
description = stringRes(R.string.enum__emoji_history_update_strategy__auto_sort_append__description),
|
||||
)
|
||||
entry(
|
||||
key = EmojiHistory.UpdateStrategy.MANUAL_SORT_PREPEND,
|
||||
label = stringRes(R.string.enum__emoji_history_update_strategy__manual_sort_prepend),
|
||||
description = stringRes(R.string.enum__emoji_history_update_strategy__manual_sort_prepend__description),
|
||||
)
|
||||
entry(
|
||||
key = EmojiHistory.UpdateStrategy.MANUAL_SORT_APPEND,
|
||||
label = stringRes(R.string.enum__emoji_history_update_strategy__manual_sort_append),
|
||||
description = stringRes(R.string.enum__emoji_history_update_strategy__manual_sort_append__description),
|
||||
)
|
||||
}
|
||||
},
|
||||
EmojiSkinTone::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = EmojiSkinTone.DEFAULT,
|
||||
label = stringRes(
|
||||
R.string.enum__emoji_skin_tone__default,
|
||||
"emoji" to "\uD83D\uDC4B" // 👋
|
||||
),
|
||||
)
|
||||
entry(
|
||||
key = EmojiSkinTone.LIGHT_SKIN_TONE,
|
||||
label = stringRes(
|
||||
R.string.enum__emoji_skin_tone__light_skin_tone,
|
||||
"emoji" to "\uD83D\uDC4B\uD83C\uDFFB" // 👋🏻
|
||||
),
|
||||
)
|
||||
entry(
|
||||
key = EmojiSkinTone.MEDIUM_LIGHT_SKIN_TONE,
|
||||
label = stringRes(
|
||||
R.string.enum__emoji_skin_tone__medium_light_skin_tone,
|
||||
"emoji" to "\uD83D\uDC4B\uD83C\uDFFC" // 👋🏼
|
||||
),
|
||||
)
|
||||
entry(
|
||||
key = EmojiSkinTone.MEDIUM_SKIN_TONE,
|
||||
label = stringRes(
|
||||
R.string.enum__emoji_skin_tone__medium_skin_tone,
|
||||
"emoji" to "\uD83D\uDC4B\uD83C\uDFFD" // 👋🏽
|
||||
),
|
||||
)
|
||||
entry(
|
||||
key = EmojiSkinTone.MEDIUM_DARK_SKIN_TONE,
|
||||
label = stringRes(
|
||||
R.string.enum__emoji_skin_tone__medium_dark_skin_tone,
|
||||
"emoji" to "\uD83D\uDC4B\uD83C\uDFFE" // 👋🏾
|
||||
),
|
||||
)
|
||||
entry(
|
||||
key = EmojiSkinTone.DARK_SKIN_TONE,
|
||||
label = stringRes(
|
||||
R.string.enum__emoji_skin_tone__dark_skin_tone,
|
||||
"emoji" to "\uD83D\uDC4B\uD83C\uDFFF" // 👋🏿
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
EmojiSuggestionType::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = EmojiSuggestionType.LEADING_COLON,
|
||||
label = stringRes(R.string.enum__emoji_suggestion_type__leading_colon),
|
||||
description = stringRes(R.string.enum__emoji_suggestion_type__leading_colon__description),
|
||||
)
|
||||
entry(
|
||||
key = EmojiSuggestionType.INLINE_TEXT,
|
||||
label = stringRes(R.string.enum__emoji_suggestion_type__inline_text),
|
||||
description = stringRes(R.string.enum__emoji_suggestion_type__inline_text__description),
|
||||
)
|
||||
}
|
||||
},
|
||||
ExtendedActionsPlacement::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = ExtendedActionsPlacement.ABOVE_CANDIDATES,
|
||||
label = stringRes(R.string.enum__extended_actions_placement__above_candidates),
|
||||
description = stringRes(R.string.enum__extended_actions_placement__above_candidates__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = ExtendedActionsPlacement.BELOW_CANDIDATES,
|
||||
label = stringRes(R.string.enum__extended_actions_placement__below_candidates),
|
||||
description = stringRes(R.string.enum__extended_actions_placement__below_candidates__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = ExtendedActionsPlacement.OVERLAY_APP_UI,
|
||||
label = stringRes(R.string.enum__extended_actions_placement__overlay_app_ui),
|
||||
description = stringRes(R.string.enum__extended_actions_placement__overlay_app_ui__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
}
|
||||
},
|
||||
HapticVibrationMode::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = HapticVibrationMode.USE_VIBRATOR_DIRECTLY,
|
||||
label = stringRes(R.string.enum__haptic_vibration_mode__use_vibrator_directly),
|
||||
description = stringRes(R.string.enum__haptic_vibration_mode__use_vibrator_directly__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = HapticVibrationMode.USE_HAPTIC_FEEDBACK_INTERFACE,
|
||||
label = stringRes(R.string.enum__haptic_vibration_mode__use_haptic_feedback_interface),
|
||||
description = stringRes(R.string.enum__haptic_vibration_mode__use_haptic_feedback_interface__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
}
|
||||
},
|
||||
KeyHintMode::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = KeyHintMode.ACCENT_PRIORITY,
|
||||
label = stringRes(R.string.enum__key_hint_mode__accent_priority),
|
||||
description = stringRes(R.string.enum__key_hint_mode__accent_priority__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = KeyHintMode.HINT_PRIORITY,
|
||||
label = stringRes(R.string.enum__key_hint_mode__hint_priority),
|
||||
description = stringRes(R.string.enum__key_hint_mode__hint_priority__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = KeyHintMode.SMART_PRIORITY,
|
||||
label = stringRes(R.string.enum__key_hint_mode__smart_priority),
|
||||
description = stringRes(R.string.enum__key_hint_mode__smart_priority__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
}
|
||||
},
|
||||
IncognitoDisplayMode::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = IncognitoDisplayMode.REPLACE_SHARED_ACTIONS_TOGGLE,
|
||||
label = stringRes(id = R.string.enum__incognito_display_mode__replace_shared_actions_toggle),
|
||||
)
|
||||
entry(
|
||||
key = IncognitoDisplayMode.DISPLAY_BEHIND_KEYBOARD,
|
||||
label = stringRes(id = R.string.enum__incognito_display_mode__display_behind_keyboard),
|
||||
)
|
||||
}
|
||||
},
|
||||
IncognitoMode::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = IncognitoMode.FORCE_OFF,
|
||||
label = stringRes(R.string.enum__incognito_mode__force_off),
|
||||
description = stringRes(R.string.enum__incognito_mode__force_off__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = IncognitoMode.DYNAMIC_ON_OFF,
|
||||
label = stringRes(R.string.enum__incognito_mode__dynamic_on_off),
|
||||
description = stringRes(R.string.enum__incognito_mode__dynamic_on_off__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = IncognitoMode.FORCE_ON,
|
||||
label = stringRes(R.string.enum__incognito_mode__force_on),
|
||||
description = stringRes(R.string.enum__incognito_mode__force_on__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
}
|
||||
},
|
||||
InputFeedbackActivationMode::class to "audio" to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = InputFeedbackActivationMode.RESPECT_SYSTEM_SETTINGS,
|
||||
label = stringRes(R.string.enum__input_feedback_activation_mode__audio_respect_system_settings),
|
||||
)
|
||||
entry(
|
||||
key = InputFeedbackActivationMode.IGNORE_SYSTEM_SETTINGS,
|
||||
label = stringRes(R.string.enum__input_feedback_activation_mode__audio_ignore_system_settings),
|
||||
)
|
||||
}
|
||||
},
|
||||
InputFeedbackActivationMode::class to "haptic" to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = InputFeedbackActivationMode.RESPECT_SYSTEM_SETTINGS,
|
||||
label = stringRes(R.string.enum__input_feedback_activation_mode__haptic_respect_system_settings),
|
||||
)
|
||||
entry(
|
||||
key = InputFeedbackActivationMode.IGNORE_SYSTEM_SETTINGS,
|
||||
label = stringRes(R.string.enum__input_feedback_activation_mode__haptic_ignore_system_settings),
|
||||
)
|
||||
}
|
||||
},
|
||||
LandscapeInputUiMode::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = LandscapeInputUiMode.NEVER_SHOW,
|
||||
label = stringRes(R.string.enum__landscape_input_ui_mode__never_show),
|
||||
)
|
||||
entry(
|
||||
key = LandscapeInputUiMode.ALWAYS_SHOW,
|
||||
label = stringRes(R.string.enum__landscape_input_ui_mode__always_show),
|
||||
)
|
||||
entry(
|
||||
key = LandscapeInputUiMode.DYNAMICALLY_SHOW,
|
||||
label = stringRes(R.string.enum__landscape_input_ui_mode__dynamically_show),
|
||||
)
|
||||
}
|
||||
},
|
||||
OneHandedMode::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = OneHandedMode.OFF,
|
||||
label = stringRes(R.string.enum__one_handed_mode__off),
|
||||
)
|
||||
entry(
|
||||
key = OneHandedMode.START,
|
||||
label = stringRes(R.string.enum__one_handed_mode__start),
|
||||
)
|
||||
entry(
|
||||
key = OneHandedMode.END,
|
||||
label = stringRes(R.string.enum__one_handed_mode__end),
|
||||
)
|
||||
}
|
||||
},
|
||||
SmartbarLayout::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = SmartbarLayout.SUGGESTIONS_ONLY,
|
||||
label = stringRes(R.string.enum__smartbar_layout__suggestions_only),
|
||||
description = stringRes(R.string.enum__smartbar_layout__suggestions_only__description),
|
||||
)
|
||||
entry(
|
||||
key = SmartbarLayout.ACTIONS_ONLY,
|
||||
label = stringRes(R.string.enum__smartbar_layout__actions_only),
|
||||
description = stringRes(R.string.enum__smartbar_layout__actions_only__description),
|
||||
)
|
||||
entry(
|
||||
key = SmartbarLayout.SUGGESTIONS_ACTIONS_SHARED,
|
||||
label = stringRes(R.string.enum__smartbar_layout__suggestions_action_shared),
|
||||
description = stringRes(R.string.enum__smartbar_layout__suggestions_action_shared__description),
|
||||
)
|
||||
entry(
|
||||
key = SmartbarLayout.SUGGESTIONS_ACTIONS_EXTENDED,
|
||||
label = stringRes(R.string.enum__smartbar_layout__suggestions_actions_extended),
|
||||
description = stringRes(R.string.enum__smartbar_layout__suggestions_actions_extended__description),
|
||||
)
|
||||
}
|
||||
},
|
||||
SnyggLevel::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = SnyggLevel.BASIC,
|
||||
label = stringRes(R.string.enum__snygg_level__basic),
|
||||
description = stringRes(R.string.enum__snygg_level__basic__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = SnyggLevel.ADVANCED,
|
||||
label = stringRes(R.string.enum__snygg_level__advanced),
|
||||
description = stringRes(R.string.enum__snygg_level__advanced__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = SnyggLevel.DEVELOPER,
|
||||
label = stringRes(R.string.enum__snygg_level__developer),
|
||||
description = stringRes(R.string.enum__snygg_level__developer__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
}
|
||||
},
|
||||
SpaceBarMode::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = SpaceBarMode.NOTHING,
|
||||
label = stringRes(R.string.enum__space_bar_mode__nothing),
|
||||
)
|
||||
entry(
|
||||
key = SpaceBarMode.CURRENT_LANGUAGE,
|
||||
label = stringRes(R.string.enum__space_bar_mode__current_language),
|
||||
)
|
||||
entry(
|
||||
key = SpaceBarMode.SPACE_BAR_KEY,
|
||||
label = stringRes(R.string.enum__space_bar_mode__space_bar_key),
|
||||
)
|
||||
}
|
||||
},
|
||||
SpellingLanguageMode::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = SpellingLanguageMode.USE_SYSTEM_LANGUAGES,
|
||||
label = stringRes(R.string.enum__spelling_language_mode__use_system_languages),
|
||||
)
|
||||
entry(
|
||||
key = SpellingLanguageMode.USE_KEYBOARD_SUBTYPES,
|
||||
label = stringRes(R.string.enum__spelling_language_mode__use_keyboard_subtypes),
|
||||
)
|
||||
}
|
||||
},
|
||||
SwipeAction::class to "general" to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = SwipeAction.NO_ACTION,
|
||||
label = stringRes(R.string.enum__swipe_action__no_action),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.CYCLE_TO_PREVIOUS_KEYBOARD_MODE,
|
||||
label = stringRes(R.string.enum__swipe_action__cycle_to_previous_keyboard_mode),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.CYCLE_TO_NEXT_KEYBOARD_MODE,
|
||||
label = stringRes(R.string.enum__swipe_action__cycle_to_next_keyboard_mode),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.DELETE_WORD,
|
||||
label = stringRes(R.string.enum__swipe_action__delete_word),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.HIDE_KEYBOARD,
|
||||
label = stringRes(R.string.enum__swipe_action__hide_keyboard),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.INSERT_SPACE,
|
||||
label = stringRes(R.string.enum__swipe_action__insert_space),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.MOVE_CURSOR_UP,
|
||||
label = stringRes(R.string.enum__swipe_action__move_cursor_up),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.MOVE_CURSOR_DOWN,
|
||||
label = stringRes(R.string.enum__swipe_action__move_cursor_down),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.MOVE_CURSOR_LEFT,
|
||||
label = stringRes(R.string.enum__swipe_action__move_cursor_left),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.MOVE_CURSOR_RIGHT,
|
||||
label = stringRes(R.string.enum__swipe_action__move_cursor_right),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.MOVE_CURSOR_START_OF_LINE,
|
||||
label = stringRes(R.string.enum__swipe_action__move_cursor_start_of_line),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.MOVE_CURSOR_END_OF_LINE,
|
||||
label = stringRes(R.string.enum__swipe_action__move_cursor_end_of_line),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.MOVE_CURSOR_START_OF_PAGE,
|
||||
label = stringRes(R.string.enum__swipe_action__move_cursor_start_of_page),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.MOVE_CURSOR_END_OF_PAGE,
|
||||
label = stringRes(R.string.enum__swipe_action__move_cursor_end_of_page),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.SHIFT,
|
||||
label = stringRes(R.string.enum__swipe_action__shift),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.REDO,
|
||||
label = stringRes(R.string.enum__swipe_action__redo),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.UNDO,
|
||||
label = stringRes(R.string.enum__swipe_action__undo),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.SWITCH_TO_CLIPBOARD_CONTEXT,
|
||||
label = stringRes(R.string.enum__swipe_action__switch_to_clipboard_context),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.SHOW_INPUT_METHOD_PICKER,
|
||||
label = stringRes(R.string.enum__swipe_action__show_input_method_picker),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.SWITCH_TO_PREV_SUBTYPE,
|
||||
label = stringRes(R.string.enum__swipe_action__switch_to_prev_subtype),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.SWITCH_TO_NEXT_SUBTYPE,
|
||||
label = stringRes(R.string.enum__swipe_action__switch_to_next_subtype),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.SWITCH_TO_PREV_KEYBOARD,
|
||||
label = stringRes(R.string.enum__swipe_action__switch_to_prev_keyboard),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.TOGGLE_SMARTBAR_VISIBILITY,
|
||||
label = stringRes(R.string.enum__swipe_action__toggle_smartbar_visibility),
|
||||
)
|
||||
}
|
||||
},
|
||||
SwipeAction::class to "deleteSwipe" to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = SwipeAction.NO_ACTION,
|
||||
label = stringRes(R.string.enum__swipe_action__no_action),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.DELETE_CHARACTERS_PRECISELY,
|
||||
label = stringRes(R.string.enum__swipe_action__delete_characters_precisely),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.DELETE_WORD,
|
||||
label = stringRes(R.string.enum__swipe_action__delete_word),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.DELETE_WORDS_PRECISELY,
|
||||
label = stringRes(R.string.enum__swipe_action__delete_words_precisely),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.SELECT_CHARACTERS_PRECISELY,
|
||||
label = stringRes(R.string.enum__swipe_action__select_characters_precisely),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.SELECT_WORDS_PRECISELY,
|
||||
label = stringRes(R.string.enum__swipe_action__select_words_precisely),
|
||||
)
|
||||
}
|
||||
},
|
||||
SwipeAction::class to "deleteLongPress" to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = SwipeAction.DELETE_CHARACTER,
|
||||
label = stringRes(R.string.enum__swipe_action__delete_character),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.DELETE_WORD,
|
||||
label = stringRes(R.string.enum__swipe_action__delete_word),
|
||||
)
|
||||
}
|
||||
},
|
||||
ThemeMode::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = ThemeMode.ALWAYS_DAY,
|
||||
label = stringRes(R.string.enum__theme_mode__always_day),
|
||||
)
|
||||
entry(
|
||||
key = ThemeMode.ALWAYS_NIGHT,
|
||||
label = stringRes(R.string.enum__theme_mode__always_night),
|
||||
)
|
||||
entry(
|
||||
key = ThemeMode.FOLLOW_SYSTEM,
|
||||
label = stringRes(R.string.enum__theme_mode__follow_system),
|
||||
)
|
||||
entry(
|
||||
key = ThemeMode.FOLLOW_TIME,
|
||||
label = stringRes(R.string.enum__theme_mode__follow_time),
|
||||
)
|
||||
}
|
||||
},
|
||||
UtilityKeyAction::class to DEFAULT to {
|
||||
listPrefEntries {
|
||||
entry(
|
||||
key = UtilityKeyAction.SWITCH_TO_EMOJIS,
|
||||
label = stringRes(R.string.enum__utility_key_action__switch_to_emojis),
|
||||
)
|
||||
entry(
|
||||
key = UtilityKeyAction.SWITCH_LANGUAGE,
|
||||
label = stringRes(R.string.enum__utility_key_action__switch_language),
|
||||
)
|
||||
entry(
|
||||
key = UtilityKeyAction.SWITCH_KEYBOARD_APP,
|
||||
label = stringRes(R.string.enum__utility_key_action__switch_keyboard_app),
|
||||
)
|
||||
entry(
|
||||
key = UtilityKeyAction.DYNAMIC_SWITCH_LANGUAGE_EMOJIS,
|
||||
label = stringRes(R.string.enum__utility_key_action__dynamic_switch_language_emojis),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun <V : Any> enumDisplayEntriesOf(
|
||||
enumClass: KClass<V>,
|
||||
variant: String = DEFAULT,
|
||||
): List<ListPreferenceEntry<V>> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return ENUM_DISPLAY_ENTRIES[enumClass to variant]?.invoke()
|
||||
as List<ListPreferenceEntry<V>>
|
||||
}
|
||||
@@ -17,45 +17,48 @@
|
||||
package dev.patrickgold.florisboard.app
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.displayCutoutPadding
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
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.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.cacheManager
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import dev.patrickgold.florisboard.lib.android.AndroidVersion
|
||||
import dev.patrickgold.florisboard.lib.android.hideAppIcon
|
||||
import dev.patrickgold.florisboard.lib.android.setLocale
|
||||
import dev.patrickgold.florisboard.lib.android.showAppIcon
|
||||
import dev.patrickgold.florisboard.lib.compose.LocalPreviewFieldController
|
||||
import dev.patrickgold.florisboard.lib.compose.PreviewKeyboardField
|
||||
import dev.patrickgold.florisboard.lib.compose.ProvideLocalizedResources
|
||||
import dev.patrickgold.florisboard.lib.compose.SystemUiApp
|
||||
import dev.patrickgold.florisboard.lib.compose.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
|
||||
|
||||
enum class AppTheme(val id: String) {
|
||||
AUTO("auto"),
|
||||
@@ -71,9 +74,11 @@ val LocalNavController = staticCompositionLocalOf<NavController> {
|
||||
|
||||
class FlorisAppActivity : ComponentActivity() {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val cacheManager by cacheManager()
|
||||
private var appTheme by mutableStateOf(AppTheme.AUTO)
|
||||
private var showAppIcon = true
|
||||
private var resourcesContext by mutableStateOf(this as Context)
|
||||
private var intentToBeHandled by mutableStateOf<Intent?>(null)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// Splash screen should be installed before calling super.onCreate()
|
||||
@@ -88,7 +93,8 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
}
|
||||
prefs.advanced.settingsLanguage.observe(this) {
|
||||
val config = Configuration(resources.configuration)
|
||||
config.setLocale(if (it == "auto") FlorisLocale.default() else FlorisLocale.fromTag(it))
|
||||
val locale = if (it == "auto") FlorisLocale.default() else FlorisLocale.fromTag(it)
|
||||
config.setLocale(locale.base)
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
if (AndroidVersion.ATMOST_API28_P) {
|
||||
@@ -111,14 +117,15 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
AppVersionUtils.updateVersionOnInstallAndLastUse(this, prefs)
|
||||
setContent {
|
||||
ProvideLocalizedResources(resourcesContext) {
|
||||
FlorisAppTheme(theme = appTheme) {
|
||||
Surface(color = MaterialTheme.colors.background) {
|
||||
SystemUiApp()
|
||||
val useMaterialYou by prefs.advanced.useMaterialYou.observeAsState()
|
||||
FlorisAppTheme(theme = appTheme, isMaterialYouAware = useMaterialYou) {
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
AppContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
onNewIntent(intent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +143,25 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
setIntent(intent)
|
||||
|
||||
if (intent.action == Intent.ACTION_VIEW && intent.categories?.contains(Intent.CATEGORY_BROWSABLE) == true) {
|
||||
intentToBeHandled = intent
|
||||
return
|
||||
}
|
||||
if (intent.action == Intent.ACTION_VIEW && intent.data != null) {
|
||||
intentToBeHandled = intent
|
||||
return
|
||||
}
|
||||
if (intent.action == Intent.ACTION_SEND && intent.clipData != null) {
|
||||
intentToBeHandled = intent
|
||||
return
|
||||
}
|
||||
intentToBeHandled = null
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AppContent() {
|
||||
val navController = rememberNavController()
|
||||
@@ -154,8 +180,11 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.statusBarsPadding()
|
||||
//.statusBarsPadding()
|
||||
.navigationBarsPadding()
|
||||
.conditional(LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
displayCutoutPadding()
|
||||
}
|
||||
.imePadding(),
|
||||
) {
|
||||
Routes.AppNavHost(
|
||||
@@ -168,8 +197,22 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
SideEffect {
|
||||
navController.setOnBackPressedDispatcher(this.onBackPressedDispatcher)
|
||||
LaunchedEffect(intentToBeHandled) {
|
||||
val intent = intentToBeHandled
|
||||
if (intent != null) {
|
||||
if (intent.action == Intent.ACTION_VIEW && intent.categories?.contains(Intent.CATEGORY_BROWSABLE) == true) {
|
||||
navController.handleDeepLink(intent)
|
||||
} else {
|
||||
val data = if (intent.action == Intent.ACTION_VIEW) {
|
||||
intent.data!!
|
||||
} else {
|
||||
intent.clipData!!.getItemAt(0).uri
|
||||
}
|
||||
val workspace = runCatching { cacheManager.readFromUriIntoCache(data) }.getOrNull()
|
||||
navController.navigate(Routes.Ext.Import(ExtensionImportScreenType.EXT_ANY, workspace?.uuid))
|
||||
}
|
||||
}
|
||||
intentToBeHandled = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,19 +16,32 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app
|
||||
|
||||
import androidx.compose.animation.AnimatedContentScope
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideIn
|
||||
import androidx.compose.animation.slideOut
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.navDeepLink
|
||||
import dev.patrickgold.florisboard.app.devtools.AndroidLocalesScreen
|
||||
import dev.patrickgold.florisboard.app.devtools.AndroidSettingsScreen
|
||||
import dev.patrickgold.florisboard.app.devtools.DevtoolsScreen
|
||||
import dev.patrickgold.florisboard.app.devtools.ExportDebugLogScreen
|
||||
import dev.patrickgold.florisboard.app.ext.CheckUpdatesScreen
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionEditScreen
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionExportScreen
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionHomeScreen
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionImportScreen
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionImportScreenType
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionListScreen
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionListScreenType
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionViewScreen
|
||||
import dev.patrickgold.florisboard.app.settings.HomeScreen
|
||||
import dev.patrickgold.florisboard.app.settings.about.AboutScreen
|
||||
@@ -58,7 +71,7 @@ import dev.patrickgold.florisboard.app.settings.typing.TypingScreen
|
||||
import dev.patrickgold.florisboard.app.setup.SetupScreen
|
||||
import org.florisboard.lib.kotlin.curlyFormat
|
||||
|
||||
@Suppress("FunctionName")
|
||||
@Suppress("FunctionName", "ConstPropertyName")
|
||||
object Routes {
|
||||
object Setup {
|
||||
const val Screen = "setup"
|
||||
@@ -117,6 +130,14 @@ object Routes {
|
||||
}
|
||||
|
||||
object Ext {
|
||||
const val Home = "ext"
|
||||
|
||||
const val List = "ext/list/{type}?showUpdate={showUpdate}"
|
||||
fun List(
|
||||
type: ExtensionListScreenType,
|
||||
showUpdate: Boolean
|
||||
) = List.curlyFormat("type" to type.id, "showUpdate" to showUpdate)
|
||||
|
||||
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 ?: ""))
|
||||
@@ -133,6 +154,8 @@ object Routes {
|
||||
|
||||
const val View = "ext/view/{id}"
|
||||
fun View(id: String) = View.curlyFormat("id" to id)
|
||||
|
||||
const val CheckUpdates = "ext/check-updates"
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -141,97 +164,131 @@ object Routes {
|
||||
navController: NavHostController,
|
||||
startDestination: String,
|
||||
) {
|
||||
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,
|
||||
startDestination = startDestination,
|
||||
enterTransition = {
|
||||
slideIn { IntOffset(it.width, 0) } + fadeIn()
|
||||
},
|
||||
exitTransition = {
|
||||
slideOut { IntOffset(-it.width, 0) } + fadeOut()
|
||||
},
|
||||
popEnterTransition = {
|
||||
slideIn { IntOffset(-it.width, 0) } + fadeIn()
|
||||
},
|
||||
popExitTransition = {
|
||||
slideOut { IntOffset(it.width, 0) } + fadeOut()
|
||||
}
|
||||
) {
|
||||
composable(Setup.Screen) { SetupScreen() }
|
||||
|
||||
composable(Settings.Home) { HomeScreen() }
|
||||
composableWithDeepLink(Settings.Home) { HomeScreen() }
|
||||
|
||||
composable(Settings.Localization) { LocalizationScreen() }
|
||||
composable(Settings.SelectLocale) { SelectLocaleScreen() }
|
||||
composable(Settings.LanguagePackManager) { navBackStack ->
|
||||
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)
|
||||
}
|
||||
composable(Settings.SubtypeAdd) { SubtypeEditorScreen(null) }
|
||||
composable(Settings.SubtypeEdit) { navBackStack ->
|
||||
composableWithDeepLink(Settings.SubtypeAdd) { SubtypeEditorScreen(null) }
|
||||
composableWithDeepLink(Settings.SubtypeEdit) { navBackStack ->
|
||||
val id = navBackStack.arguments?.getString("id")?.toLongOrNull()
|
||||
SubtypeEditorScreen(id)
|
||||
}
|
||||
|
||||
composable(Settings.Theme) { ThemeScreen() }
|
||||
composable(Settings.ThemeManager) { navBackStack ->
|
||||
composableWithDeepLink(Settings.Theme) { ThemeScreen() }
|
||||
composableWithDeepLink(Settings.ThemeManager) { navBackStack ->
|
||||
val action = navBackStack.arguments?.getString("action")?.let { actionId ->
|
||||
ThemeManagerScreenAction.entries.firstOrNull { it.id == actionId }
|
||||
}
|
||||
ThemeManagerScreen(action)
|
||||
}
|
||||
|
||||
composable(Settings.Keyboard) { KeyboardScreen() }
|
||||
composable(Settings.InputFeedback) { InputFeedbackScreen() }
|
||||
composableWithDeepLink(Settings.Keyboard) { KeyboardScreen() }
|
||||
composableWithDeepLink(Settings.InputFeedback) { InputFeedbackScreen() }
|
||||
|
||||
composable(Settings.Smartbar) { SmartbarScreen() }
|
||||
composableWithDeepLink(Settings.Smartbar) { SmartbarScreen() }
|
||||
|
||||
composable(Settings.Typing) { TypingScreen() }
|
||||
composableWithDeepLink(Settings.Typing) { TypingScreen() }
|
||||
|
||||
composable(Settings.Dictionary) { DictionaryScreen() }
|
||||
composable(Settings.UserDictionary) { navBackStack ->
|
||||
composableWithDeepLink(Settings.Dictionary) { DictionaryScreen() }
|
||||
composableWithDeepLink(Settings.UserDictionary) { navBackStack ->
|
||||
val type = navBackStack.arguments?.getString("type")?.let { typeId ->
|
||||
UserDictionaryType.entries.firstOrNull { it.id == typeId }
|
||||
}
|
||||
UserDictionaryScreen(type!!)
|
||||
}
|
||||
|
||||
composable(Settings.Gestures) { GesturesScreen() }
|
||||
composableWithDeepLink(Settings.Gestures) { GesturesScreen() }
|
||||
|
||||
composable(Settings.Clipboard) { ClipboardScreen() }
|
||||
composableWithDeepLink(Settings.Clipboard) { ClipboardScreen() }
|
||||
|
||||
composable(Settings.Media) { MediaScreen() }
|
||||
composableWithDeepLink(Settings.Media) { MediaScreen() }
|
||||
|
||||
composable(Settings.Advanced) { AdvancedScreen() }
|
||||
composable(Settings.Backup) { BackupScreen() }
|
||||
composable(Settings.Restore) { RestoreScreen() }
|
||||
composableWithDeepLink(Settings.Advanced) { AdvancedScreen() }
|
||||
composableWithDeepLink(Settings.Backup) { BackupScreen() }
|
||||
composableWithDeepLink(Settings.Restore) { RestoreScreen() }
|
||||
|
||||
composable(Settings.About) { AboutScreen() }
|
||||
composable(Settings.ProjectLicense) { ProjectLicenseScreen() }
|
||||
composable(Settings.ThirdPartyLicenses) { ThirdPartyLicensesScreen() }
|
||||
composableWithDeepLink(Settings.About) { AboutScreen() }
|
||||
composableWithDeepLink(Settings.ProjectLicense) { ProjectLicenseScreen() }
|
||||
composableWithDeepLink(Settings.ThirdPartyLicenses) { ThirdPartyLicensesScreen() }
|
||||
|
||||
composable(Devtools.Home) { DevtoolsScreen() }
|
||||
composable(Devtools.AndroidLocales) { AndroidLocalesScreen() }
|
||||
composable(Devtools.AndroidSettings) { navBackStack ->
|
||||
composableWithDeepLink(Devtools.Home) { DevtoolsScreen() }
|
||||
composableWithDeepLink(Devtools.AndroidLocales) { AndroidLocalesScreen() }
|
||||
composableWithDeepLink(Devtools.AndroidSettings) { navBackStack ->
|
||||
val name = navBackStack.arguments?.getString("name")
|
||||
AndroidSettingsScreen(name)
|
||||
}
|
||||
composable(Devtools.ExportDebugLog) { ExportDebugLogScreen() }
|
||||
composableWithDeepLink(Devtools.ExportDebugLog) { ExportDebugLogScreen() }
|
||||
|
||||
composable(Ext.Edit) { navBackStack ->
|
||||
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.Edit) { navBackStack ->
|
||||
val extensionId = navBackStack.arguments?.getString("id")
|
||||
val serialType = navBackStack.arguments?.getString("serial_type")
|
||||
ExtensionEditScreen(
|
||||
id = extensionId.toString(),
|
||||
createSerialType = serialType.takeIf { it != null && it.isNotBlank() },
|
||||
createSerialType = serialType.takeIf { !it.isNullOrBlank() },
|
||||
)
|
||||
}
|
||||
composable(Ext.Export) { navBackStack ->
|
||||
composableWithDeepLink(Ext.Export) { navBackStack ->
|
||||
val extensionId = navBackStack.arguments?.getString("id")
|
||||
ExtensionExportScreen(id = extensionId.toString())
|
||||
}
|
||||
composable(Ext.Import) { navBackStack ->
|
||||
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)
|
||||
}
|
||||
composable(Ext.View) { navBackStack ->
|
||||
composableWithDeepLink(Ext.View) { navBackStack ->
|
||||
val extensionId = navBackStack.arguments?.getString("id")
|
||||
ExtensionViewScreen(id = extensionId.toString())
|
||||
}
|
||||
composableWithDeepLink(Ext.CheckUpdates) {
|
||||
CheckUpdatesScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,85 @@ package dev.patrickgold.florisboard.app.apptheme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
/* Legacy Colors
|
||||
val Green500 = Color(0xFF4CAF50)
|
||||
val Green700 = Color(0xFF388E3C)
|
||||
val Orange700 = Color(0xFFF57C00)
|
||||
val Orange900 = Color(0xFFE65100)
|
||||
*/
|
||||
|
||||
//Colors created with the material theme builder
|
||||
|
||||
val primaryLight = Color(0xFF006E1C)
|
||||
val onPrimaryLight = Color(0xFFFFFFFF)
|
||||
val primaryContainerLight = Color(0xFF58BC5B)
|
||||
val onPrimaryContainerLight = Color(0xFF002204)
|
||||
val secondaryLight = Color(0xFF005E16)
|
||||
val onSecondaryLight = Color(0xFFFFFFFF)
|
||||
val secondaryContainerLight = Color(0xFF2E8534)
|
||||
val onSecondaryContainerLight = Color(0xFFFFFFFF)
|
||||
val tertiaryLight = Color(0xFF964900)
|
||||
val onTertiaryLight = Color(0xFFFFFFFF)
|
||||
val tertiaryContainerLight = Color(0xFFFF8926)
|
||||
val onTertiaryContainerLight = Color(0xFF341500)
|
||||
val errorLight = Color(0xFFBA1A1A)
|
||||
val onErrorLight = Color(0xFFFFFFFF)
|
||||
val errorContainerLight = Color(0xFFFFDAD6)
|
||||
val onErrorContainerLight = Color(0xFF410002)
|
||||
val backgroundLight = Color(0xFFF5FBEF)
|
||||
val onBackgroundLight = Color(0xFF171D16)
|
||||
val surfaceLight = Color(0xFFF5FBEF)
|
||||
val onSurfaceLight = Color(0xFF171D16)
|
||||
val surfaceVariantLight = Color(0xFFDAE6D4)
|
||||
val onSurfaceVariantLight = Color(0xFF3F4A3C)
|
||||
val outlineLight = Color(0xFF6F7A6B)
|
||||
val outlineVariantLight = Color(0xFFBECAB9)
|
||||
val scrimLight = Color(0xFF000000)
|
||||
val inverseSurfaceLight = Color(0xFF2C322A)
|
||||
val inverseOnSurfaceLight = Color(0xFFEDF3E7)
|
||||
val inversePrimaryLight = Color(0xFF78DC77)
|
||||
val surfaceDimLight = Color(0xFFD6DCD0)
|
||||
val surfaceBrightLight = Color(0xFFF5FBEF)
|
||||
val surfaceContainerLowestLight = Color(0xFFFFFFFF)
|
||||
val surfaceContainerLowLight = Color(0xFFF0F6EA)
|
||||
val surfaceContainerLight = Color(0xFFEAF0E4)
|
||||
val surfaceContainerHighLight = Color(0xFFE4EADE)
|
||||
val surfaceContainerHighestLight = Color(0xFFDEE4D9)
|
||||
|
||||
val primaryDark = Color(0xFF78DC77)
|
||||
val onPrimaryDark = Color(0xFF00390A)
|
||||
val primaryContainerDark = Color(0xFF43A648)
|
||||
val onPrimaryContainerDark = Color(0xFF000000)
|
||||
val secondaryDark = Color(0xFF82DB7E)
|
||||
val onSecondaryDark = Color(0xFF00390A)
|
||||
val secondaryContainerDark = Color(0xFF2D8433)
|
||||
val onSecondaryContainerDark = Color(0xFFFFFFFF)
|
||||
val tertiaryDark = Color(0xFFFFB786)
|
||||
val onTertiaryDark = Color(0xFF502400)
|
||||
val tertiaryContainerDark = Color(0xFFEA7600)
|
||||
val onTertiaryContainerDark = Color(0xFF030100)
|
||||
val errorDark = Color(0xFFFFB4AB)
|
||||
val onErrorDark = Color(0xFF690005)
|
||||
val errorContainerDark = Color(0xFF93000A)
|
||||
val onErrorContainerDark = Color(0xFFFFDAD6)
|
||||
val backgroundDark = Color(0xFF0F120E)
|
||||
val onBackgroundDark = Color(0xFFDEE4D9)
|
||||
val surfaceDark = Color(0xFF0F120E)
|
||||
val onSurfaceDark = Color(0xFFDEE4D9)
|
||||
val surfaceVariantDark = Color(0xFF3F4A3C)
|
||||
val onSurfaceVariantDark = Color(0xFFBECAB9)
|
||||
val outlineDark = Color(0xFF899484)
|
||||
val outlineVariantDark = Color(0xFF3F4A3C)
|
||||
val scrimDark = Color(0xFF000000)
|
||||
val inverseSurfaceDark = Color(0xFFDEE4D9)
|
||||
val inverseOnSurfaceDark = Color(0xFF2C322A)
|
||||
val inversePrimaryDark = Color(0xFF006E1C)
|
||||
val surfaceDimDark = Color(0xFF0F150E)
|
||||
val surfaceBrightDark = Color(0xFF353B33)
|
||||
val surfaceContainerLowestDark = Color(0xFF0A1009)
|
||||
val surfaceContainerLowDark = Color(0xFF171D16)
|
||||
val surfaceContainerDark = Color(0xFF1B211A)
|
||||
val surfaceContainerHighDark = Color(0xFF262C24)
|
||||
val surfaceContainerHighestDark = Color(0xFF30362E)
|
||||
|
||||
val amoledDark = Color(0xFF000000)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package dev.patrickgold.florisboard.app.apptheme
|
||||
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Shapes
|
||||
import androidx.compose.material3.Shapes
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
val Shapes = Shapes(
|
||||
|
||||
@@ -16,41 +16,46 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.apptheme
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.Colors
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.darkColors
|
||||
import androidx.compose.material.lightColors
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
import dev.patrickgold.florisboard.app.AppTheme
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
|
||||
private val AmoledDarkColorPalette = darkColors(
|
||||
/*private val AmoledDarkColorPalette = darkColorScheme(
|
||||
primary = Green500,
|
||||
primaryVariant = Green700,
|
||||
secondary = Orange700,
|
||||
secondaryVariant = Orange900,
|
||||
secondary = Green700,
|
||||
tertiary = Orange700,
|
||||
// = Orange900,
|
||||
|
||||
background = Color(0xFF000000),
|
||||
surface = Color(0xFF212121),
|
||||
)
|
||||
|
||||
private val DarkColorPalette = darkColors(
|
||||
private val DarkColorPalette = darkColorScheme(
|
||||
primary = Green500,
|
||||
primaryVariant = Green700,
|
||||
secondary = Orange700,
|
||||
secondaryVariant = Orange900,
|
||||
secondary = Green700,
|
||||
tertiary = Orange700,
|
||||
//secondaryVariant = Orange900,
|
||||
|
||||
background = Color(0xFF1F1F1F),
|
||||
surface = Color(0xFF212121),
|
||||
)
|
||||
|
||||
private val LightColorPalette = lightColors(
|
||||
private val LightColorPalette = lightColorScheme(
|
||||
primary = Green500,
|
||||
primaryVariant = Green700,
|
||||
secondary = Orange700,
|
||||
secondaryVariant = Orange900,
|
||||
secondary = Green700,
|
||||
tertiary = Orange700,
|
||||
//secondaryVariant = Orange900,
|
||||
|
||||
background = Color(0xFFFFFFFF),
|
||||
surface = Color(0xFFE7E7E7),
|
||||
@@ -63,35 +68,184 @@ private val LightColorPalette = lightColors(
|
||||
onBackground = Color.Black,
|
||||
onSurface = Color.Black,
|
||||
*/
|
||||
)*/
|
||||
|
||||
private val lightScheme = lightColorScheme(
|
||||
primary = primaryLight,
|
||||
onPrimary = onPrimaryLight,
|
||||
primaryContainer = primaryContainerLight,
|
||||
onPrimaryContainer = onPrimaryContainerLight,
|
||||
secondary = secondaryLight,
|
||||
onSecondary = onSecondaryLight,
|
||||
secondaryContainer = secondaryContainerLight,
|
||||
onSecondaryContainer = onSecondaryContainerLight,
|
||||
tertiary = tertiaryLight,
|
||||
onTertiary = onTertiaryLight,
|
||||
tertiaryContainer = tertiaryContainerLight,
|
||||
onTertiaryContainer = onTertiaryContainerLight,
|
||||
error = errorLight,
|
||||
onError = onErrorLight,
|
||||
errorContainer = errorContainerLight,
|
||||
onErrorContainer = onErrorContainerLight,
|
||||
background = backgroundLight,
|
||||
onBackground = onBackgroundLight,
|
||||
surface = surfaceLight,
|
||||
onSurface = onSurfaceLight,
|
||||
surfaceVariant = surfaceVariantLight,
|
||||
onSurfaceVariant = onSurfaceVariantLight,
|
||||
outline = outlineLight,
|
||||
outlineVariant = outlineVariantLight,
|
||||
scrim = scrimLight,
|
||||
inverseSurface = inverseSurfaceLight,
|
||||
inverseOnSurface = inverseOnSurfaceLight,
|
||||
inversePrimary = inversePrimaryLight,
|
||||
surfaceDim = surfaceDimLight,
|
||||
surfaceBright = surfaceBrightLight,
|
||||
surfaceContainerLowest = surfaceContainerLowestLight,
|
||||
surfaceContainerLow = surfaceContainerLowLight,
|
||||
surfaceContainer = surfaceContainerLight,
|
||||
surfaceContainerHigh = surfaceContainerHighLight,
|
||||
surfaceContainerHighest = surfaceContainerHighestLight,
|
||||
)
|
||||
|
||||
private val darkScheme = darkColorScheme(
|
||||
primary = primaryDark,
|
||||
onPrimary = onPrimaryDark,
|
||||
primaryContainer = primaryContainerDark,
|
||||
onPrimaryContainer = onPrimaryContainerDark,
|
||||
secondary = secondaryDark,
|
||||
onSecondary = onSecondaryDark,
|
||||
secondaryContainer = secondaryContainerDark,
|
||||
onSecondaryContainer = onSecondaryContainerDark,
|
||||
tertiary = tertiaryDark,
|
||||
onTertiary = onTertiaryDark,
|
||||
tertiaryContainer = tertiaryContainerDark,
|
||||
onTertiaryContainer = onTertiaryContainerDark,
|
||||
error = errorDark,
|
||||
onError = onErrorDark,
|
||||
errorContainer = errorContainerDark,
|
||||
onErrorContainer = onErrorContainerDark,
|
||||
background = backgroundDark,
|
||||
onBackground = onBackgroundDark,
|
||||
surface = surfaceDark,
|
||||
onSurface = onSurfaceDark,
|
||||
surfaceVariant = surfaceVariantDark,
|
||||
onSurfaceVariant = onSurfaceVariantDark,
|
||||
outline = outlineDark,
|
||||
outlineVariant = outlineVariantDark,
|
||||
scrim = scrimDark,
|
||||
inverseSurface = inverseSurfaceDark,
|
||||
inverseOnSurface = inverseOnSurfaceDark,
|
||||
inversePrimary = inversePrimaryDark,
|
||||
surfaceDim = surfaceDimDark,
|
||||
surfaceBright = surfaceBrightDark,
|
||||
surfaceContainerLowest = surfaceContainerLowestDark,
|
||||
surfaceContainerLow = surfaceContainerLowDark,
|
||||
surfaceContainer = surfaceContainerDark,
|
||||
surfaceContainerHigh = surfaceContainerHighDark,
|
||||
surfaceContainerHighest = surfaceContainerHighestDark,
|
||||
)
|
||||
|
||||
private val amoledScheme = darkScheme.copy(
|
||||
background = amoledDark,
|
||||
surface = amoledDark
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun FlorisAppTheme(
|
||||
theme: AppTheme,
|
||||
isMaterialYouAware: Boolean,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colors = when (theme) {
|
||||
AppTheme.AUTO -> when {
|
||||
isSystemInDarkTheme() -> DarkColorPalette
|
||||
else -> LightColorPalette
|
||||
|
||||
val colors = if (AndroidVersion.ATLEAST_API31_S) {
|
||||
when (theme) {
|
||||
AppTheme.AUTO -> when {
|
||||
isMaterialYouAware -> when {
|
||||
isSystemInDarkTheme() -> dynamicDarkColorScheme(LocalContext.current)
|
||||
else -> dynamicLightColorScheme(LocalContext.current)
|
||||
}
|
||||
|
||||
else -> {
|
||||
when {
|
||||
isSystemInDarkTheme() -> darkScheme
|
||||
else -> lightScheme
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AppTheme.AUTO_AMOLED -> when {
|
||||
isMaterialYouAware -> when {
|
||||
isSystemInDarkTheme() -> dynamicDarkColorScheme(LocalContext.current).copy(
|
||||
background = amoledDark,
|
||||
surface = amoledDark,
|
||||
)
|
||||
|
||||
else -> dynamicLightColorScheme(LocalContext.current)
|
||||
}
|
||||
|
||||
else -> {
|
||||
when {
|
||||
isSystemInDarkTheme() -> amoledScheme
|
||||
else -> lightScheme
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AppTheme.LIGHT -> when {
|
||||
isMaterialYouAware -> dynamicLightColorScheme(LocalContext.current)
|
||||
else -> lightScheme
|
||||
}
|
||||
|
||||
AppTheme.DARK -> when {
|
||||
isMaterialYouAware -> dynamicDarkColorScheme(LocalContext.current)
|
||||
else -> darkScheme
|
||||
}
|
||||
|
||||
AppTheme.AMOLED_DARK -> when {
|
||||
isMaterialYouAware -> dynamicDarkColorScheme(LocalContext.current).copy(
|
||||
background = amoledDark,
|
||||
surface = amoledDark,
|
||||
)
|
||||
|
||||
else -> amoledScheme
|
||||
}
|
||||
}
|
||||
AppTheme.AUTO_AMOLED -> when {
|
||||
isSystemInDarkTheme() -> AmoledDarkColorPalette
|
||||
else -> LightColorPalette
|
||||
} else {
|
||||
when (theme) {
|
||||
AppTheme.AUTO -> when {
|
||||
isSystemInDarkTheme() -> darkScheme
|
||||
else -> lightScheme
|
||||
}
|
||||
|
||||
AppTheme.AUTO_AMOLED -> when {
|
||||
isSystemInDarkTheme() -> darkScheme
|
||||
else -> lightScheme
|
||||
}
|
||||
|
||||
AppTheme.LIGHT -> lightScheme
|
||||
AppTheme.DARK -> darkScheme
|
||||
AppTheme.AMOLED_DARK -> amoledScheme
|
||||
}
|
||||
}
|
||||
|
||||
val darkTheme =
|
||||
theme == AppTheme.DARK
|
||||
|| theme == AppTheme.AMOLED_DARK
|
||||
|| (theme == AppTheme.AUTO && isSystemInDarkTheme())
|
||||
|| (theme == AppTheme.AUTO_AMOLED && isSystemInDarkTheme())
|
||||
|
||||
val view = LocalView.current
|
||||
if (!view.isInEditMode) {
|
||||
SideEffect {
|
||||
val window = (view.context as Activity).window
|
||||
WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightStatusBars = !darkTheme
|
||||
}
|
||||
AppTheme.LIGHT -> LightColorPalette
|
||||
AppTheme.DARK -> DarkColorPalette
|
||||
AppTheme.AMOLED_DARK -> AmoledDarkColorPalette
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colors = colors,
|
||||
colorScheme = colors,
|
||||
typography = Typography,
|
||||
shapes = Shapes,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
val Colors.outline: Color
|
||||
@Composable
|
||||
get() = this.onSurface.copy(alpha = ButtonDefaults.OutlinedBorderOpacity)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.apptheme
|
||||
|
||||
import androidx.compose.material.Typography
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
@@ -24,7 +24,7 @@ import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
body1 = TextStyle(
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp
|
||||
|
||||
@@ -21,26 +21,25 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Save
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
|
||||
import dev.patrickgold.florisboard.lib.android.showLongToast
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.io.subDir
|
||||
import dev.patrickgold.florisboard.lib.io.subFile
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import java.util.*
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import java.util.Locale
|
||||
|
||||
@Composable
|
||||
fun AndroidLocalesScreen() = FlorisScreen {
|
||||
|
||||
@@ -19,7 +19,7 @@ package dev.patrickgold.florisboard.app.devtools
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -27,7 +27,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.android.AndroidSettings
|
||||
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
|
||||
|
||||
@@ -16,13 +16,15 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.devtools
|
||||
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.collectAsState
|
||||
@@ -40,10 +42,12 @@ import androidx.compose.ui.unit.sp
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.ime.nlp.NlpInlineAutofill
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.florisboard.nlpManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@@ -57,6 +61,7 @@ fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
val showPrimaryClip by prefs.devtools.showPrimaryClip.observeAsState()
|
||||
val showInputStateOverlay by prefs.devtools.showInputStateOverlay.observeAsState()
|
||||
val showSpellingOverlay by prefs.devtools.showSpellingOverlay.observeAsState()
|
||||
val showInlineAutofillOverlay by prefs.devtools.showInlineAutofillOverlay.observeAsState()
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides Color.White,
|
||||
@@ -72,6 +77,9 @@ fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
if (showSpellingOverlay) {
|
||||
DevtoolsSpellingOverlay()
|
||||
}
|
||||
if (showInlineAutofillOverlay && AndroidVersion.ATLEAST_API30_R) {
|
||||
DevtoolsInlineAutofillOverlay()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,7 +125,6 @@ private fun DevtoolsInputStateOverlay() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
private fun DevtoolsSpellingOverlay() {
|
||||
val context = LocalContext.current
|
||||
@@ -160,6 +167,25 @@ private fun DevtoolsSpellingOverlay() {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
@Composable
|
||||
private fun DevtoolsInlineAutofillOverlay() {
|
||||
val inlineSuggestions by NlpInlineAutofill.suggestions.collectAsState()
|
||||
|
||||
DevtoolsOverlayBox(title = "Inline autofill overlay (${inlineSuggestions.size})") {
|
||||
for (inlineSuggestion in inlineSuggestions) {
|
||||
DevtoolsSubGroup(title = "NlpInlineSuggestion") {
|
||||
val info = inlineSuggestion.info
|
||||
DevtoolsText(text = "info.type: ${info.type}")
|
||||
DevtoolsText(text = "info.source: ${info.source}")
|
||||
DevtoolsText(text = "info.isPinned: ${info.isPinned}")
|
||||
val view = inlineSuggestion.view
|
||||
DevtoolsText(text = "view: ${view?.javaClass?.name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DevtoolsOverlayBox(
|
||||
title: String,
|
||||
|
||||
@@ -27,8 +27,8 @@ 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 dev.patrickgold.florisboard.lib.android.AndroidSettings
|
||||
import dev.patrickgold.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.AndroidSettings
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisConfirmDeleteDialog
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
@@ -36,6 +36,7 @@ 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.AndroidVersion
|
||||
|
||||
class DebugOnPurposeCrashException : Exception(
|
||||
"Success! The app crashed purposely to display this beautiful screen we all love :)"
|
||||
@@ -84,6 +85,13 @@ fun DevtoolsScreen() = FlorisScreen {
|
||||
summary = stringRes(R.string.devtools__show_spelling_overlay__summary),
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.devtools.showInlineAutofillOverlay,
|
||||
title = stringRes(R.string.devtools__show_inline_autofill_overlay__label),
|
||||
summary = stringRes(R.string.devtools__show_inline_autofill_overlay__summary),
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
visibleIf = { AndroidVersion.ATLEAST_API30_R },
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.devtools.showKeyTouchBoundaries,
|
||||
title = stringRes(R.string.devtools__show_key_touch_boundaries__label),
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.devtools
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -35,19 +37,21 @@ import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
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.clipboardManager
|
||||
import dev.patrickgold.florisboard.lib.android.showShortToast
|
||||
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
|
||||
|
||||
// TODO: This screen is just a quick thrown-together thing and needs further enhancing in the UI and in localization
|
||||
// TODO: This screen is just a quick thrown-together thing and needs further enhancing in the UI
|
||||
@Composable
|
||||
fun ExportDebugLogScreen() = FlorisScreen {
|
||||
title = "Debug log"
|
||||
title = stringRes(R.string.devtools__debuglog__title)
|
||||
scrollable = false
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
@@ -55,21 +59,36 @@ fun ExportDebugLogScreen() = FlorisScreen {
|
||||
val clipboardManager by context.clipboardManager()
|
||||
|
||||
var debugLog by remember { mutableStateOf<List<String>?>(null) }
|
||||
var formattedDebugLog by remember { mutableStateOf<List<String>?>(null) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
debugLog = Devtools.generateDebugLog(context, prefs, includeLogcat = true).lines()
|
||||
formattedDebugLog = Devtools.generateDebugLogForGithub(context, prefs, includeLogcat = true).lines()
|
||||
}
|
||||
|
||||
bottomBar {
|
||||
FlorisButton(
|
||||
onClick = {
|
||||
clipboardManager.addNewPlaintext(debugLog!!.joinToString("\n"))
|
||||
context.showShortToast("Copied debug log to clipboard")
|
||||
},
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Export (copy to clipboard)",
|
||||
enabled = debugLog != null,
|
||||
)
|
||||
) {
|
||||
FlorisButton(
|
||||
onClick = {
|
||||
clipboardManager.addNewPlaintext(debugLog!!.joinToString("\n"))
|
||||
context.showShortToast(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
},
|
||||
modifier = Modifier,
|
||||
text = stringRes(R.string.devtools__debuglog__copy_log),
|
||||
enabled = debugLog != null,
|
||||
)
|
||||
FlorisButton(
|
||||
onClick = {
|
||||
clipboardManager.addNewPlaintext(formattedDebugLog!!.joinToString("\n"))
|
||||
context.showShortToast(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
},
|
||||
text = stringRes(R.string.devtools__debuglog__copy_for_github),
|
||||
enabled = debugLog != null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
content {
|
||||
@@ -86,7 +105,7 @@ fun ExportDebugLogScreen() = FlorisScreen {
|
||||
val log = debugLog
|
||||
if (log == null) {
|
||||
item {
|
||||
Text("Loading...")
|
||||
Text(stringRes(R.string.devtools__debuglog__loading))
|
||||
}
|
||||
} else {
|
||||
items(log) { logLine ->
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package dev.patrickgold.florisboard.app.ext
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Shop
|
||||
import androidx.compose.material.icons.outlined.FileDownload
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
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.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.kotlin.curlyFormat
|
||||
|
||||
@Composable
|
||||
fun UpdateBox(extensionIndex: List<Extension>) {
|
||||
val context = LocalContext.current
|
||||
FlorisOutlinedBox(
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 4.dp),
|
||||
text = stringRes(id = R.string.ext__update_box__internet_permission_hint),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 6.dp),
|
||||
) {
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
context.launchUrl(extensionIndex.generateUpdateUrl())
|
||||
},
|
||||
icon = Icons.Outlined.FileDownload,
|
||||
text = stringRes(id = R.string.ext__update_box__search_for_updates)
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AddonManagementReferenceBox(
|
||||
type: ExtensionListScreenType
|
||||
) {
|
||||
val navController = LocalNavController.current
|
||||
|
||||
FlorisOutlinedBox(
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
title = stringRes(id = R.string.ext__addon_management_box__managing_placeholder).curlyFormat(
|
||||
"extensions" to type.let { stringRes(id = it.titleResId).lowercase() }
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||
text = stringRes(id = R.string.ext__addon_management_box__addon_manager_info),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 6.dp),
|
||||
) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
val route = Routes.Ext.List(type, showUpdate = true)
|
||||
navController.navigate(
|
||||
route
|
||||
)
|
||||
},
|
||||
icon = Icons.Default.Shop,
|
||||
text = stringRes(id = R.string.ext__addon_management_box__go_to_page).curlyFormat(
|
||||
"ext_home_title" to stringRes(type.titleResId),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package dev.patrickgold.florisboard.app.ext
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
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
|
||||
|
||||
@Composable
|
||||
fun CheckUpdatesScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.ext__check_updates__title)
|
||||
|
||||
val context = LocalContext.current
|
||||
val extensionManager by context.extensionManager()
|
||||
val extensionIndex = extensionManager.combinedExtensionList()
|
||||
|
||||
content {
|
||||
UpdateBox(extensionIndex)
|
||||
}
|
||||
}
|
||||
@@ -21,21 +21,18 @@ import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ListItem
|
||||
import androidx.compose.material.LocalContentAlpha
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
@@ -97,8 +94,8 @@ fun ExtensionComponentView(
|
||||
}
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.body2,
|
||||
color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = LocalContentColor.current,
|
||||
)
|
||||
}
|
||||
is LanguagePackComponent -> {
|
||||
@@ -113,8 +110,8 @@ fun ExtensionComponentView(
|
||||
}
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.body2,
|
||||
color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = LocalContentColor.current,
|
||||
)
|
||||
}
|
||||
else -> { }
|
||||
@@ -132,7 +129,7 @@ fun ExtensionComponentView(
|
||||
icon = Icons.Default.Delete,
|
||||
text = stringRes(R.string.action__delete),
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
contentColor = MaterialTheme.colors.error,
|
||||
contentColor = MaterialTheme.colorScheme.error,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -149,7 +146,6 @@ fun ExtensionComponentView(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun <T : ExtensionComponent> ExtensionComponentListView(
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -160,19 +156,19 @@ fun <T : ExtensionComponent> ExtensionComponentListView(
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
ListItem(
|
||||
text = { Text(
|
||||
headlineContent = { Text(
|
||||
text = title,
|
||||
color = MaterialTheme.colors.secondary,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
) },
|
||||
trailing = if (onCreateBtnClick != null) {
|
||||
trailingContent = if (onCreateBtnClick != null) {
|
||||
@Composable {
|
||||
FlorisIconButton(
|
||||
onClick = onCreateBtnClick,
|
||||
icon = Icons.Default.Add,
|
||||
iconColor = MaterialTheme.colors.secondary,
|
||||
iconColor = MaterialTheme.colorScheme.secondary,
|
||||
)
|
||||
}
|
||||
} else { null },
|
||||
|
||||
@@ -28,14 +28,14 @@ import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextFieldDefaults
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.outlined.LibraryBooks
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Code
|
||||
import androidx.compose.material.icons.outlined.LibraryBooks
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -47,7 +47,6 @@ 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.apptheme.outline
|
||||
import dev.patrickgold.florisboard.app.settings.advanced.RadioListItem
|
||||
import dev.patrickgold.florisboard.app.settings.theme.DialogProperty
|
||||
import dev.patrickgold.florisboard.app.settings.theme.ThemeEditorScreen
|
||||
@@ -60,7 +59,6 @@ import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponentEditor
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponentImpl
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionEditor
|
||||
import dev.patrickgold.florisboard.lib.ValidationResult
|
||||
import dev.patrickgold.florisboard.lib.android.showLongToast
|
||||
import dev.patrickgold.florisboard.lib.cache.CacheManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisButtonBar
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
@@ -69,7 +67,6 @@ import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedTextField
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisUnsavedChangesDialog
|
||||
import dev.patrickgold.florisboard.lib.compose.autoMirrorForRtl
|
||||
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.ext.Extension
|
||||
@@ -85,15 +82,16 @@ import dev.patrickgold.florisboard.lib.ext.ExtensionValidation
|
||||
import dev.patrickgold.florisboard.lib.ext.validate
|
||||
import dev.patrickgold.florisboard.lib.io.FlorisRef
|
||||
import dev.patrickgold.florisboard.lib.io.ZipUtils
|
||||
import dev.patrickgold.florisboard.lib.io.subFile
|
||||
import dev.patrickgold.florisboard.lib.io.writeJson
|
||||
import dev.patrickgold.florisboard.lib.rememberValidationResult
|
||||
import dev.patrickgold.florisboard.lib.snygg.SnyggStylesheetJsonConfig
|
||||
import org.florisboard.lib.snygg.SnyggStylesheetJsonConfig
|
||||
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 java.util.*
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import org.florisboard.lib.kotlin.io.writeJson
|
||||
import java.util.UUID
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
private val TextFieldVerticalPadding = 8.dp
|
||||
@@ -300,8 +298,7 @@ private fun EditScreen(
|
||||
navigationIcon {
|
||||
FlorisIconButton(
|
||||
onClick = { handleBackPress() },
|
||||
modifier = Modifier.autoMirrorForRtl(),
|
||||
icon = Icons.Default.ArrowBack,
|
||||
icon = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -325,17 +322,17 @@ private fun EditScreen(
|
||||
FlorisOutlinedBox(
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
) {
|
||||
this@content.Preference(
|
||||
Preference(
|
||||
onClick = { workspace.currentAction = EditorAction.ManageMetaData },
|
||||
icon = Icons.Default.Code,
|
||||
title = stringRes(R.string.ext__editor__metadata__title),
|
||||
)
|
||||
this@content.Preference(
|
||||
Preference(
|
||||
onClick = { workspace.currentAction = EditorAction.ManageDependencies },
|
||||
icon = Icons.Outlined.LibraryBooks,
|
||||
icon = Icons.AutoMirrored.Outlined.LibraryBooks,
|
||||
title = stringRes(R.string.ext__editor__dependencies__title),
|
||||
)
|
||||
this@content.Preference(
|
||||
Preference(
|
||||
onClick = { workspace.currentAction = EditorAction.ManageFiles },
|
||||
icon = vectorResource(R.drawable.ic_file_blank),
|
||||
title = stringRes(R.string.ext__editor__files__title),
|
||||
@@ -863,7 +860,7 @@ private fun EditorSheetTextField(
|
||||
showValidationError: Boolean = false,
|
||||
validationResult: ValidationResult? = null,
|
||||
) {
|
||||
val borderColor = MaterialTheme.colors.outline
|
||||
val borderColor = MaterialTheme.colorScheme.outline
|
||||
Column(modifier = Modifier.padding(vertical = TextFieldVerticalPadding)) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@@ -872,14 +869,14 @@ private fun EditorSheetTextField(
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
style = MaterialTheme.typography.subtitle2,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
)
|
||||
if (isRequired) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 2.dp),
|
||||
text = "*",
|
||||
style = MaterialTheme.typography.subtitle2,
|
||||
color = MaterialTheme.colors.error,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -891,7 +888,7 @@ private fun EditorSheetTextField(
|
||||
singleLine = singleLine,
|
||||
showValidationError = showValidationError,
|
||||
validationResult = validationResult,
|
||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
unfocusedBorderColor = borderColor,
|
||||
disabledBorderColor = borderColor,
|
||||
)
|
||||
|
||||
@@ -23,7 +23,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.lib.android.showLongToast
|
||||
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
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package dev.patrickgold.florisboard.app.ext
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Input
|
||||
import androidx.compose.material.icons.filled.Keyboard
|
||||
import androidx.compose.material.icons.filled.Language
|
||||
import androidx.compose.material.icons.filled.Palette
|
||||
import androidx.compose.material.icons.filled.Shop
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
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.stringRes
|
||||
import dev.patrickgold.florisboard.lib.util.launchUrl
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
|
||||
@Composable
|
||||
fun ExtensionHomeScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.ext__home__title)
|
||||
previewFieldVisible = false
|
||||
|
||||
val context = LocalContext.current
|
||||
val navController = LocalNavController.current
|
||||
val extensionManager by context.extensionManager()
|
||||
val extensionIndex = extensionManager.combinedExtensionList()
|
||||
|
||||
content {
|
||||
FlorisOutlinedBox(
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 4.dp),
|
||||
text = stringRes(id = R.string.ext__home__info),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 6.dp),
|
||||
) {
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
context.launchUrl("https://${BuildConfig.FLADDONS_STORE_URL}/")
|
||||
},
|
||||
icon = Icons.Default.Shop,
|
||||
text = stringRes(id = R.string.ext__home__visit_store),
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
navController.navigate(Routes.Ext.Import(ExtensionImportScreenType.EXT_ANY, null))
|
||||
},
|
||||
icon = Icons.AutoMirrored.Filled.Input,
|
||||
text = stringRes(R.string.action__import),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
UpdateBox(extensionIndex = extensionIndex)
|
||||
|
||||
Preference(
|
||||
icon = Icons.Default.Palette,
|
||||
title = stringRes(R.string.ext__list__ext_theme),
|
||||
onClick = {
|
||||
navController.navigate(Routes.Ext.List(ExtensionListScreenType.EXT_THEME, false))
|
||||
},
|
||||
)
|
||||
Preference(
|
||||
icon = Icons.Default.Keyboard,
|
||||
title = stringRes(R.string.ext__list__ext_keyboard),
|
||||
onClick = {
|
||||
navController.navigate(Routes.Ext.List(ExtensionListScreenType.EXT_KEYBOARD, false))
|
||||
},
|
||||
)
|
||||
Preference(
|
||||
icon = Icons.Default.Language,
|
||||
title = stringRes(R.string.ext__list__ext_languagepack),
|
||||
onClick = {
|
||||
navController.navigate(Routes.Ext.List(ExtensionListScreenType.EXT_LANGUAGEPACK, false))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -29,14 +29,13 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -51,7 +50,7 @@ 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 dev.patrickgold.florisboard.lib.android.showLongToast
|
||||
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
|
||||
@@ -100,12 +99,6 @@ fun ExtensionImportScreen(type: ExtensionImportScreenType, initUuid: String?) =
|
||||
val cacheManager by context.cacheManager()
|
||||
val extensionManager by context.extensionManager()
|
||||
|
||||
val initWsUuid by rememberSaveable { mutableStateOf(initUuid) }
|
||||
var importResult by remember {
|
||||
val workspace = initWsUuid?.let { cacheManager.importer.getWorkspaceByUuid(it) }?.let { resultOk(it) }
|
||||
mutableStateOf(workspace)
|
||||
}
|
||||
|
||||
fun getSkipReason(fileInfo: CacheManager.FileInfo): Int {
|
||||
return when {
|
||||
!FileRegistry.matchesFileFilter(fileInfo, type.supportedFiles) -> {
|
||||
@@ -119,29 +112,37 @@ fun ExtensionImportScreen(type: ExtensionImportScreenType, initUuid: String?) =
|
||||
NATIVE_NULLPTR.toInt()
|
||||
}
|
||||
}
|
||||
fileInfo.mediaType == FileRegistry.FlexExtension.mediaType -> {
|
||||
else -> { // ext == null
|
||||
R.string.ext__import__file_skip_ext_corrupted
|
||||
}
|
||||
else -> {
|
||||
NATIVE_NULLPTR.toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Result<CacheManager.ImporterWorkspace>.mapSkipReasons(): Result<CacheManager.ImporterWorkspace> {
|
||||
return this.map { workspace ->
|
||||
workspace.inputFileInfos.forEach { fileInfo ->
|
||||
fileInfo.skipReason = getSkipReason(fileInfo)
|
||||
}
|
||||
workspace
|
||||
}
|
||||
}
|
||||
|
||||
var importResult by remember(initUuid) {
|
||||
val workspace = initUuid?.let { cacheManager.importer.getWorkspaceByUuid(it) }
|
||||
?.let { resultOk(it) }
|
||||
?.mapSkipReasons()
|
||||
mutableStateOf(workspace)
|
||||
}
|
||||
|
||||
val importLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.GetMultipleContents(),
|
||||
onResult = { uriList ->
|
||||
// If uri is null it indicates that the selection activity
|
||||
// was cancelled (mostly by pressing the back button), so
|
||||
// we don't display an error message here.
|
||||
if (uriList.isNullOrEmpty()) return@rememberLauncherForActivityResult
|
||||
if (uriList.isEmpty()) return@rememberLauncherForActivityResult
|
||||
importResult?.getOrNull()?.close()
|
||||
importResult = runCatching { cacheManager.readFromUriIntoCache(uriList) }.map { workspace ->
|
||||
workspace.inputFileInfos.forEach { fileInfo ->
|
||||
fileInfo.skipReason = getSkipReason(fileInfo)
|
||||
}
|
||||
workspace
|
||||
}
|
||||
importResult = runCatching { cacheManager.readFromUriIntoCache(uriList) }.mapSkipReasons()
|
||||
},
|
||||
)
|
||||
|
||||
@@ -197,15 +198,17 @@ fun ExtensionImportScreen(type: ExtensionImportScreenType, initUuid: String?) =
|
||||
}
|
||||
|
||||
content {
|
||||
FlorisOutlinedButton(
|
||||
onClick = {
|
||||
importLauncher.launch("*/*")
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(vertical = 16.dp)
|
||||
.align(Alignment.CenterHorizontally),
|
||||
text = stringRes(R.string.action__select_files),
|
||||
)
|
||||
if (initUuid == null) {
|
||||
FlorisOutlinedButton(
|
||||
onClick = {
|
||||
importLauncher.launch("*/*")
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(vertical = 16.dp)
|
||||
.align(Alignment.CenterHorizontally),
|
||||
text = stringRes(R.string.action__select_files),
|
||||
)
|
||||
}
|
||||
|
||||
val result = importResult
|
||||
when {
|
||||
@@ -228,8 +231,8 @@ fun ExtensionImportScreen(type: ExtensionImportScreenType, initUuid: String?) =
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
text = stringRes(R.string.ext__import__error_unexpected_exception),
|
||||
style = MaterialTheme.typography.body2,
|
||||
color = MaterialTheme.colors.error,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
SelectionContainer {
|
||||
Text(
|
||||
@@ -237,8 +240,8 @@ fun ExtensionImportScreen(type: ExtensionImportScreenType, initUuid: String?) =
|
||||
.florisHorizontalScroll()
|
||||
.padding(horizontal = 16.dp),
|
||||
text = result.exceptionOrNull()?.stackTraceToString() ?: "null",
|
||||
style = MaterialTheme.typography.body2,
|
||||
color = MaterialTheme.colors.error,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
@@ -266,20 +269,20 @@ private fun FileInfoView(
|
||||
Row {
|
||||
Text(
|
||||
text = Formatter.formatShortFileSize(LocalContext.current, fileInfo.size),
|
||||
style = MaterialTheme.typography.body2,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = grayColor,
|
||||
)
|
||||
if (ext != null) {
|
||||
FlorisBulletSpacer()
|
||||
Text(
|
||||
text = ext.meta.id,
|
||||
style = MaterialTheme.typography.body2,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = grayColor,
|
||||
)
|
||||
FlorisBulletSpacer()
|
||||
Text(
|
||||
text = ext.meta.version,
|
||||
style = MaterialTheme.typography.body2,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = grayColor,
|
||||
)
|
||||
}
|
||||
@@ -288,12 +291,12 @@ private fun FileInfoView(
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = ext.meta.title,
|
||||
style = MaterialTheme.typography.body2,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
ext.meta.description?.let { description ->
|
||||
Text(
|
||||
text = description,
|
||||
style = MaterialTheme.typography.body2,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
@@ -303,13 +306,13 @@ private fun FileInfoView(
|
||||
}
|
||||
Text(
|
||||
text = stringRes(R.string.ext__meta__maintainers_by, "maintainers" to maintainers),
|
||||
style = MaterialTheme.typography.body2,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
for (component in ext.components()) {
|
||||
Text(
|
||||
text = component.id,
|
||||
style = MaterialTheme.typography.body2,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -318,16 +321,16 @@ private fun FileInfoView(
|
||||
.fillMaxWidth()
|
||||
.height(19.dp)
|
||||
.padding(top = 10.dp, bottom = 8.dp)
|
||||
.background(MaterialTheme.colors.error.copy(alpha = 0.56f)))
|
||||
.background(MaterialTheme.colorScheme.error.copy(alpha = 0.56f)))
|
||||
Text(
|
||||
text = stringRes(R.string.ext__import__file_skip),
|
||||
style = MaterialTheme.typography.body2,
|
||||
color = MaterialTheme.colors.error,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
Text(
|
||||
text = stringRes(fileInfo.skipReason),
|
||||
style = MaterialTheme.typography.body2,
|
||||
color = MaterialTheme.colors.error,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -17,13 +17,11 @@
|
||||
package dev.patrickgold.florisboard.app.ext
|
||||
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisChip
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun ExtensionKeywordChip(
|
||||
keyword: String,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2024 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,84 +16,135 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.ext
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||
import androidx.compose.material3.FloatingActionButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
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.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.stringRes
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
|
||||
enum class ExtensionListScreenType(
|
||||
val id: String,
|
||||
@StringRes val titleResId: Int,
|
||||
val getExtensionIndex: (ExtensionManager) -> ExtensionManager.ExtensionIndex<*>,
|
||||
val launchExtensionCreate: ((NavController) -> Unit)?,
|
||||
) {
|
||||
EXT_THEME(
|
||||
id = "ext-theme",
|
||||
titleResId = R.string.ext__list__ext_theme,
|
||||
getExtensionIndex = { it.themes },
|
||||
launchExtensionCreate = { it.navigate(Routes.Ext.Edit("null", ThemeExtension.SERIAL_TYPE)) },
|
||||
),
|
||||
EXT_KEYBOARD(
|
||||
id = "ext-keyboard",
|
||||
titleResId = R.string.ext__list__ext_keyboard,
|
||||
getExtensionIndex = { it.keyboardExtensions },
|
||||
launchExtensionCreate = null,//{ it.navigate(Routes.Ext.Edit("null", KeyboardExtension.SERIAL_TYPE)) },
|
||||
),
|
||||
EXT_LANGUAGEPACK(
|
||||
id = "ext-languagepack",
|
||||
titleResId = R.string.ext__list__ext_languagepack,
|
||||
getExtensionIndex = { it.languagePacks },
|
||||
launchExtensionCreate = null,//{ it.navigate(Routes.Ext.Edit("null", LanguagePackExtension.SERIAL_TYPE)) },
|
||||
);
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ExtensionListScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.about__title)
|
||||
fun ExtensionListScreen(type: ExtensionListScreenType, showUpdate: Boolean) = FlorisScreen {
|
||||
title = stringRes(type.titleResId)
|
||||
previewFieldVisible = false
|
||||
|
||||
/*val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val extensionManager = ExtensionManager.def
|
||||
val navController = LocalNavController.current
|
||||
val extensionManager by context.extensionManager()
|
||||
val extensionIndex by type.getExtensionIndex(extensionManager).observeAsNonNullState()
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Top,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 24.dp, bottom = 32.dp)
|
||||
) {
|
||||
FlorisAppIcon()
|
||||
Text(
|
||||
text = stringRes(R.string.floris_app_name),
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
)
|
||||
}
|
||||
Preference(
|
||||
icon = R.drawable.ic_info,
|
||||
title = stringRes(R.string.about__version__title),
|
||||
summary = appVersion,
|
||||
onClick = {
|
||||
try {
|
||||
val isImeSelected = InputMethodUtils.checkIsFlorisboardSelected(context)
|
||||
if (isImeSelected) {
|
||||
FlorisClipboardManager.getInstance().addNewPlaintext(appVersion)
|
||||
} else {
|
||||
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText("Florisboard version", appVersion)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
content {
|
||||
if (showUpdate) {
|
||||
UpdateBox(extensionIndex = extensionIndex)
|
||||
}
|
||||
for (ext in extensionIndex) {
|
||||
FlorisOutlinedBox(
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
title = ext.meta.title,
|
||||
subtitle = ext.meta.id,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||
text = ext.meta.description ?: "",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 6.dp),
|
||||
) {
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
navController.navigate(Routes.Ext.View(ext.meta.id))
|
||||
},
|
||||
icon = Icons.Outlined.Info,
|
||||
text = stringRes(id = R.string.ext__list__view_details),//stringRes(R.string.action__add),
|
||||
colors = ButtonDefaults.textButtonColors(),
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
navController.navigate(Routes.Ext.Edit(ext.meta.id))
|
||||
},
|
||||
icon = Icons.Default.Edit,
|
||||
text = stringRes(R.string.action__edit),
|
||||
enabled = extensionManager.canDelete(ext),
|
||||
)
|
||||
}
|
||||
Toast.makeText(context, R.string.about__version_copied__title, Toast.LENGTH_SHORT).show()
|
||||
} catch (e: Throwable) {
|
||||
Toast.makeText(
|
||||
context, context.getString(R.string.about__version_copied__error, e.message), Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
},
|
||||
)
|
||||
Preference(
|
||||
icon = R.drawable.ic_history,
|
||||
title = stringRes(R.string.about__changelog__title),
|
||||
summary = stringRes(R.string.about__changelog__summary),
|
||||
onClick = { launchUrl(context, R.string.florisboard__changelog_url, arrayOf(BuildConfig.VERSION_NAME)) },
|
||||
)
|
||||
Preference(
|
||||
icon = R.drawable.ic_code,
|
||||
title = stringRes(R.string.about__repository__title),
|
||||
summary = stringRes(R.string.about__repository__summary),
|
||||
onClick = { launchUrl(context, R.string.florisboard__repo_url) },
|
||||
)
|
||||
Preference(
|
||||
icon = R.drawable.ic_policy,
|
||||
title = stringRes(R.string.about__privacy_policy__title),
|
||||
summary = stringRes(R.string.about__privacy_policy__summary),
|
||||
onClick = { launchUrl(context, R.string.florisboard__privacy_policy_url) },
|
||||
)
|
||||
Preference(
|
||||
icon = R.drawable.ic_description,
|
||||
title = stringRes(R.string.about__project_license__title),
|
||||
summary = stringRes(R.string.about__project_license__summary, "license_name" to "Apache 2.0"),
|
||||
onClick = { navController.navigate(Routes.Settings.ProjectLicense) },
|
||||
)
|
||||
Preference(
|
||||
icon = R.drawable.ic_description,
|
||||
title = stringRes(id = R.string.about__third_party_licenses__title),
|
||||
summary = stringRes(id = R.string.about__third_party_licenses__summary),
|
||||
onClick = { navController.navigate(Routes.Settings.ThirdPartyLicenses) },
|
||||
)*/
|
||||
}
|
||||
}
|
||||
|
||||
if (type.launchExtensionCreate != null) {
|
||||
floatingActionButton {
|
||||
ExtendedFloatingActionButton(
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Add,
|
||||
contentDescription = stringRes(id = R.string.ext__editor__title_create_any),
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = stringRes(id = R.string.ext__editor__title_create_any),
|
||||
)
|
||||
},
|
||||
shape = FloatingActionButtonDefaults.extendedFabShape,
|
||||
onClick = { type.launchExtensionCreate.invoke(navController) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,8 @@ package dev.patrickgold.florisboard.app.ext
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Link
|
||||
import androidx.compose.material.icons.filled.Mail
|
||||
import androidx.compose.material.icons.outlined.Mail
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -32,13 +30,11 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.android.launchUrl
|
||||
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
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun ExtensionMaintainerChip(
|
||||
maintainer: ExtensionMaintainer,
|
||||
|
||||
@@ -18,7 +18,7 @@ package dev.patrickgold.florisboard.app.ext
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@@ -27,13 +27,13 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -42,7 +42,6 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
@@ -52,7 +51,7 @@ 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 dev.patrickgold.florisboard.lib.android.showLongToast
|
||||
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
|
||||
@@ -150,7 +149,7 @@ private fun ViewScreen(ext: Extension) = FlorisScreen {
|
||||
icon = Icons.Default.Delete,
|
||||
text = stringRes(R.string.action__delete),
|
||||
colors = ButtonDefaults.outlinedButtonColors(
|
||||
contentColor = MaterialTheme.colors.error,
|
||||
contentColor = MaterialTheme.colorScheme.error,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -225,7 +224,7 @@ private fun ExtensionMetaRowSimpleText(
|
||||
content: @Composable RowScope.() -> Unit,
|
||||
) {
|
||||
if (showDividerAbove) {
|
||||
Divider()
|
||||
HorizontalDivider()
|
||||
}
|
||||
Row(
|
||||
modifier = modifier
|
||||
@@ -247,7 +246,7 @@ private fun ExtensionMetaRowScrollableChips(
|
||||
content: @Composable RowScope.() -> Unit,
|
||||
) {
|
||||
if (showDividerAbove) {
|
||||
Divider()
|
||||
HorizontalDivider()
|
||||
}
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
|
||||
@@ -18,14 +18,13 @@ package dev.patrickgold.florisboard.app.settings
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Adb
|
||||
import androidx.compose.material.icons.automirrored.outlined.Assignment
|
||||
import androidx.compose.material.icons.filled.Extension
|
||||
import androidx.compose.material.icons.filled.Gesture
|
||||
import androidx.compose.material.icons.filled.Language
|
||||
import androidx.compose.material.icons.filled.LibraryBooks
|
||||
import androidx.compose.material.icons.filled.SentimentSatisfiedAlt
|
||||
import androidx.compose.material.icons.filled.SmartButton
|
||||
import androidx.compose.material.icons.filled.Spellcheck
|
||||
import androidx.compose.material.icons.outlined.Assignment
|
||||
import androidx.compose.material.icons.outlined.Build
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material.icons.outlined.Keyboard
|
||||
@@ -131,18 +130,13 @@ fun HomeScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.settings__typing__title),
|
||||
onClick = { navController.navigate(Routes.Settings.Typing) },
|
||||
)
|
||||
Preference(
|
||||
icon = Icons.Default.LibraryBooks,
|
||||
title = stringRes(R.string.settings__dictionary__title),
|
||||
onClick = { navController.navigate(Routes.Settings.Dictionary) },
|
||||
)
|
||||
Preference(
|
||||
icon = Icons.Default.Gesture,
|
||||
title = stringRes(R.string.settings__gestures__title),
|
||||
onClick = { navController.navigate(Routes.Settings.Gestures) },
|
||||
)
|
||||
Preference(
|
||||
icon = Icons.Outlined.Assignment,
|
||||
icon = Icons.AutoMirrored.Outlined.Assignment,
|
||||
title = stringRes(R.string.settings__clipboard__title),
|
||||
onClick = { navController.navigate(Routes.Settings.Clipboard) },
|
||||
)
|
||||
@@ -152,9 +146,9 @@ fun HomeScreen() = FlorisScreen {
|
||||
onClick = { navController.navigate(Routes.Settings.Media) },
|
||||
)
|
||||
Preference(
|
||||
icon = Icons.Default.Adb,
|
||||
title = stringRes(R.string.devtools__title),
|
||||
onClick = { navController.navigate(Routes.Devtools.Home) },
|
||||
icon = Icons.Default.Extension,
|
||||
title = stringRes(R.string.ext__home__title),
|
||||
onClick = { navController.navigate(Routes.Ext.Home) },
|
||||
)
|
||||
Preference(
|
||||
icon = Icons.Outlined.Build,
|
||||
|
||||
@@ -22,13 +22,13 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Code
|
||||
import androidx.compose.material.icons.filled.History
|
||||
import androidx.compose.material.icons.outlined.Description
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material.icons.outlined.Policy
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -41,8 +41,8 @@ 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.android.launchUrl
|
||||
import dev.patrickgold.florisboard.lib.android.stringRes
|
||||
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
|
||||
|
||||
@@ -18,7 +18,7 @@ package dev.patrickgold.florisboard.app.settings.about
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -28,12 +28,12 @@ import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.sp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.assetManager
|
||||
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
|
||||
|
||||
@Composable
|
||||
fun ProjectLicenseScreen() = FlorisScreen {
|
||||
@@ -41,7 +41,6 @@ fun ProjectLicenseScreen() = FlorisScreen {
|
||||
scrollable = false
|
||||
|
||||
val context = LocalContext.current
|
||||
val assetManager by context.assetManager()
|
||||
|
||||
content {
|
||||
// Forcing LTR because the Apache 2.0 License shipped and displayed
|
||||
@@ -54,8 +53,8 @@ fun ProjectLicenseScreen() = FlorisScreen {
|
||||
.florisVerticalScroll()
|
||||
.florisHorizontalScroll(),
|
||||
) {
|
||||
val licenseText = assetManager.loadTextAsset(
|
||||
FlorisRef.assets("license/project_license.txt")
|
||||
val licenseText = FlorisRef.assets("license/project_license.txt").loadTextAsset(
|
||||
context
|
||||
).getOrElse {
|
||||
stringRes(R.string.about__project_license__error_license_text_failed, "error_message" to (it.message ?: ""))
|
||||
}
|
||||
|
||||
@@ -18,9 +18,11 @@ package dev.patrickgold.florisboard.app.settings.about
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
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.LibrariesContainer
|
||||
import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
|
||||
import com.mikepenz.aboutlibraries.ui.compose.m3.LibraryDefaults
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
|
||||
@@ -39,6 +41,13 @@ fun ThirdPartyLicensesScreen() = FlorisScreen {
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.florisScrollbar(lazyListState, isVertical = true),
|
||||
colors = LibraryDefaults.libraryColors(
|
||||
backgroundColor = MaterialTheme.colorScheme.background,
|
||||
badgeBackgroundColor = MaterialTheme.colorScheme.primaryContainer,
|
||||
badgeContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onBackground,
|
||||
dialogConfirmButtonColor = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
lazyListState = lazyListState,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
package dev.patrickgold.florisboard.app.settings.advanced
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Adb
|
||||
import androidx.compose.material.icons.filled.Archive
|
||||
import androidx.compose.material.icons.filled.FormatPaint
|
||||
import androidx.compose.material.icons.filled.Language
|
||||
import androidx.compose.material.icons.filled.Palette
|
||||
import androidx.compose.material.icons.filled.Preview
|
||||
@@ -28,10 +30,11 @@ import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.AppTheme
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
|
||||
import dev.patrickgold.florisboard.ime.keyboard.IncognitoMode
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import dev.patrickgold.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
@@ -54,27 +57,14 @@ fun AdvancedScreen() = FlorisScreen {
|
||||
prefs.advanced.settingsTheme,
|
||||
icon = Icons.Default.Palette,
|
||||
title = stringRes(R.string.pref__advanced__settings_theme__label),
|
||||
entries = listPrefEntries {
|
||||
entry(
|
||||
key = AppTheme.AUTO,
|
||||
label = stringRes(R.string.settings__system_default),
|
||||
)
|
||||
entry(
|
||||
key = AppTheme.AUTO_AMOLED,
|
||||
label = stringRes(R.string.pref__advanced__settings_theme__auto_amoled),
|
||||
)
|
||||
entry(
|
||||
key = AppTheme.LIGHT,
|
||||
label = stringRes(R.string.pref__advanced__settings_theme__light),
|
||||
)
|
||||
entry(
|
||||
key = AppTheme.DARK,
|
||||
label = stringRes(R.string.pref__advanced__settings_theme__dark),
|
||||
)
|
||||
entry(
|
||||
key = AppTheme.AMOLED_DARK,
|
||||
label = stringRes(R.string.pref__advanced__settings_theme__amoled_dark),
|
||||
)
|
||||
entries = enumDisplayEntriesOf(AppTheme::class),
|
||||
)
|
||||
SwitchPreference(
|
||||
pref = prefs.advanced.useMaterialYou,
|
||||
icon = Icons.Default.FormatPaint,
|
||||
title = stringRes(R.string.pref__advanced__settings_material_you__label),
|
||||
visibleIf = {
|
||||
AndroidVersion.ATLEAST_API31_S
|
||||
},
|
||||
)
|
||||
ListPreference(
|
||||
@@ -155,7 +145,12 @@ fun AdvancedScreen() = FlorisScreen {
|
||||
prefs.advanced.incognitoMode,
|
||||
icon = vectorResource(id = R.drawable.ic_incognito),
|
||||
title = stringRes(R.string.pref__advanced__incognito_mode__label),
|
||||
entries = IncognitoMode.listEntries(),
|
||||
entries = enumDisplayEntriesOf(IncognitoMode::class),
|
||||
)
|
||||
Preference(
|
||||
icon = Icons.Default.Adb,
|
||||
title = stringRes(R.string.devtools__title),
|
||||
onClick = { navController.navigate(Routes.Devtools.Home) },
|
||||
)
|
||||
|
||||
PreferenceGroup(title = stringRes(R.string.backup_and_restore__title)) {
|
||||
|
||||
@@ -16,26 +16,36 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.settings.advanced
|
||||
|
||||
import android.content.ContentUris
|
||||
import android.content.Intent
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.material.Checkbox
|
||||
import androidx.compose.material.RadioButton
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.TriStateCheckbox
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.state.ToggleableState
|
||||
import androidx.compose.ui.unit.dp
|
||||
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.LocalNavController
|
||||
import dev.patrickgold.florisboard.cacheManager
|
||||
import dev.patrickgold.florisboard.lib.android.showLongToast
|
||||
import dev.patrickgold.florisboard.lib.android.writeFromFile
|
||||
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
|
||||
@@ -47,17 +57,22 @@ 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.florisboard.lib.io.subDir
|
||||
import dev.patrickgold.florisboard.lib.io.subFile
|
||||
import dev.patrickgold.florisboard.lib.io.writeJson
|
||||
import dev.patrickgold.jetpref.datastore.jetprefDatastoreDir
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.writeFromFile
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import org.florisboard.lib.kotlin.io.writeJson
|
||||
|
||||
object Backup {
|
||||
const val FILE_PROVIDER_AUTHORITY = "${BuildConfig.APPLICATION_ID}.provider.file"
|
||||
const val METADATA_JSON_NAME = "backup_metadata.json"
|
||||
const val CLIPBOARD_TEXT_ITEMS_JSON_NAME = "clipboard_text_items.json"
|
||||
const val CLIPBOARD_IMAGES_JSON_NAME = "clipboard_images.json"
|
||||
const val CLIPBOARD_VIDEO_JSON_NAME = "clipboard_video.json"
|
||||
|
||||
fun defaultFileName(metadata: Metadata): String {
|
||||
return "backup_${metadata.packageName}_${metadata.versionCode}_${metadata.timestamp}.zip"
|
||||
@@ -72,9 +87,34 @@ object Backup {
|
||||
var jetprefDatastore by mutableStateOf(true)
|
||||
var imeKeyboard by mutableStateOf(true)
|
||||
var imeTheme by mutableStateOf(true)
|
||||
var clipboardTextItems by mutableStateOf(false)
|
||||
var clipboardImageItems by mutableStateOf(false)
|
||||
var clipboardVideoItems by mutableStateOf(false)
|
||||
|
||||
private var _clipboardData: MutableState<ToggleableState> = mutableStateOf(ToggleableState.Off)
|
||||
val clipboardData: State<ToggleableState> = _clipboardData
|
||||
|
||||
fun updateCheckboxState() {
|
||||
val newValue = if (
|
||||
!clipboardVideoItems && !clipboardImageItems && !clipboardTextItems
|
||||
) {
|
||||
ToggleableState.Off
|
||||
} else if (
|
||||
clipboardVideoItems && clipboardImageItems && clipboardTextItems
|
||||
) {
|
||||
ToggleableState.On
|
||||
} else {
|
||||
ToggleableState.Indeterminate
|
||||
}
|
||||
_clipboardData.value = newValue
|
||||
}
|
||||
|
||||
fun provideClipboardItems(): Boolean {
|
||||
return clipboardTextItems || clipboardImageItems || clipboardVideoItems
|
||||
}
|
||||
|
||||
fun atLeastOneSelected(): Boolean {
|
||||
return jetprefDatastore || imeKeyboard || imeTheme
|
||||
return jetprefDatastore || imeKeyboard || imeTheme || clipboardTextItems || clipboardImageItems || clipboardVideoItems
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +142,7 @@ fun BackupScreen() = FlorisScreen {
|
||||
var backupWorkspace: CacheManager.BackupAndRestoreWorkspace? = null
|
||||
|
||||
val backUpToFileSystemLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.CreateDocument(),
|
||||
contract = ActivityResultContracts.CreateDocument("application/zip"),
|
||||
onResult = { uri ->
|
||||
if (uri == null) {
|
||||
// User can modify checkboxes between cancellation and second
|
||||
@@ -143,6 +183,36 @@ fun BackupScreen() = FlorisScreen {
|
||||
dir.copyRecursively(workspaceFilesDir.subDir(ExtensionManager.IME_THEME_PATH))
|
||||
}
|
||||
}
|
||||
|
||||
if (backupFilesSelector.provideClipboardItems()) {
|
||||
val clipboardHistory = context.clipboardManager().value.history().all
|
||||
val clipboardFilesDir = workspace.inputDir.subDir("clipboard")
|
||||
clipboardFilesDir.mkdir()
|
||||
if (backupFilesSelector.clipboardTextItems) {
|
||||
clipboardFilesDir.subFile(Backup.CLIPBOARD_TEXT_ITEMS_JSON_NAME)
|
||||
.writeJson(clipboardHistory.filter { it.type == ItemType.TEXT })
|
||||
}
|
||||
if (backupFilesSelector.clipboardImageItems) {
|
||||
clipboardFilesDir.subFile(Backup.CLIPBOARD_IMAGES_JSON_NAME)
|
||||
.writeJson(clipboardHistory.filter { it.type == ItemType.IMAGE })
|
||||
for (item in clipboardHistory.filter { it.type == ItemType.IMAGE }) {
|
||||
val id = ContentUris.parseId(item.uri!!)
|
||||
ClipboardFileStorage.getFileForId(context, id).copyTo(
|
||||
clipboardFilesDir.subFile("${ClipboardFileStorage.CLIPBOARD_FILES_PATH}/$id")
|
||||
)
|
||||
}
|
||||
}
|
||||
if (backupFilesSelector.clipboardVideoItems) {
|
||||
clipboardFilesDir.subFile(Backup.CLIPBOARD_VIDEO_JSON_NAME)
|
||||
.writeJson(clipboardHistory.filter { it.type == ItemType.VIDEO })
|
||||
for (item in clipboardHistory.filter { it.type == ItemType.VIDEO }) {
|
||||
val id = ContentUris.parseId(item.uri!!)
|
||||
ClipboardFileStorage.getFileForId(context, id).copyTo(
|
||||
clipboardFilesDir.subFile("${ClipboardFileStorage.CLIPBOARD_FILES_PATH}/$id")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
workspace.metadata = Backup.Metadata(
|
||||
packageName = BuildConfig.APPLICATION_ID,
|
||||
versionCode = BuildConfig.VERSION_CODE,
|
||||
@@ -164,8 +234,10 @@ fun BackupScreen() = FlorisScreen {
|
||||
Backup.Destination.FILE_SYS -> {
|
||||
backUpToFileSystemLauncher.launch(backupWorkspace!!.zipFile.name)
|
||||
}
|
||||
|
||||
Backup.Destination.SHARE_INTENT -> {
|
||||
val uri = FileProvider.getUriForFile(context, Backup.FILE_PROVIDER_AUTHORITY, backupWorkspace!!.zipFile)
|
||||
val uri =
|
||||
FileProvider.getUriForFile(context, Backup.FILE_PROVIDER_AUTHORITY, backupWorkspace!!.zipFile)
|
||||
val shareIntent = ShareCompat.IntentBuilder(context)
|
||||
.setStream(uri)
|
||||
.setType(FileRegistry.BackupArchive.mediaType)
|
||||
@@ -253,6 +325,56 @@ internal fun BackupFilesSelector(
|
||||
checked = filesSelector.imeTheme,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_ime_theme),
|
||||
)
|
||||
|
||||
TriStateCheckboxListItem(
|
||||
onClick = {
|
||||
if (
|
||||
filesSelector.clipboardData.value == ToggleableState.Off ||
|
||||
filesSelector.clipboardData.value == ToggleableState.Indeterminate
|
||||
) {
|
||||
filesSelector.clipboardImageItems = true
|
||||
filesSelector.clipboardVideoItems = true
|
||||
filesSelector.clipboardTextItems = true
|
||||
} else {
|
||||
filesSelector.clipboardImageItems = false
|
||||
filesSelector.clipboardVideoItems = false
|
||||
filesSelector.clipboardTextItems = false
|
||||
}
|
||||
filesSelector.updateCheckboxState()
|
||||
},
|
||||
state = filesSelector.clipboardData.value,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_clipboard_history),
|
||||
)
|
||||
|
||||
|
||||
CheckboxListItem(
|
||||
onClick = {
|
||||
filesSelector.clipboardTextItems = !filesSelector.clipboardTextItems
|
||||
filesSelector.updateCheckboxState()
|
||||
},
|
||||
checked = filesSelector.clipboardTextItems,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_clipboard_history__clipboard_text_items),
|
||||
isSecondaryListItem = true,
|
||||
)
|
||||
CheckboxListItem(
|
||||
onClick = {
|
||||
filesSelector.clipboardImageItems = !filesSelector.clipboardImageItems
|
||||
filesSelector.updateCheckboxState()
|
||||
},
|
||||
checked = filesSelector.clipboardImageItems,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_clipboard_history__clipboard_image_items),
|
||||
isSecondaryListItem = true,
|
||||
)
|
||||
CheckboxListItem(
|
||||
onClick = {
|
||||
filesSelector.clipboardVideoItems = !filesSelector.clipboardVideoItems
|
||||
filesSelector.updateCheckboxState()
|
||||
},
|
||||
checked = filesSelector.clipboardVideoItems,
|
||||
text = stringRes(R.string.backup_and_restore__back_up__files_clipboard_history__clipboard_video_items),
|
||||
isSecondaryListItem = true,
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,14 +383,44 @@ internal fun CheckboxListItem(
|
||||
onClick: () -> Unit,
|
||||
checked: Boolean,
|
||||
text: String,
|
||||
isSecondaryListItem: Boolean = false
|
||||
) {
|
||||
JetPrefListItem(
|
||||
modifier = Modifier.rippleClickable(onClick = onClick),
|
||||
icon = {
|
||||
Checkbox(
|
||||
checked = checked,
|
||||
onCheckedChange = null,
|
||||
)
|
||||
Row {
|
||||
if (isSecondaryListItem) {
|
||||
Spacer(modifier = Modifier.width(40.dp))
|
||||
}
|
||||
Checkbox(
|
||||
checked = checked,
|
||||
onCheckedChange = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
text = text,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun TriStateCheckboxListItem(
|
||||
onClick: () -> Unit,
|
||||
state: ToggleableState,
|
||||
text: String,
|
||||
isSecondaryListItem: Boolean = false,
|
||||
) {
|
||||
JetPrefListItem(
|
||||
modifier = Modifier.rippleClickable(onClick = onClick),
|
||||
icon = {
|
||||
Row {
|
||||
if (isSecondaryListItem) {
|
||||
Spacer(modifier = Modifier.width(40.dp))
|
||||
}
|
||||
TriStateCheckbox(
|
||||
state = state,
|
||||
onClick = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
text = text,
|
||||
)
|
||||
|
||||
@@ -24,14 +24,13 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.LocalContentAlpha
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Code
|
||||
import androidx.compose.material.icons.filled.Schedule
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -47,11 +46,13 @@ import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.cacheManager
|
||||
import dev.patrickgold.florisboard.lib.android.readToFile
|
||||
import dev.patrickgold.florisboard.lib.android.showLongToast
|
||||
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.CardDefaults
|
||||
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
|
||||
@@ -59,15 +60,17 @@ import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
|
||||
import dev.patrickgold.florisboard.lib.io.ZipUtils
|
||||
import dev.patrickgold.florisboard.lib.io.deleteContentsRecursively
|
||||
import dev.patrickgold.florisboard.lib.io.readJson
|
||||
import dev.patrickgold.florisboard.lib.io.subDir
|
||||
import dev.patrickgold.florisboard.lib.io.subFile
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.readToFile
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
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.*
|
||||
@@ -135,7 +138,10 @@ fun RestoreScreen() = FlorisScreen {
|
||||
}
|
||||
restoreWorkspace = workspace
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(R.string.backup_and_restore__restore__failure, "error_message" to error.localizedMessage)
|
||||
context.showLongToast(
|
||||
R.string.backup_and_restore__restore__failure,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -173,6 +179,61 @@ fun RestoreScreen() = FlorisScreen {
|
||||
srcDir.copyRecursively(dstDir, overwrite = true)
|
||||
}
|
||||
}
|
||||
val clipboardManager = context.clipboardManager().value
|
||||
if (shouldReset) {
|
||||
clipboardManager.clearFullHistory()
|
||||
ClipboardFileStorage.resetClipboardFileStorage(context)
|
||||
}
|
||||
|
||||
if (restoreFilesSelector.provideClipboardItems()) {
|
||||
val clipboardFilesDir = workspace.outputDir.subDir("clipboard")
|
||||
|
||||
if (restoreFilesSelector.clipboardTextItems) {
|
||||
val clipboardItems = clipboardFilesDir.subFile(Backup.CLIPBOARD_TEXT_ITEMS_JSON_NAME)
|
||||
if (clipboardItems.exists()) {
|
||||
val clipboardItemsList = clipboardItems.readJson<List<ClipboardItem>>()
|
||||
clipboardManager.restoreHistory(items = clipboardItemsList.filter { it.type == ItemType.TEXT })
|
||||
}
|
||||
}
|
||||
if (restoreFilesSelector.clipboardImageItems) {
|
||||
val clipboardItems = clipboardFilesDir.subFile(Backup.CLIPBOARD_IMAGES_JSON_NAME)
|
||||
if (clipboardItems.exists()) {
|
||||
val clipboardItemsList = clipboardItems.readJson<List<ClipboardItem>>()
|
||||
for (item in clipboardItemsList.filter { it.type == ItemType.IMAGE }) {
|
||||
ClipboardFileStorage.insertFileFromBackupIfNotExisting(
|
||||
context,
|
||||
clipboardFilesDir.subFile(
|
||||
relPath = "${ClipboardFileStorage.CLIPBOARD_FILES_PATH}/${
|
||||
item.uri!!.path!!.split(
|
||||
'/'
|
||||
).last()
|
||||
}"
|
||||
)
|
||||
)
|
||||
}
|
||||
clipboardManager.restoreHistory(items = clipboardItemsList.filter { it.type == ItemType.IMAGE })
|
||||
}
|
||||
}
|
||||
if (restoreFilesSelector.clipboardVideoItems) {
|
||||
val clipboardItems = clipboardFilesDir.subFile(Backup.CLIPBOARD_VIDEO_JSON_NAME)
|
||||
if (clipboardItems.exists()) {
|
||||
val clipboardItemsList = clipboardItems.readJson<List<ClipboardItem>>()
|
||||
for (item in clipboardItemsList.filter { it.type == ItemType.VIDEO }) {
|
||||
ClipboardFileStorage.insertFileFromBackupIfNotExisting(
|
||||
context,
|
||||
clipboardFilesDir.subFile(
|
||||
relPath = "${ClipboardFileStorage.CLIPBOARD_FILES_PATH}/${
|
||||
item.uri!!.path!!.split(
|
||||
'/'
|
||||
).last()
|
||||
}"
|
||||
)
|
||||
)
|
||||
}
|
||||
clipboardManager.restoreHistory(items = clipboardItemsList.filter { it.type == ItemType.VIDEO })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bottomBar {
|
||||
@@ -181,7 +242,7 @@ fun RestoreScreen() = FlorisScreen {
|
||||
ButtonBarTextButton(
|
||||
onClick = {
|
||||
restoreWorkspace?.close()
|
||||
navController.popBackStack()
|
||||
navController.navigateUp()
|
||||
},
|
||||
text = stringRes(R.string.action__cancel),
|
||||
)
|
||||
@@ -191,9 +252,13 @@ fun RestoreScreen() = FlorisScreen {
|
||||
try {
|
||||
performRestore()
|
||||
context.showLongToast(R.string.backup_and_restore__restore__success)
|
||||
navController.popBackStack()
|
||||
navController.navigateUp()
|
||||
} catch (e: Throwable) {
|
||||
context.showLongToast(R.string.backup_and_restore__restore__failure, "error_message" to e.localizedMessage)
|
||||
e.printStackTrace()
|
||||
context.showLongToast(
|
||||
R.string.backup_and_restore__restore__failure,
|
||||
"error_message" to e.localizedMessage,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -228,7 +293,10 @@ fun RestoreScreen() = FlorisScreen {
|
||||
runCatching {
|
||||
restoreDataFromFileSystemLauncher.launch("*/*")
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(R.string.backup_and_restore__restore__failure, "error_message" to error.localizedMessage)
|
||||
context.showLongToast(
|
||||
R.string.backup_and_restore__restore__failure,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
@@ -250,15 +318,15 @@ fun RestoreScreen() = FlorisScreen {
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
title = stringRes(R.string.backup_and_restore__restore__metadata),
|
||||
) {
|
||||
this@content.Preference(
|
||||
Preference(
|
||||
icon = Icons.Default.Code,
|
||||
title = workspace.metadata.packageName,
|
||||
)
|
||||
this@content.Preference(
|
||||
Preference(
|
||||
icon = Icons.Outlined.Info,
|
||||
title = "${workspace.metadata.versionName} (${workspace.metadata.versionCode})",
|
||||
)
|
||||
this@content.Preference(
|
||||
Preference(
|
||||
icon = Icons.Default.Schedule,
|
||||
title = remember(workspace.metadata.timestamp) {
|
||||
val formatter = DateFormat.getDateTimeInstance()
|
||||
@@ -268,34 +336,34 @@ fun RestoreScreen() = FlorisScreen {
|
||||
},
|
||||
)
|
||||
if (workspace.restoreErrorId != null) {
|
||||
Column(modifier = Modifier.padding(CardDefaults.ContentPadding)) {
|
||||
Column(modifier = Modifier.padding(FlorisCardDefaults.ContentPadding)) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(9.dp)
|
||||
.padding(bottom = 8.dp)
|
||||
.background(MaterialTheme.colors.error.copy(alpha = 0.56f))
|
||||
.background(MaterialTheme.colorScheme.error.copy(alpha = 0.56f))
|
||||
)
|
||||
Text(
|
||||
text = stringRes(workspace.restoreErrorId!!),
|
||||
style = MaterialTheme.typography.body2,
|
||||
color = MaterialTheme.colors.error,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
} else if (workspace.restoreWarningId != null) {
|
||||
Column(modifier = Modifier.padding(CardDefaults.ContentPadding)) {
|
||||
Column(modifier = Modifier.padding(FlorisCardDefaults.ContentPadding)) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(9.dp)
|
||||
.padding(bottom = 8.dp)
|
||||
.background(LocalContentColor.current.copy(alpha = LocalContentAlpha.current))
|
||||
.background(LocalContentColor.current)
|
||||
)
|
||||
Text(
|
||||
text = stringRes(workspace.restoreWarningId!!),
|
||||
style = MaterialTheme.typography.body2,
|
||||
color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = LocalContentColor.current,
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -23,16 +23,16 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.DropdownMenu
|
||||
import androidx.compose.material.DropdownMenuItem
|
||||
import androidx.compose.material.ExtendedFloatingActionButton
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -43,7 +43,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
@@ -56,19 +55,19 @@ import dev.patrickgold.florisboard.ime.dictionary.UserDictionaryDao
|
||||
import dev.patrickgold.florisboard.ime.dictionary.UserDictionaryEntry
|
||||
import dev.patrickgold.florisboard.ime.dictionary.UserDictionaryValidation
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import dev.patrickgold.florisboard.lib.android.launchActivity
|
||||
import dev.patrickgold.florisboard.lib.android.showLongToast
|
||||
import dev.patrickgold.florisboard.lib.android.stringRes
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedTextField
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.rippleClickable
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.rememberValidationResult
|
||||
import dev.patrickgold.florisboard.lib.util.launchActivity
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.stringRes
|
||||
|
||||
private val AllLanguagesLocale = FlorisLocale.from(language = "zz")
|
||||
private val UserDictionaryEntryToAdd = UserDictionaryEntry(id = 0, "", 255, null, null)
|
||||
@@ -194,7 +193,7 @@ fun UserDictionaryScreen(type: UserDictionaryType) = FlorisScreen {
|
||||
icon = if (currentLocale != null) {
|
||||
Icons.Default.Close
|
||||
} else {
|
||||
Icons.Default.ArrowBack
|
||||
Icons.AutoMirrored.Filled.ArrowBack
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -214,14 +213,14 @@ fun UserDictionaryScreen(type: UserDictionaryType) = FlorisScreen {
|
||||
importDictionary.launch("*/*")
|
||||
expanded = false
|
||||
},
|
||||
content = { Text(text = stringRes(R.string.action__import)) },
|
||||
text = { Text(text = stringRes(R.string.action__import)) },
|
||||
)
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
exportDictionary.launch("my-personal-dictionary.clb")
|
||||
expanded = false
|
||||
},
|
||||
content = { Text(text = stringRes(R.string.action__export)) },
|
||||
text = { Text(text = stringRes(R.string.action__export)) },
|
||||
)
|
||||
if (type == UserDictionaryType.SYSTEM) {
|
||||
DropdownMenuItem(
|
||||
@@ -229,7 +228,7 @@ fun UserDictionaryScreen(type: UserDictionaryType) = FlorisScreen {
|
||||
context.launchActivity { it.action = SystemUserDictionaryUiIntentAction }
|
||||
expanded = false
|
||||
},
|
||||
content = { Text(text = stringRes(R.string.settings__udm__open_system_manager_ui)) },
|
||||
text = { Text(text = stringRes(R.string.settings__udm__open_system_manager_ui)) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisInfoCard
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
@@ -93,25 +94,25 @@ fun GesturesScreen() = FlorisScreen {
|
||||
ListPreference(
|
||||
prefs.gestures.swipeUp,
|
||||
title = stringRes(R.string.pref__gestures__swipe_up__label),
|
||||
entries = SwipeAction.generalListEntries(),
|
||||
entries = enumDisplayEntriesOf(SwipeAction::class, "general"),
|
||||
enabledIf = { prefs.glide.enabled isEqualTo false },
|
||||
)
|
||||
ListPreference(
|
||||
prefs.gestures.swipeDown,
|
||||
title = stringRes(R.string.pref__gestures__swipe_down__label),
|
||||
entries = SwipeAction.generalListEntries(),
|
||||
entries = enumDisplayEntriesOf(SwipeAction::class, "general"),
|
||||
enabledIf = { prefs.glide.enabled isEqualTo false },
|
||||
)
|
||||
ListPreference(
|
||||
prefs.gestures.swipeLeft,
|
||||
title = stringRes(R.string.pref__gestures__swipe_left__label),
|
||||
entries = SwipeAction.generalListEntries(),
|
||||
entries = enumDisplayEntriesOf(SwipeAction::class, "general"),
|
||||
enabledIf = { prefs.glide.enabled isEqualTo false },
|
||||
)
|
||||
ListPreference(
|
||||
prefs.gestures.swipeRight,
|
||||
title = stringRes(R.string.pref__gestures__swipe_right__label),
|
||||
entries = SwipeAction.generalListEntries(),
|
||||
entries = enumDisplayEntriesOf(SwipeAction::class, "general"),
|
||||
enabledIf = { prefs.glide.enabled isEqualTo false },
|
||||
)
|
||||
}
|
||||
@@ -120,22 +121,22 @@ fun GesturesScreen() = FlorisScreen {
|
||||
ListPreference(
|
||||
prefs.gestures.spaceBarSwipeUp,
|
||||
title = stringRes(R.string.pref__gestures__space_bar_swipe_up__label),
|
||||
entries = SwipeAction.generalListEntries(),
|
||||
entries = enumDisplayEntriesOf(SwipeAction::class, "general"),
|
||||
)
|
||||
ListPreference(
|
||||
prefs.gestures.spaceBarSwipeLeft,
|
||||
title = stringRes(R.string.pref__gestures__space_bar_swipe_left__label),
|
||||
entries = SwipeAction.generalListEntries(),
|
||||
entries = enumDisplayEntriesOf(SwipeAction::class, "general"),
|
||||
)
|
||||
ListPreference(
|
||||
prefs.gestures.spaceBarSwipeRight,
|
||||
title = stringRes(R.string.pref__gestures__space_bar_swipe_right__label),
|
||||
entries = SwipeAction.generalListEntries(),
|
||||
entries = enumDisplayEntriesOf(SwipeAction::class, "general"),
|
||||
)
|
||||
ListPreference(
|
||||
prefs.gestures.spaceBarLongPress,
|
||||
title = stringRes(R.string.pref__gestures__space_bar_long_press__label),
|
||||
entries = SwipeAction.generalListEntries(),
|
||||
entries = enumDisplayEntriesOf(SwipeAction::class, "general"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -143,12 +144,12 @@ fun GesturesScreen() = FlorisScreen {
|
||||
ListPreference(
|
||||
prefs.gestures.deleteKeySwipeLeft,
|
||||
title = stringRes(R.string.pref__gestures__delete_key_swipe_left__label),
|
||||
entries = SwipeAction.deleteSwipeListEntries(),
|
||||
entries = enumDisplayEntriesOf(SwipeAction::class, "deleteSwipe"),
|
||||
)
|
||||
ListPreference(
|
||||
prefs.gestures.deleteKeyLongPress,
|
||||
title = stringRes(R.string.pref__gestures__delete_key_long_press__label),
|
||||
entries = SwipeAction.deleteLongPressListEntries(),
|
||||
entries = enumDisplayEntriesOf(SwipeAction::class, "deleteLongPress"),
|
||||
)
|
||||
DialogSliderPreference(
|
||||
prefs.gestures.swipeVelocityThreshold,
|
||||
|
||||
@@ -19,11 +19,12 @@ package dev.patrickgold.florisboard.app.settings.keyboard
|
||||
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 dev.patrickgold.florisboard.lib.android.AndroidVersion
|
||||
import dev.patrickgold.florisboard.lib.android.systemVibratorOrNull
|
||||
import dev.patrickgold.florisboard.lib.android.vibrate
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.systemVibratorOrNull
|
||||
import org.florisboard.lib.android.vibrate
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference
|
||||
@@ -49,7 +50,7 @@ fun InputFeedbackScreen() = FlorisScreen {
|
||||
switchPref = prefs.inputFeedback.audioEnabled,
|
||||
title = stringRes(R.string.pref__input_feedback__audio_enabled__label),
|
||||
summarySwitchDisabled = stringRes(R.string.pref__input_feedback__audio_enabled__summary_disabled),
|
||||
entries = InputFeedbackActivationMode.audioListEntries(),
|
||||
entries = enumDisplayEntriesOf(InputFeedbackActivationMode::class, "audio"),
|
||||
)
|
||||
DialogSliderPreference(
|
||||
prefs.inputFeedback.audioVolume,
|
||||
@@ -98,13 +99,13 @@ fun InputFeedbackScreen() = FlorisScreen {
|
||||
switchPref = prefs.inputFeedback.hapticEnabled,
|
||||
title = stringRes(R.string.pref__input_feedback__haptic_enabled__label),
|
||||
summarySwitchDisabled = stringRes(R.string.pref__input_feedback__haptic_enabled__summary_disabled),
|
||||
entries = InputFeedbackActivationMode.hapticListEntries(),
|
||||
entries = enumDisplayEntriesOf(InputFeedbackActivationMode::class, "haptic")
|
||||
)
|
||||
ListPreference(
|
||||
prefs.inputFeedback.hapticVibrationMode,
|
||||
title = stringRes(R.string.pref__input_feedback__haptic_vibration_mode__label),
|
||||
enabledIf = { prefs.inputFeedback.hapticEnabled isEqualTo true },
|
||||
entries = HapticVibrationMode.listEntries(),
|
||||
entries = enumDisplayEntriesOf(HapticVibrationMode::class),
|
||||
)
|
||||
DialogSliderPreference(
|
||||
prefs.inputFeedback.hapticVibrationDuration,
|
||||
|
||||
@@ -20,10 +20,12 @@ import androidx.compose.runtime.Composable
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.input.CapitalizationBehavior
|
||||
import dev.patrickgold.florisboard.ime.keyboard.SpaceBarMode
|
||||
import dev.patrickgold.florisboard.ime.landscapeinput.LandscapeInputUiMode
|
||||
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
|
||||
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
|
||||
@@ -54,14 +56,15 @@ fun KeyboardScreen() = FlorisScreen {
|
||||
switchPref = prefs.keyboard.hintedNumberRowEnabled,
|
||||
title = stringRes(R.string.pref__keyboard__hinted_number_row_mode__label),
|
||||
summarySwitchDisabled = stringRes(R.string.state__disabled),
|
||||
entries = KeyHintMode.listEntries(),
|
||||
entries = enumDisplayEntriesOf(KeyHintMode::class),
|
||||
enabledIf = { prefs.keyboard.numberRow.isFalse() }
|
||||
)
|
||||
ListPreference(
|
||||
listPref = prefs.keyboard.hintedSymbolsMode,
|
||||
switchPref = prefs.keyboard.hintedSymbolsEnabled,
|
||||
title = stringRes(R.string.pref__keyboard__hinted_symbols_mode__label),
|
||||
summarySwitchDisabled = stringRes(R.string.state__disabled),
|
||||
entries = KeyHintMode.listEntries(),
|
||||
entries = enumDisplayEntriesOf(KeyHintMode::class),
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.keyboard.utilityKeyEnabled,
|
||||
@@ -71,18 +74,18 @@ fun KeyboardScreen() = FlorisScreen {
|
||||
ListPreference(
|
||||
prefs.keyboard.utilityKeyAction,
|
||||
title = stringRes(R.string.pref__keyboard__utility_key_action__label),
|
||||
entries = UtilityKeyAction.listEntries(),
|
||||
entries = enumDisplayEntriesOf(UtilityKeyAction::class),
|
||||
visibleIf = { prefs.keyboard.utilityKeyEnabled isEqualTo true },
|
||||
)
|
||||
ListPreference(
|
||||
prefs.keyboard.spaceBarMode,
|
||||
title = stringRes(R.string.pref__keyboard__space_bar_mode__label),
|
||||
entries = SpaceBarMode.listEntries(),
|
||||
entries = enumDisplayEntriesOf(SpaceBarMode::class),
|
||||
)
|
||||
ListPreference(
|
||||
prefs.keyboard.capitalizationBehavior,
|
||||
title = stringRes(R.string.pref__keyboard__capitalization_behavior__label),
|
||||
entries = CapitalizationBehavior.listEntries(),
|
||||
entries = enumDisplayEntriesOf(CapitalizationBehavior::class),
|
||||
)
|
||||
DialogSliderPreference(
|
||||
primaryPref = prefs.keyboard.fontSizeMultiplierPortrait,
|
||||
@@ -95,12 +98,17 @@ fun KeyboardScreen() = FlorisScreen {
|
||||
max = 150,
|
||||
stepIncrement = 5,
|
||||
)
|
||||
ListPreference(
|
||||
listPref = prefs.keyboard.incognitoDisplayMode,
|
||||
title = stringRes(R.string.pref__keyboard__incognito_indicator__label),
|
||||
entries = enumDisplayEntriesOf(IncognitoDisplayMode::class),
|
||||
)
|
||||
|
||||
PreferenceGroup(title = stringRes(R.string.pref__keyboard__group_layout__label)) {
|
||||
ListPreference(
|
||||
prefs.keyboard.oneHandedMode,
|
||||
title = stringRes(R.string.pref__keyboard__one_handed_mode__label),
|
||||
entries = OneHandedMode.listEntries(),
|
||||
entries = enumDisplayEntriesOf(OneHandedMode::class),
|
||||
)
|
||||
DialogSliderPreference(
|
||||
prefs.keyboard.oneHandedModeScaleFactor,
|
||||
@@ -114,7 +122,7 @@ fun KeyboardScreen() = FlorisScreen {
|
||||
ListPreference(
|
||||
prefs.keyboard.landscapeInputUiMode,
|
||||
title = stringRes(R.string.pref__keyboard__landscape_input_ui_mode__label),
|
||||
entries = LandscapeInputUiMode.listEntries(),
|
||||
entries = enumDisplayEntriesOf(LandscapeInputUiMode::class),
|
||||
)
|
||||
DialogSliderPreference(
|
||||
primaryPref = prefs.keyboard.heightFactorPortrait,
|
||||
|
||||
@@ -25,12 +25,12 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Input
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
@@ -47,7 +47,7 @@ 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 dev.patrickgold.florisboard.lib.android.showLongToast
|
||||
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
|
||||
@@ -112,7 +112,7 @@ fun LanguagePackManagerScreen(action: LanguagePackManagerScreenAction?) = Floris
|
||||
FlorisOutlinedBox(
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
) {
|
||||
this@content.Preference(
|
||||
Preference(
|
||||
onClick = { navController.navigate(
|
||||
Routes.Ext.Import(ExtensionImportScreenType.EXT_LANGUAGEPACK, null)
|
||||
) },
|
||||
@@ -177,7 +177,7 @@ fun LanguagePackManagerScreen(action: LanguagePackManagerScreenAction?) = Floris
|
||||
icon = Icons.Default.Delete,
|
||||
text = stringRes(R.string.action__delete),
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
contentColor = MaterialTheme.colors.error,
|
||||
contentColor = MaterialTheme.colorScheme.error,
|
||||
),
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
@@ -16,23 +16,32 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.settings.localization
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.ExtendedFloatingActionButton
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||
import androidx.compose.material3.FloatingActionButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.Saver
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.cacheManager
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
|
||||
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
|
||||
@@ -44,7 +53,20 @@ import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
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.material.ui.JetPrefAlertDialog
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
internal val SubtypeSaver = Saver<MutableState<Subtype?>, String>(
|
||||
save = {
|
||||
Json.encodeToString<Subtype?>(it.value)
|
||||
},
|
||||
restore = {
|
||||
mutableStateOf(Json.decodeFromString(it))
|
||||
},
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun LocalizationScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.settings__localization__title)
|
||||
@@ -55,7 +77,7 @@ fun LocalizationScreen() = FlorisScreen {
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val subtypeManager by context.subtypeManager()
|
||||
val cacheManager by context.cacheManager()
|
||||
var chosenSubtypeToDelete: Subtype? by rememberSaveable(saver = SubtypeSaver) { mutableStateOf(null) }
|
||||
|
||||
floatingActionButton {
|
||||
ExtendedFloatingActionButton(
|
||||
@@ -70,19 +92,18 @@ fun LocalizationScreen() = FlorisScreen {
|
||||
text = stringRes(R.string.settings__localization__subtype_add_title),
|
||||
)
|
||||
},
|
||||
shape = FloatingActionButtonDefaults.extendedFabShape,
|
||||
onClick = { navController.navigate(Routes.Settings.SubtypeAdd) },
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
content {
|
||||
ListPreference(
|
||||
prefs.localization.displayLanguageNamesIn,
|
||||
title = stringRes(R.string.settings__localization__display_language_names_in__label),
|
||||
entries = DisplayLanguageNamesIn.listEntries(),
|
||||
entries = enumDisplayEntriesOf(DisplayLanguageNamesIn::class),
|
||||
)
|
||||
Preference(
|
||||
// icon = R.drawable.ic_edit,
|
||||
title = stringRes(R.string.settings__localization__language_pack_title),
|
||||
summary = stringRes(R.string.settings__localization__language_pack_summary),
|
||||
onClick = {
|
||||
@@ -116,17 +137,50 @@ fun LocalizationScreen() = FlorisScreen {
|
||||
DisplayLanguageNamesIn.NATIVE_LOCALE -> subtype.primaryLocale.displayName(subtype.primaryLocale)
|
||||
},
|
||||
summary = summary,
|
||||
onClick = {
|
||||
navController.navigate(
|
||||
Routes.Settings.SubtypeEdit(subtype.id)
|
||||
)
|
||||
},
|
||||
modifier = Modifier.combinedClickable(
|
||||
onClick = {
|
||||
navController.navigate(
|
||||
Routes.Settings.SubtypeEdit(subtype.id)
|
||||
)
|
||||
},
|
||||
onLongClick = {
|
||||
chosenSubtypeToDelete = subtype
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//PreferenceGroup(title = stringRes(R.string.settings__localization__group_layouts__label)) {
|
||||
//}
|
||||
DeleteSubtypeConfirmationDialog(
|
||||
subtypeToDelete = chosenSubtypeToDelete,
|
||||
onDismiss = {
|
||||
chosenSubtypeToDelete = null
|
||||
},
|
||||
onConfirm = {
|
||||
chosenSubtypeToDelete?.let { subtypeManager.removeSubtype(subtypeToRemove = it) }
|
||||
chosenSubtypeToDelete = null
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DeleteSubtypeConfirmationDialog(
|
||||
subtypeToDelete: Subtype?,
|
||||
onDismiss: () -> Unit,
|
||||
onConfirm: () -> Unit,
|
||||
) {
|
||||
subtypeToDelete?.let {
|
||||
JetPrefAlertDialog(
|
||||
title = stringRes(R.string.settings__localization__subtype_delete_confirmation_title),
|
||||
confirmLabel = stringRes(R.string.action__yes),
|
||||
dismissLabel = stringRes(R.string.action__no),
|
||||
onDismiss = onDismiss,
|
||||
onConfirm = onConfirm,
|
||||
) {
|
||||
Text(stringRes(R.string.settings__localization__subtype_delete_confirmation_warning))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,13 +24,13 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextField
|
||||
import androidx.compose.material.TextFieldDefaults
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -41,7 +41,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
@@ -107,7 +106,7 @@ fun SelectLocaleScreen() = FlorisScreen {
|
||||
},
|
||||
singleLine = true,
|
||||
shape = RectangleShape,
|
||||
colors = TextFieldDefaults.textFieldColors(
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
disabledIndicatorColor = Color.Transparent,
|
||||
|
||||
@@ -25,14 +25,14 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
@@ -47,12 +47,11 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
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.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
@@ -66,7 +65,6 @@ import dev.patrickgold.florisboard.ime.core.SubtypePreset
|
||||
import dev.patrickgold.florisboard.ime.keyboard.LayoutArrangementComponent
|
||||
import dev.patrickgold.florisboard.ime.keyboard.LayoutType
|
||||
import dev.patrickgold.florisboard.ime.keyboard.extCorePopupMapping
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.nlp.han.HanShapeBasedLanguageProvider
|
||||
import dev.patrickgold.florisboard.ime.nlp.latin.LatinLanguageProvider
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
@@ -82,7 +80,6 @@ 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.JetPrefListItem
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
|
||||
private val SelectComponentName = ExtensionComponentName("00", "00")
|
||||
@@ -210,7 +207,7 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
var layoutMap by subtypeEditor.layoutMap
|
||||
var nlpProviders by subtypeEditor.nlpProviders
|
||||
|
||||
var showSubtypePresetsDialog by rememberSaveable { mutableStateOf(false) }
|
||||
var showSubtypePresetsDialog by rememberSaveable { mutableStateOf(id == null) }
|
||||
var showSelectAsError by rememberSaveable { mutableStateOf(false) }
|
||||
var errorDialogStrId by rememberSaveable { mutableStateOf<Int?>(null) }
|
||||
|
||||
@@ -228,6 +225,24 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
onDispose { selectLocaleScreenResult?.removeObserver(observer) }
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun SubtypePropertyDropdown(
|
||||
title: String,
|
||||
layoutType: LayoutType
|
||||
) {
|
||||
SubtypeProperty(title) {
|
||||
SubtypeLayoutDropdown(
|
||||
layoutType = layoutType,
|
||||
layouts = layoutExtensions[layoutType] ?: mapOf(),
|
||||
showSelectAsError = showSelectAsError,
|
||||
layoutMap = layoutMap,
|
||||
onLayoutMapChanged = { layoutMap = it },
|
||||
selectListValues = selectListValues,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
actions {
|
||||
if (id != null) {
|
||||
IconButton(onClick = {
|
||||
@@ -281,7 +296,7 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
Text(
|
||||
modifier = Modifier.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
text = stringRes(R.string.settings__localization__suggested_subtype_presets),
|
||||
color = MaterialTheme.colors.primary,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
@@ -365,17 +380,7 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
onDismissRequest = { expanded = false },
|
||||
)
|
||||
}
|
||||
SubtypeProperty(stringRes(R.string.settings__localization__subtype_characters_layout)) {
|
||||
val layoutType = LayoutType.CHARACTERS
|
||||
SubtypeLayoutDropdown(
|
||||
layoutType = layoutType,
|
||||
layouts = layoutExtensions[layoutType] ?: mapOf(),
|
||||
showSelectAsError = showSelectAsError,
|
||||
layoutMap = layoutMap,
|
||||
onLayoutMapChanged = { layoutMap = it },
|
||||
selectListValues = selectListValues,
|
||||
)
|
||||
}
|
||||
SubtypePropertyDropdown(stringRes(R.string.settings__localization__subtype_characters_layout), LayoutType.CHARACTERS)
|
||||
|
||||
SubtypeGroupSpacer()
|
||||
|
||||
@@ -388,7 +393,7 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
)
|
||||
|
||||
val nlpProviderMappingIds = remember(nlpProviderMappings) {
|
||||
SelectListKeys + nlpProviderMappings.keys
|
||||
listOf(SelectNlpProviderId) + nlpProviderMappings.keys
|
||||
}
|
||||
val nlpProviderMappingLabels = remember(nlpProviderMappings) {
|
||||
selectListValues + nlpProviderMappings.values.map { it }
|
||||
@@ -401,8 +406,8 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
selectedIndex = selectedIndex,
|
||||
isError = showSelectAsError && selectedIndex == 0,
|
||||
onSelectItem = { nlpProviders = SubtypeNlpProviderMap(
|
||||
suggestion = nlpProviderMappingIds[it] as String,
|
||||
spelling = nlpProviderMappingIds[it] as String
|
||||
suggestion = nlpProviderMappingIds[it],
|
||||
spelling = nlpProviderMappingIds[it]
|
||||
) },
|
||||
onExpandRequest = { expanded = true },
|
||||
onDismissRequest = { expanded = false },
|
||||
@@ -411,28 +416,9 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
|
||||
SubtypeGroupSpacer()
|
||||
|
||||
SubtypeProperty(stringRes(R.string.settings__localization__subtype_symbols_layout)) {
|
||||
val layoutType = LayoutType.SYMBOLS
|
||||
SubtypeLayoutDropdown(
|
||||
layoutType = layoutType,
|
||||
layouts = layoutExtensions[layoutType] ?: mapOf(),
|
||||
showSelectAsError = showSelectAsError,
|
||||
layoutMap = layoutMap,
|
||||
onLayoutMapChanged = { layoutMap = it },
|
||||
selectListValues = selectListValues,
|
||||
)
|
||||
}
|
||||
SubtypeProperty(stringRes(R.string.settings__localization__subtype_symbols2_layout)) {
|
||||
val layoutType = LayoutType.SYMBOLS2
|
||||
SubtypeLayoutDropdown(
|
||||
layoutType = layoutType,
|
||||
layouts = layoutExtensions[layoutType] ?: mapOf(),
|
||||
showSelectAsError = showSelectAsError,
|
||||
layoutMap = layoutMap,
|
||||
onLayoutMapChanged = { layoutMap = it },
|
||||
selectListValues = selectListValues,
|
||||
)
|
||||
}
|
||||
SubtypePropertyDropdown(stringRes(R.string.settings__localization__subtype_symbols_layout), LayoutType.SYMBOLS)
|
||||
SubtypePropertyDropdown(stringRes(R.string.settings__localization__subtype_symbols2_layout), LayoutType.SYMBOLS2)
|
||||
|
||||
SubtypeProperty(stringRes(R.string.settings__localization__subtype_composer)) {
|
||||
val composerIds = remember(composers) {
|
||||
SelectListKeys + composers.keys
|
||||
@@ -472,64 +458,17 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
|
||||
SubtypeGroupSpacer()
|
||||
|
||||
SubtypeProperty(stringRes(R.string.settings__localization__subtype_numeric_layout)) {
|
||||
val layoutType = LayoutType.NUMERIC
|
||||
SubtypeLayoutDropdown(
|
||||
layoutType = layoutType,
|
||||
layouts = layoutExtensions[layoutType] ?: mapOf(),
|
||||
showSelectAsError = showSelectAsError,
|
||||
layoutMap = layoutMap,
|
||||
onLayoutMapChanged = { layoutMap = it },
|
||||
selectListValues = selectListValues,
|
||||
)
|
||||
}
|
||||
SubtypeProperty(stringRes(R.string.settings__localization__subtype_numeric_advanced_layout)) {
|
||||
val layoutType = LayoutType.NUMERIC_ADVANCED
|
||||
SubtypeLayoutDropdown(
|
||||
layoutType = layoutType,
|
||||
layouts = layoutExtensions[layoutType] ?: mapOf(),
|
||||
showSelectAsError = showSelectAsError,
|
||||
layoutMap = layoutMap,
|
||||
onLayoutMapChanged = { layoutMap = it },
|
||||
selectListValues = selectListValues,
|
||||
)
|
||||
}
|
||||
SubtypeProperty(stringRes(R.string.settings__localization__subtype_numeric_row_layout)) {
|
||||
val layoutType = LayoutType.NUMERIC_ROW
|
||||
SubtypeLayoutDropdown(
|
||||
layoutType = layoutType,
|
||||
layouts = layoutExtensions[layoutType] ?: mapOf(),
|
||||
showSelectAsError = showSelectAsError,
|
||||
layoutMap = layoutMap,
|
||||
onLayoutMapChanged = { layoutMap = it },
|
||||
selectListValues = selectListValues,
|
||||
)
|
||||
}
|
||||
SubtypePropertyDropdown(stringRes(R.string.settings__localization__subtype_numeric_layout), LayoutType.NUMERIC)
|
||||
|
||||
SubtypePropertyDropdown(stringRes(R.string.settings__localization__subtype_numeric_advanced_layout), LayoutType.NUMERIC_ADVANCED)
|
||||
|
||||
SubtypePropertyDropdown(stringRes(R.string.settings__localization__subtype_numeric_row_layout), LayoutType.NUMERIC_ROW)
|
||||
|
||||
SubtypeGroupSpacer()
|
||||
|
||||
SubtypeProperty(stringRes(R.string.settings__localization__subtype_phone_layout)) {
|
||||
val layoutType = LayoutType.PHONE
|
||||
SubtypeLayoutDropdown(
|
||||
layoutType = layoutType,
|
||||
layouts = layoutExtensions[layoutType] ?: mapOf(),
|
||||
showSelectAsError = showSelectAsError,
|
||||
layoutMap = layoutMap,
|
||||
onLayoutMapChanged = { layoutMap = it },
|
||||
selectListValues = selectListValues,
|
||||
)
|
||||
}
|
||||
SubtypeProperty(stringRes(R.string.settings__localization__subtype_phone2_layout)) {
|
||||
val layoutType = LayoutType.PHONE2
|
||||
SubtypeLayoutDropdown(
|
||||
layoutType = layoutType,
|
||||
layouts = layoutExtensions[layoutType] ?: mapOf(),
|
||||
showSelectAsError = showSelectAsError,
|
||||
layoutMap = layoutMap,
|
||||
onLayoutMapChanged = { layoutMap = it },
|
||||
selectListValues = selectListValues,
|
||||
)
|
||||
}
|
||||
SubtypePropertyDropdown(stringRes(R.string.settings__localization__subtype_phone_layout), LayoutType.PHONE)
|
||||
|
||||
SubtypePropertyDropdown(stringRes(R.string.settings__localization__subtype_phone2_layout), LayoutType.PHONE2)
|
||||
}
|
||||
|
||||
if (showSubtypePresetsDialog) {
|
||||
@@ -583,7 +522,7 @@ private fun SubtypeProperty(text: String, content: @Composable () -> Unit) {
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 8.dp),
|
||||
text = text,
|
||||
style = MaterialTheme.typography.subtitle2,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
)
|
||||
content()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2024 Patrick Goldinger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,42 +16,126 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.settings.media
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.EmojiSymbols
|
||||
import androidx.compose.material.icons.outlined.Schedule
|
||||
import androidx.compose.runtime.Composable
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistory
|
||||
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
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
|
||||
@OptIn(ExperimentalJetPrefDatastoreUi::class)
|
||||
@Composable
|
||||
fun MediaScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.settings__media__title)
|
||||
previewFieldVisible = true
|
||||
iconSpaceReserved = false
|
||||
iconSpaceReserved = true
|
||||
|
||||
content {
|
||||
ListPreference(
|
||||
prefs.media.emojiPreferredSkinTone,
|
||||
prefs.emoji.preferredSkinTone,
|
||||
title = stringRes(R.string.prefs__media__emoji_preferred_skin_tone),
|
||||
entries = EmojiSkinTone.listEntries(),
|
||||
)
|
||||
DialogSliderPreference(
|
||||
prefs.media.emojiRecentlyUsedMaxSize,
|
||||
title = stringRes(R.string.prefs__media__emoji_recently_used_max_size),
|
||||
valueLabel = { maxSize ->
|
||||
if (maxSize == 0) {
|
||||
stringRes(R.string.general__unlimited)
|
||||
} else {
|
||||
pluralsRes(R.plurals.unit__items__written, maxSize, "v" to maxSize)
|
||||
}
|
||||
},
|
||||
min = 0,
|
||||
max = 120,
|
||||
stepIncrement = 1,
|
||||
entries = enumDisplayEntriesOf(EmojiSkinTone::class),
|
||||
)
|
||||
|
||||
PreferenceGroup(title = stringRes(R.string.prefs__media__emoji_history__title)) {
|
||||
SwitchPreference(
|
||||
prefs.emoji.historyEnabled,
|
||||
icon = Icons.Outlined.Schedule,
|
||||
title = stringRes(R.string.prefs__media__emoji_history_enabled),
|
||||
summary = stringRes(R.string.prefs__media__emoji_history_enabled__summary),
|
||||
)
|
||||
ListPreference(
|
||||
prefs.emoji.historyPinnedUpdateStrategy,
|
||||
title = stringRes(R.string.prefs__media__emoji_history_pinned_update_strategy),
|
||||
entries = enumDisplayEntriesOf(EmojiHistory.UpdateStrategy::class),
|
||||
enabledIf = { prefs.emoji.historyEnabled.isTrue() },
|
||||
)
|
||||
ListPreference(
|
||||
prefs.emoji.historyRecentUpdateStrategy,
|
||||
title = stringRes(R.string.prefs__media__emoji_history_recent_update_strategy),
|
||||
entries = enumDisplayEntriesOf(EmojiHistory.UpdateStrategy::class),
|
||||
enabledIf = { prefs.emoji.historyEnabled.isTrue() },
|
||||
)
|
||||
DialogSliderPreference(
|
||||
primaryPref = prefs.emoji.historyPinnedMaxSize,
|
||||
secondaryPref = prefs.emoji.historyRecentMaxSize,
|
||||
title = stringRes(R.string.prefs__media__emoji_history_max_size),
|
||||
primaryLabel = stringRes(R.string.emoji__history__pinned),
|
||||
secondaryLabel = stringRes(R.string.emoji__history__recent),
|
||||
valueLabel = { maxSize ->
|
||||
if (maxSize == EmojiHistory.MaxSizeUnlimited) {
|
||||
stringRes(R.string.general__unlimited)
|
||||
} else {
|
||||
pluralsRes(R.plurals.unit__items__written, maxSize, "v" to maxSize)
|
||||
}
|
||||
},
|
||||
min = 0,
|
||||
max = 120,
|
||||
stepIncrement = 1,
|
||||
enabledIf = { prefs.emoji.historyEnabled.isTrue() },
|
||||
)
|
||||
}
|
||||
|
||||
PreferenceGroup(title = stringRes(R.string.prefs__media__emoji_suggestion__title)) {
|
||||
SwitchPreference(
|
||||
prefs.emoji.suggestionEnabled,
|
||||
icon = Icons.Outlined.EmojiSymbols,
|
||||
title = stringRes(R.string.prefs__media__emoji_suggestion_enabled),
|
||||
summary = stringRes(R.string.prefs__media__emoji_suggestion_enabled__summary),
|
||||
)
|
||||
ListPreference(
|
||||
prefs.emoji.suggestionType,
|
||||
title = stringRes(R.string.prefs__media__emoji_suggestion_type),
|
||||
entries = enumDisplayEntriesOf(EmojiSuggestionType::class),
|
||||
enabledIf = { prefs.emoji.suggestionEnabled.isTrue() },
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.emoji.suggestionUpdateHistory,
|
||||
title = stringRes(R.string.prefs__media__emoji_suggestion_update_history),
|
||||
summary = stringRes(R.string.prefs__media__emoji_suggestion_update_history__summary),
|
||||
enabledIf = {
|
||||
prefs.emoji.suggestionEnabled.isTrue() && prefs.emoji.historyEnabled.isTrue()
|
||||
},
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.emoji.suggestionCandidateShowName,
|
||||
title = stringRes(R.string.prefs__media__emoji_suggestion_candidate_show_name),
|
||||
summary = stringRes(R.string.prefs__media__emoji_suggestion_candidate_show_name__summary),
|
||||
enabledIf = { prefs.emoji.suggestionEnabled.isTrue() },
|
||||
)
|
||||
DialogSliderPreference(
|
||||
prefs.emoji.suggestionQueryMinLength,
|
||||
title = stringRes(R.string.prefs__media__emoji_suggestion_query_min_length),
|
||||
valueLabel = { length ->
|
||||
pluralsRes(R.plurals.unit__characters__written, length, "v" to length)
|
||||
},
|
||||
min = 1,
|
||||
max = 5,
|
||||
stepIncrement = 1,
|
||||
enabledIf = { prefs.emoji.suggestionEnabled.isTrue() },
|
||||
)
|
||||
DialogSliderPreference(
|
||||
prefs.emoji.suggestionCandidateMaxCount,
|
||||
title = stringRes(R.string.prefs__media__emoji_suggestion_candidate_max_count),
|
||||
valueLabel = { count ->
|
||||
pluralsRes(R.plurals.unit__candidates__written, count, "v" to count)
|
||||
},
|
||||
min = 1,
|
||||
max = 10,
|
||||
stepIncrement = 1,
|
||||
enabledIf = { prefs.emoji.suggestionEnabled.isTrue() },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
package dev.patrickgold.florisboard.app.settings.smartbar
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.smartbar.CandidatesDisplayMode
|
||||
import dev.patrickgold.florisboard.ime.smartbar.ExtendedActionsPlacement
|
||||
import dev.patrickgold.florisboard.ime.smartbar.SmartbarLayout
|
||||
@@ -41,7 +43,7 @@ fun SmartbarScreen() = FlorisScreen {
|
||||
ListPreference(
|
||||
listPref = prefs.smartbar.layout,
|
||||
title = stringRes(R.string.pref__smartbar__layout__label),
|
||||
entries = SmartbarLayout.listEntries(),
|
||||
entries = enumDisplayEntriesOf(SmartbarLayout::class),
|
||||
enabledIf = { prefs.smartbar.enabled isEqualTo true },
|
||||
)
|
||||
|
||||
@@ -49,7 +51,7 @@ fun SmartbarScreen() = FlorisScreen {
|
||||
ListPreference(
|
||||
prefs.suggestion.displayMode,
|
||||
title = stringRes(R.string.pref__suggestion__display_mode__label),
|
||||
entries = CandidatesDisplayMode.listEntries(),
|
||||
entries = enumDisplayEntriesOf(CandidatesDisplayMode::class),
|
||||
enabledIf = { prefs.smartbar.enabled isEqualTo true },
|
||||
visibleIf = { prefs.smartbar.layout isNotEqualTo SmartbarLayout.ACTIONS_ONLY },
|
||||
)
|
||||
@@ -63,17 +65,22 @@ fun SmartbarScreen() = FlorisScreen {
|
||||
prefs.smartbar.layout isEqualTo SmartbarLayout.SUGGESTIONS_ACTIONS_EXTENDED
|
||||
},
|
||||
)
|
||||
// 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)
|
||||
}
|
||||
SwitchPreference(
|
||||
prefs.smartbar.sharedActionsAutoExpandCollapse,
|
||||
title = stringRes(R.string.pref__smartbar__shared_actions_auto_expand_collapse__label),
|
||||
summary = stringRes(R.string.pref__smartbar__shared_actions_auto_expand_collapse__summary),
|
||||
enabledIf = { prefs.smartbar.enabled isEqualTo true },
|
||||
summary = "[Since v0.4.1] Always enabled due to UX issues",
|
||||
enabledIf = { false },
|
||||
visibleIf = { prefs.smartbar.layout isEqualTo SmartbarLayout.SUGGESTIONS_ACTIONS_SHARED },
|
||||
)
|
||||
ListPreference(
|
||||
listPref = prefs.smartbar.extendedActionsPlacement,
|
||||
title = stringRes(R.string.pref__smartbar__extended_actions_placement__label),
|
||||
entries = ExtendedActionsPlacement.listEntries(),
|
||||
entries = enumDisplayEntriesOf(ExtendedActionsPlacement::class),
|
||||
enabledIf = { prefs.smartbar.enabled isEqualTo true },
|
||||
visibleIf = { prefs.smartbar.layout isEqualTo SmartbarLayout.SUGGESTIONS_ACTIONS_EXTENDED },
|
||||
)
|
||||
|
||||
@@ -16,34 +16,10 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.settings.theme
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.ui.listPrefEntries
|
||||
import org.florisboard.lib.kotlin.curlyFormat
|
||||
|
||||
/**
|
||||
* DisplayColorsAs indicates how color strings should be visually presented to the user.
|
||||
*/
|
||||
enum class DisplayColorsAs {
|
||||
HEX8,
|
||||
RGBA;
|
||||
|
||||
companion object {
|
||||
@Composable
|
||||
fun listEntries() = listPrefEntries {
|
||||
entry(
|
||||
key = HEX8,
|
||||
label = stringRes(R.string.enum__display_colors_as__hex8),
|
||||
description = stringRes(R.string.general__example_given).curlyFormat("example" to "#4caf50ff"),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = RGBA,
|
||||
label = stringRes(R.string.enum__display_colors_as__rgba),
|
||||
description = stringRes(R.string.general__example_given).curlyFormat("example" to "rgba(76,175,80,1.0)"),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,6 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.settings.theme
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.ui.listPrefEntries
|
||||
|
||||
/**
|
||||
* DisplayPreviewAfterDialogs indicates if the keyboard should auto-open after closing
|
||||
* any dialog. This is useful because the dialog always hides the keyboard and one may
|
||||
@@ -30,28 +25,4 @@ enum class DisplayKbdAfterDialogs {
|
||||
ALWAYS,
|
||||
NEVER,
|
||||
REMEMBER;
|
||||
|
||||
companion object {
|
||||
@Composable
|
||||
fun listEntries() = listPrefEntries {
|
||||
entry(
|
||||
key = ALWAYS,
|
||||
label = stringRes(R.string.enum__display_kbd_after_dialogs__always),
|
||||
description = stringRes(R.string.enum__display_kbd_after_dialogs__always__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = NEVER,
|
||||
label = stringRes(R.string.enum__display_kbd_after_dialogs__never),
|
||||
description = stringRes(R.string.enum__display_kbd_after_dialogs__never__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
entry(
|
||||
key = REMEMBER,
|
||||
label = stringRes(R.string.enum__display_kbd_after_dialogs__remember),
|
||||
description = stringRes(R.string.enum__display_kbd_after_dialogs__remember__description),
|
||||
showDescriptionOnlyIfSelected = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,12 +30,11 @@ import androidx.compose.foundation.shape.CutCornerShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.HelpOutline
|
||||
import androidx.compose.material.icons.outlined.HelpOutline
|
||||
import androidx.compose.material.icons.automirrored.filled.HelpOutline
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -49,7 +48,6 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
@@ -72,29 +70,29 @@ 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.lib.snygg.SnyggLevel
|
||||
import dev.patrickgold.florisboard.lib.snygg.SnyggPropertySetSpec
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.MaterialYouColor
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggCutCornerDpShapeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggCutCornerPercentShapeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggDefinedVarValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggDpShapeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggDpSizeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggImplicitInheritValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggMaterialYouDarkColorValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggMaterialYouLightColorValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggMaterialYouValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggPercentShapeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggRoundedCornerDpShapeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggRoundedCornerPercentShapeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggShapeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggSolidColorValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggSpSizeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggValueEncoder
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggVarValueEncoders
|
||||
import org.florisboard.lib.snygg.SnyggLevel
|
||||
import org.florisboard.lib.snygg.SnyggPropertySetSpec
|
||||
import org.florisboard.lib.snygg.value.MaterialYouColor
|
||||
import org.florisboard.lib.snygg.value.SnyggCutCornerDpShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggCutCornerPercentShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDefinedVarValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDpShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDpSizeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggImplicitInheritValue
|
||||
import org.florisboard.lib.snygg.value.SnyggMaterialYouDarkColorValue
|
||||
import org.florisboard.lib.snygg.value.SnyggMaterialYouLightColorValue
|
||||
import org.florisboard.lib.snygg.value.SnyggMaterialYouValue
|
||||
import org.florisboard.lib.snygg.value.SnyggPercentShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggRoundedCornerDpShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggRoundedCornerPercentShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggSolidColorValue
|
||||
import org.florisboard.lib.snygg.value.SnyggSpSizeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggValue
|
||||
import org.florisboard.lib.snygg.value.SnyggValueEncoder
|
||||
import org.florisboard.lib.snygg.value.SnyggVarValueEncoders
|
||||
import dev.patrickgold.florisboard.lib.stripUnicodeCtrlChars
|
||||
import dev.patrickgold.jetpref.material.ui.ExperimentalJetPrefMaterialUi
|
||||
import dev.patrickgold.jetpref.material.ui.ExperimentalJetPrefMaterial3Ui
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefColorPicker
|
||||
import dev.patrickgold.jetpref.material.ui.rememberJetPrefColorPickerState
|
||||
@@ -220,7 +218,7 @@ internal fun EditPropertyDialog(
|
||||
},
|
||||
onNeutral = onDelete,
|
||||
neutralColors = ButtonDefaults.textButtonColors(
|
||||
contentColor = MaterialTheme.colors.error,
|
||||
contentColor = MaterialTheme.colorScheme.error,
|
||||
),
|
||||
) {
|
||||
Column {
|
||||
@@ -228,7 +226,7 @@ internal fun EditPropertyDialog(
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 16.dp),
|
||||
text = stringRes(R.string.settings__theme_editor__property_already_exists),
|
||||
color = MaterialTheme.colors.error,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -353,7 +351,7 @@ private fun PropertyValueEncoderDropdown(
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalJetPrefMaterialUi::class)
|
||||
@OptIn(ExperimentalJetPrefMaterial3Ui::class)
|
||||
@Composable
|
||||
private fun PropertyValueEditor(
|
||||
value: SnyggValue,
|
||||
@@ -417,7 +415,7 @@ private fun PropertyValueEditor(
|
||||
.padding(end = 12.dp)
|
||||
.weight(1f),
|
||||
text = colorPickerStr,
|
||||
style = MaterialTheme.typography.body2,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
)
|
||||
SnyggValueIcon(
|
||||
@@ -457,7 +455,7 @@ private fun PropertyValueEditor(
|
||||
FlorisIconButton(
|
||||
onClick = { showSyntaxHelp = !showSyntaxHelp },
|
||||
modifier = Modifier.offset(x = 12.dp),
|
||||
icon = Icons.Default.HelpOutline,
|
||||
icon = Icons.AutoMirrored.Filled.HelpOutline,
|
||||
)
|
||||
},
|
||||
) {
|
||||
@@ -477,7 +475,7 @@ private fun PropertyValueEditor(
|
||||
rgb(r,g,b)
|
||||
-> r,g,b in 0..255
|
||||
""".trimIndent(),
|
||||
style = MaterialTheme.typography.body2,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
)
|
||||
}
|
||||
@@ -680,7 +678,7 @@ private fun PropertyValueEditor(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.requiredSize(40.dp)
|
||||
.border(1.dp, MaterialTheme.colors.onBackground, shape),
|
||||
.border(1.dp, MaterialTheme.colorScheme.onBackground, shape),
|
||||
)
|
||||
Column {
|
||||
FlorisChip(
|
||||
@@ -849,7 +847,7 @@ private fun PropertyValueEditor(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.requiredSize(40.dp)
|
||||
.border(1.dp, MaterialTheme.colors.onBackground, shape),
|
||||
.border(1.dp, MaterialTheme.colorScheme.onBackground, shape),
|
||||
)
|
||||
Column {
|
||||
FlorisChip(
|
||||
@@ -938,7 +936,7 @@ private fun PropertyValueEditor(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.requiredSize(40.dp)
|
||||
.border(1.dp, MaterialTheme.colors.onBackground, value.shape),
|
||||
.border(1.dp, MaterialTheme.colorScheme.onBackground, value.shape),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,16 +39,17 @@ import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
|
||||
import androidx.compose.foundation.text.selection.TextSelectionColors
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextFieldDefaults
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.HelpOutline
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.HelpOutline
|
||||
import androidx.compose.material.icons.filled.Pageview
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
@@ -65,7 +66,6 @@ import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
@@ -85,8 +85,8 @@ import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUiSpec
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.NATIVE_NULLPTR
|
||||
import dev.patrickgold.florisboard.lib.android.showShortToast
|
||||
import dev.patrickgold.florisboard.lib.android.stringRes
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.stringRes
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisChip
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisDropdownMenu
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisHyperlinkText
|
||||
@@ -94,8 +94,8 @@ import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedTextField
|
||||
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.snygg.SnyggLevel
|
||||
import dev.patrickgold.florisboard.lib.snygg.SnyggRule
|
||||
import org.florisboard.lib.snygg.SnyggLevel
|
||||
import org.florisboard.lib.snygg.SnyggRule
|
||||
import dev.patrickgold.florisboard.lib.util.InputMethodUtils
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import org.florisboard.lib.kotlin.curlyFormat
|
||||
@@ -190,7 +190,7 @@ internal fun EditRuleDialog(
|
||||
} else {
|
||||
null
|
||||
},
|
||||
neutralColors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colors.error),
|
||||
neutralColors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.error),
|
||||
onNeutral = { onDeleteRule(initRule) },
|
||||
) {
|
||||
Column {
|
||||
@@ -198,7 +198,7 @@ internal fun EditRuleDialog(
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 16.dp),
|
||||
text = stringRes(R.string.settings__theme_editor__rule_already_exists),
|
||||
color = MaterialTheme.colors.error,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ internal fun EditRuleDialog(
|
||||
SnyggLevel.DEVELOPER -> SnyggRule.PRESSED_SELECTOR
|
||||
else -> stringRes(R.string.snygg__rule_selector__pressed)
|
||||
},
|
||||
color = if (pressedSelector) MaterialTheme.colors.primaryVariant else Color.Unspecified,
|
||||
selected = pressedSelector,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { focusSelector = !focusSelector },
|
||||
@@ -233,7 +233,7 @@ internal fun EditRuleDialog(
|
||||
SnyggLevel.DEVELOPER -> SnyggRule.FOCUS_SELECTOR
|
||||
else -> stringRes(R.string.snygg__rule_selector__focus)
|
||||
},
|
||||
color = if (focusSelector) MaterialTheme.colors.primaryVariant else Color.Unspecified,
|
||||
selected = focusSelector,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { disabledSelector = !disabledSelector },
|
||||
@@ -241,7 +241,7 @@ internal fun EditRuleDialog(
|
||||
SnyggLevel.DEVELOPER -> SnyggRule.DISABLED_SELECTOR
|
||||
else -> stringRes(R.string.snygg__rule_selector__disabled)
|
||||
},
|
||||
color = if (disabledSelector) MaterialTheme.colors.primaryVariant else Color.Unspecified,
|
||||
selected = disabledSelector,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -270,6 +270,7 @@ internal fun EditRuleDialog(
|
||||
FlorisChip(
|
||||
onClick = { editCodeDialogValue = code },
|
||||
text = code.toString(),
|
||||
selected = editCodeDialogValue == code,
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
)
|
||||
}
|
||||
@@ -286,7 +287,7 @@ internal fun EditRuleDialog(
|
||||
}
|
||||
else -> stringRes(R.string.enum__input_shift_state__unshifted)
|
||||
},
|
||||
color = if (shiftStateUnshifted) MaterialTheme.colors.primaryVariant else Color.Unspecified,
|
||||
selected = shiftStateUnshifted,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { shiftStateShiftedManual = !shiftStateShiftedManual },
|
||||
@@ -296,7 +297,7 @@ internal fun EditRuleDialog(
|
||||
}
|
||||
else -> stringRes(R.string.enum__input_shift_state__shifted_manual)
|
||||
},
|
||||
color = if (shiftStateShiftedManual) MaterialTheme.colors.primaryVariant else Color.Unspecified,
|
||||
selected = shiftStateShiftedManual,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { shiftStateShiftedAutomatic = !shiftStateShiftedAutomatic },
|
||||
@@ -306,7 +307,7 @@ internal fun EditRuleDialog(
|
||||
}
|
||||
else -> stringRes(R.string.enum__input_shift_state__shifted_automatic)
|
||||
},
|
||||
color = if (shiftStateShiftedAutomatic) MaterialTheme.colors.primaryVariant else Color.Unspecified,
|
||||
selected = shiftStateShiftedAutomatic,
|
||||
)
|
||||
FlorisChip(
|
||||
onClick = { shiftStateCapsLock = !shiftStateCapsLock },
|
||||
@@ -316,7 +317,7 @@ internal fun EditRuleDialog(
|
||||
}
|
||||
else -> stringRes(R.string.enum__input_shift_state__caps_lock)
|
||||
},
|
||||
color = if (shiftStateCapsLock) MaterialTheme.colors.primaryVariant else Color.Unspecified,
|
||||
selected = shiftStateCapsLock,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -335,6 +336,7 @@ internal fun EditRuleDialog(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun EditCodeValueDialog(
|
||||
codeValue: Int,
|
||||
@@ -368,7 +370,7 @@ private fun EditCodeValueDialog(
|
||||
val recordingKeyColor = if (isRecordingKey) {
|
||||
rememberInfiniteTransition().animateColor(
|
||||
initialValue = LocalContentColor.current,
|
||||
targetValue = MaterialTheme.colors.error,
|
||||
targetValue = MaterialTheme.colorScheme.error,
|
||||
animationSpec = infiniteRepeatable(
|
||||
tween(750),
|
||||
repeatMode = RepeatMode.Reverse,
|
||||
@@ -461,7 +463,7 @@ private fun EditCodeValueDialog(
|
||||
} else {
|
||||
null
|
||||
},
|
||||
neutralColors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colors.error),
|
||||
neutralColors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.error),
|
||||
onNeutral = {
|
||||
onDelete(codeValue)
|
||||
onDismiss()
|
||||
@@ -470,7 +472,7 @@ private fun EditCodeValueDialog(
|
||||
FlorisIconButton(
|
||||
onClick = { showKeyCodesHelp = !showKeyCodesHelp },
|
||||
modifier = Modifier.offset(x = 12.dp),
|
||||
icon = Icons.Default.HelpOutline,
|
||||
icon = Icons.AutoMirrored.Filled.HelpOutline,
|
||||
)
|
||||
},
|
||||
) {
|
||||
@@ -526,12 +528,12 @@ private fun EditCodeValueDialog(
|
||||
isError = showError,
|
||||
singleLine = true,
|
||||
colors = if (isRecordingKey) {
|
||||
TextFieldDefaults.outlinedTextFieldColors(
|
||||
textColor = Color.Transparent,
|
||||
OutlinedTextFieldDefaults.colors(
|
||||
focusedTextColor = Color.Transparent,
|
||||
cursorColor = Color.Transparent,
|
||||
)
|
||||
} else {
|
||||
TextFieldDefaults.outlinedTextFieldColors()
|
||||
OutlinedTextFieldDefaults.colors()
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -550,7 +552,7 @@ private fun EditCodeValueDialog(
|
||||
"i_min" to KeyCode.Spec.INTERNAL_MIN,
|
||||
"i_max" to KeyCode.Spec.INTERNAL_MAX,
|
||||
),
|
||||
color = MaterialTheme.colors.error,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -593,7 +595,7 @@ private fun TextKeyDataPreviewBox(
|
||||
modifier = Modifier
|
||||
.padding(end = 16.dp)
|
||||
.background(
|
||||
color = MaterialTheme.colors.onSurface.copy(alpha = 0.12f),
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f),
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
)
|
||||
.height(36.dp)
|
||||
|
||||
@@ -20,9 +20,10 @@ 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.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.snygg.SnyggLevel
|
||||
import org.florisboard.lib.snygg.SnyggLevel
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceLayout
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
@@ -40,17 +41,17 @@ fun FineTuneDialog(onDismiss: () -> Unit) {
|
||||
ListPreference(
|
||||
listPref = prefs.theme.editorLevel,
|
||||
title = stringRes(R.string.settings__theme_editor__fine_tune__level),
|
||||
entries = SnyggLevel.listEntries(),
|
||||
entries = enumDisplayEntriesOf(SnyggLevel::class),
|
||||
)
|
||||
ListPreference(
|
||||
listPref = prefs.theme.editorDisplayColorsAs,
|
||||
title = stringRes(R.string.settings__theme_editor__fine_tune__display_colors_as),
|
||||
entries = DisplayColorsAs.listEntries(),
|
||||
entries = enumDisplayEntriesOf(DisplayColorsAs::class),
|
||||
)
|
||||
ListPreference(
|
||||
listPref = prefs.theme.editorDisplayKbdAfterDialogs,
|
||||
title = stringRes(R.string.settings__theme_editor__fine_tune__display_kbd_after_dialogs),
|
||||
entries = DisplayKbdAfterDialogs.listEntries(),
|
||||
entries = enumDisplayEntriesOf(DisplayKbdAfterDialogs::class),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,32 +25,29 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.shape.CutCornerShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.FormatSize
|
||||
import androidx.compose.material.icons.filled.Link
|
||||
import androidx.compose.material.icons.filled.Straighten
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggCutCornerDpShapeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggDefinedVarValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggDpSizeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggMaterialYouLightColorValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggMaterialYouValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggRoundedCornerDpShapeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggShapeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggSolidColorValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggSpSizeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggValue
|
||||
import org.florisboard.lib.snygg.value.SnyggCutCornerDpShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDefinedVarValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDpSizeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggMaterialYouValue
|
||||
import org.florisboard.lib.snygg.value.SnyggRoundedCornerDpShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggSolidColorValue
|
||||
import org.florisboard.lib.snygg.value.SnyggSpSizeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggValue
|
||||
import dev.patrickgold.jetpref.material.ui.checkeredBackground
|
||||
|
||||
object SnyggValueIcon {
|
||||
@@ -102,7 +99,7 @@ internal fun SnyggValueIcon(
|
||||
Box(
|
||||
modifier = modifier
|
||||
.requiredSize(spec.iconSizeMinusBorder)
|
||||
.border(spec.borderWith, MaterialTheme.colors.onBackground, value.alwaysPercentShape())
|
||||
.border(spec.borderWith, MaterialTheme.colorScheme.onBackground, value.alwaysPercentShape())
|
||||
)
|
||||
}
|
||||
is SnyggDpSizeValue -> {
|
||||
@@ -143,7 +140,7 @@ internal fun SnyggValueIcon(
|
||||
.offset(x = 1.dp)
|
||||
.requiredSize(smallSpec.iconSize)
|
||||
.padding(vertical = 2.dp)
|
||||
.background(MaterialTheme.colors.background, spec.boxShape),
|
||||
.background(MaterialTheme.colorScheme.background, spec.boxShape),
|
||||
)
|
||||
Icon(
|
||||
modifier = Modifier.requiredSize(smallSpec.iconSize),
|
||||
@@ -167,8 +164,8 @@ internal fun SnyggValueColorBox(
|
||||
) {
|
||||
Surface(
|
||||
modifier = modifier.requiredSize(spec.iconSize),
|
||||
color = MaterialTheme.colors.background,
|
||||
elevation = spec.elevation,
|
||||
color = MaterialTheme.colorScheme.background,
|
||||
shadowElevation = spec.elevation,
|
||||
shape = spec.boxShape,
|
||||
) {
|
||||
Box(
|
||||
|
||||
@@ -33,18 +33,18 @@ import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.selection.toggleable
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.ExtendedFloatingActionButton
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Switch
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.Tune
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -62,13 +62,13 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
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.apptheme.Shapes
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionComponentView
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUiSpec
|
||||
@@ -76,7 +76,6 @@ import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponentEditor
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionEditor
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeManager
|
||||
import dev.patrickgold.florisboard.lib.android.showLongToast
|
||||
import dev.patrickgold.florisboard.lib.cache.CacheManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
|
||||
@@ -89,24 +88,25 @@ 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.io.readJson
|
||||
import dev.patrickgold.florisboard.lib.io.subFile
|
||||
import dev.patrickgold.florisboard.lib.rememberValidationResult
|
||||
import dev.patrickgold.florisboard.lib.snygg.SnyggLevel
|
||||
import dev.patrickgold.florisboard.lib.snygg.SnyggPropertySetEditor
|
||||
import dev.patrickgold.florisboard.lib.snygg.SnyggPropertySetSpec
|
||||
import dev.patrickgold.florisboard.lib.snygg.SnyggRule
|
||||
import dev.patrickgold.florisboard.lib.snygg.SnyggStylesheet
|
||||
import dev.patrickgold.florisboard.lib.snygg.SnyggStylesheetEditor
|
||||
import dev.patrickgold.florisboard.lib.snygg.SnyggStylesheetJsonConfig
|
||||
import dev.patrickgold.florisboard.lib.snygg.definedVariablesRule
|
||||
import dev.patrickgold.florisboard.lib.snygg.isDefinedVariablesRule
|
||||
import dev.patrickgold.florisboard.themeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.kotlin.io.readJson
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import org.florisboard.lib.snygg.SnyggLevel
|
||||
import org.florisboard.lib.snygg.SnyggPropertySetEditor
|
||||
import org.florisboard.lib.snygg.SnyggPropertySetSpec
|
||||
import org.florisboard.lib.snygg.SnyggRule
|
||||
import org.florisboard.lib.snygg.SnyggStylesheet
|
||||
import org.florisboard.lib.snygg.SnyggStylesheetEditor
|
||||
import org.florisboard.lib.snygg.SnyggStylesheetJsonConfig
|
||||
import org.florisboard.lib.snygg.definedVariablesRule
|
||||
import org.florisboard.lib.snygg.isDefinedVariablesRule
|
||||
|
||||
internal val IntListSaver = Saver<SnapshotStateList<Int>, ArrayList<Int>>(
|
||||
save = { ArrayList(it) },
|
||||
@@ -300,7 +300,7 @@ fun ThemeEditorScreen(
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 8.dp, start = 16.dp, end = 16.dp),
|
||||
text = stringRes(R.string.snygg__rule_element__defines_description),
|
||||
style = MaterialTheme.typography.body2,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
@@ -549,9 +549,9 @@ private fun SnyggRuleRow(
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(end = 8.dp)
|
||||
.background(MaterialTheme.colors.primaryVariant),
|
||||
.background(MaterialTheme.colorScheme.primaryContainer, shape = Shapes.small),
|
||||
text = text,
|
||||
style = MaterialTheme.typography.body2,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
@@ -562,7 +562,7 @@ private fun SnyggRuleRow(
|
||||
fun AttributesList(text: String, list: String) {
|
||||
Text(
|
||||
text = "$text = $list",
|
||||
style = MaterialTheme.typography.body2,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = LocalContentColor.current.copy(alpha = 0.56f),
|
||||
fontFamily = FontFamily.Monospace,
|
||||
maxLines = 1,
|
||||
@@ -582,7 +582,7 @@ private fun SnyggRuleRow(
|
||||
) {
|
||||
Text(
|
||||
text = translateElementName(rule, level),
|
||||
style = MaterialTheme.typography.body2,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
@@ -618,14 +618,14 @@ private fun SnyggRuleRow(
|
||||
FlorisIconButton(
|
||||
onClick = onEditRuleBtnClick,
|
||||
icon = Icons.Default.Edit,
|
||||
iconColor = MaterialTheme.colors.primary,
|
||||
iconColor = MaterialTheme.colorScheme.primary,
|
||||
iconModifier = Modifier.size(ButtonDefaults.IconSize),
|
||||
)
|
||||
}
|
||||
FlorisIconButton(
|
||||
onClick = onAddPropertyBtnClick,
|
||||
icon = Icons.Default.Add,
|
||||
iconColor = MaterialTheme.colors.secondary,
|
||||
iconColor = MaterialTheme.colorScheme.secondary,
|
||||
iconModifier = Modifier.size(ButtonDefaults.IconSize),
|
||||
)
|
||||
}
|
||||
@@ -645,7 +645,7 @@ internal fun DialogProperty(
|
||||
.weight(1f)
|
||||
.padding(vertical = 8.dp),
|
||||
text = text,
|
||||
style = MaterialTheme.typography.subtitle2,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
)
|
||||
trailingIconTitle()
|
||||
}
|
||||
|
||||
@@ -16,83 +16,56 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.settings.theme
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.RadioButton
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.DarkMode
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.Input
|
||||
import androidx.compose.material.icons.filled.LightMode
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
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.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.theme.ThemeExtension
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
|
||||
import dev.patrickgold.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.florisboard.themeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
|
||||
enum class ThemeManagerScreenAction(val id: String) {
|
||||
SELECT_DAY("select-day"),
|
||||
SELECT_NIGHT("select-night"),
|
||||
MANAGE("manage-installed-themes");
|
||||
SELECT_NIGHT("select-night");
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalJetPrefDatastoreUi::class)
|
||||
@Composable
|
||||
fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
title = stringRes(when (action) {
|
||||
ThemeManagerScreenAction.SELECT_DAY -> R.string.settings__theme_manager__title_day
|
||||
ThemeManagerScreenAction.SELECT_NIGHT -> R.string.settings__theme_manager__title_night
|
||||
ThemeManagerScreenAction.MANAGE -> R.string.settings__theme_manager__title_manage
|
||||
else -> error("Theme manager screen action must not be null")
|
||||
})
|
||||
previewFieldVisible = action != ThemeManagerScreenAction.MANAGE
|
||||
previewFieldVisible = true
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val extensionManager by context.extensionManager()
|
||||
val themeManager by context.themeManager()
|
||||
|
||||
val indexedThemeExtensions by extensionManager.themes.observeAsNonNullState()
|
||||
val selectedManagerThemeId = remember { mutableStateOf<ExtensionComponentName?>(null) }
|
||||
val extGroupedThemes = remember(indexedThemeExtensions) {
|
||||
buildMap<String, List<ThemeExtensionComponent>> {
|
||||
for (ext in indexedThemeExtensions) {
|
||||
@@ -104,7 +77,6 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
fun getThemeIdPref() = when (action) {
|
||||
ThemeManagerScreenAction.SELECT_DAY -> prefs.theme.dayThemeId
|
||||
ThemeManagerScreenAction.SELECT_NIGHT -> prefs.theme.nightThemeId
|
||||
ThemeManagerScreenAction.MANAGE -> error("internal error in manager logic")
|
||||
}
|
||||
|
||||
fun setTheme(extId: String, componentId: String) {
|
||||
@@ -114,18 +86,13 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
ThemeManagerScreenAction.SELECT_NIGHT -> {
|
||||
getThemeIdPref().set(extComponentName)
|
||||
}
|
||||
ThemeManagerScreenAction.MANAGE -> {
|
||||
selectedManagerThemeId.value = extComponentName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val activeThemeId by when (action) {
|
||||
ThemeManagerScreenAction.SELECT_DAY,
|
||||
ThemeManagerScreenAction.SELECT_NIGHT -> getThemeIdPref().observeAsState()
|
||||
ThemeManagerScreenAction.MANAGE -> selectedManagerThemeId
|
||||
}
|
||||
var themeExtToDelete by remember { mutableStateOf<Extension?>(null) }
|
||||
|
||||
content {
|
||||
DisposableEffect(activeThemeId) {
|
||||
@@ -135,34 +102,12 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
}
|
||||
}
|
||||
val grayColor = LocalContentColor.current.copy(alpha = 0.56f)
|
||||
if (action == ThemeManagerScreenAction.MANAGE) {
|
||||
FlorisOutlinedBox(
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
) {
|
||||
this@content.Preference(
|
||||
onClick = { navController.navigate(
|
||||
Routes.Ext.Edit("null", ThemeExtension.SERIAL_TYPE)
|
||||
) },
|
||||
icon = Icons.Default.Add,
|
||||
title = stringRes(R.string.ext__editor__title_create_theme),
|
||||
)
|
||||
this@content.Preference(
|
||||
onClick = { navController.navigate(
|
||||
Routes.Ext.Import(ExtensionImportScreenType.EXT_THEME, null)
|
||||
) },
|
||||
icon = Icons.Default.Input,
|
||||
title = stringRes(R.string.action__import),
|
||||
)
|
||||
}
|
||||
}
|
||||
for ((extensionId, configs) in extGroupedThemes) key(extensionId) {
|
||||
val ext = extensionManager.getExtensionById(extensionId)!!
|
||||
FlorisOutlinedBox(
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
title = ext.meta.title,
|
||||
onTitleClick = { navController.navigate(Routes.Ext.View(extensionId)) },
|
||||
subtitle = extensionId,
|
||||
onSubtitleClick = { navController.navigate(Routes.Ext.View(extensionId)) },
|
||||
) {
|
||||
for (config in configs) key(extensionId, config.id) {
|
||||
JetPrefListItem(
|
||||
@@ -171,8 +116,8 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
},
|
||||
icon = {
|
||||
RadioButton(
|
||||
selected = activeThemeId?.extensionId == extensionId &&
|
||||
activeThemeId?.componentId == config.id,
|
||||
selected = activeThemeId.extensionId == extensionId &&
|
||||
activeThemeId.componentId == config.id,
|
||||
onClick = null,
|
||||
)
|
||||
},
|
||||
@@ -191,51 +136,7 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
},
|
||||
)
|
||||
}
|
||||
if (action == ThemeManagerScreenAction.MANAGE && extensionManager.canDelete(ext)) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 6.dp),
|
||||
) {
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
themeExtToDelete = ext
|
||||
},
|
||||
icon = Icons.Default.Delete,
|
||||
text = stringRes(R.string.action__delete),
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
contentColor = MaterialTheme.colors.error,
|
||||
),
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
navController.navigate(Routes.Ext.Edit(ext.meta.id))
|
||||
},
|
||||
icon = Icons.Default.Edit,
|
||||
text = stringRes(R.string.action__edit),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (themeExtToDelete != null) {
|
||||
FlorisConfirmDeleteDialog(
|
||||
onConfirm = {
|
||||
runCatching {
|
||||
extensionManager.delete(themeExtToDelete!!)
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(
|
||||
R.string.error__snackbar_message,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
}
|
||||
themeExtToDelete = null
|
||||
},
|
||||
onDismiss = { themeExtToDelete = null },
|
||||
what = themeExtToDelete!!.meta.title,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,35 +16,33 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.settings.theme
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.BrightnessAuto
|
||||
import androidx.compose.material.icons.filled.DarkMode
|
||||
import androidx.compose.material.icons.filled.FormatPaint
|
||||
import androidx.compose.material.icons.filled.LightMode
|
||||
import androidx.compose.material.icons.outlined.Palette
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
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.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
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.android.launchUrl
|
||||
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.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
|
||||
@Composable
|
||||
fun ThemeScreen() = FlorisScreen {
|
||||
@@ -53,13 +51,21 @@ fun ThemeScreen() = FlorisScreen {
|
||||
|
||||
val context = LocalContext.current
|
||||
val navController = LocalNavController.current
|
||||
val themeManager by context.themeManager()
|
||||
|
||||
@Composable
|
||||
fun ThemeManager.getThemeLabel(id: ExtensionComponentName): String {
|
||||
val configs by indexedThemeConfigs.observeAsState()
|
||||
configs?.get(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)) {
|
||||
/*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 = {
|
||||
@@ -68,13 +74,13 @@ fun ThemeScreen() = FlorisScreen {
|
||||
Text("Open Feedback Thread")
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
ListPreference(
|
||||
prefs.theme.mode,
|
||||
icon = Icons.Default.BrightnessAuto,
|
||||
title = stringRes(R.string.pref__theme__mode__label),
|
||||
entries = ThemeMode.listEntries(),
|
||||
entries = enumDisplayEntriesOf(ThemeMode::class),
|
||||
)
|
||||
if (themeMode == ThemeMode.FOLLOW_TIME) {
|
||||
FlorisInfoCard(
|
||||
@@ -85,53 +91,24 @@ fun ThemeScreen() = FlorisScreen {
|
||||
)
|
||||
}
|
||||
Preference(
|
||||
icon = Icons.Outlined.Palette,
|
||||
title = stringRes(R.string.settings__theme_manager__title_manage),
|
||||
icon = Icons.Default.LightMode,
|
||||
title = stringRes(R.string.pref__theme__day),
|
||||
summary = themeManager.getThemeLabel(dayThemeId),
|
||||
enabledIf = { prefs.theme.mode isNotEqualTo ThemeMode.ALWAYS_NIGHT },
|
||||
onClick = {
|
||||
navController.navigate(Routes.Settings.ThemeManager(ThemeManagerScreenAction.MANAGE))
|
||||
navController.navigate(Routes.Settings.ThemeManager(ThemeManagerScreenAction.SELECT_DAY))
|
||||
},
|
||||
)
|
||||
Preference(
|
||||
icon = Icons.Default.DarkMode,
|
||||
title = stringRes(R.string.pref__theme__night),
|
||||
summary = themeManager.getThemeLabel(nightThemeId),
|
||||
enabledIf = { prefs.theme.mode isNotEqualTo ThemeMode.ALWAYS_DAY },
|
||||
onClick = {
|
||||
navController.navigate(Routes.Settings.ThemeManager(ThemeManagerScreenAction.SELECT_NIGHT))
|
||||
},
|
||||
)
|
||||
|
||||
PreferenceGroup(
|
||||
title = stringRes(R.string.pref__theme__day),
|
||||
enabledIf = { prefs.theme.mode isNotEqualTo ThemeMode.ALWAYS_NIGHT },
|
||||
) {
|
||||
Preference(
|
||||
icon = Icons.Default.LightMode,
|
||||
title = stringRes(R.string.pref__theme__any_theme__label),
|
||||
summary = dayThemeId.toString(),
|
||||
onClick = {
|
||||
navController.navigate(Routes.Settings.ThemeManager(ThemeManagerScreenAction.SELECT_DAY))
|
||||
},
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.theme.dayThemeAdaptToApp,
|
||||
icon = Icons.Default.FormatPaint,
|
||||
title = stringRes(R.string.pref__theme__any_theme_adapt_to_app__label),
|
||||
summary = stringRes(R.string.pref__theme__any_theme_adapt_to_app__summary),
|
||||
visibleIf = { false },
|
||||
)
|
||||
}
|
||||
|
||||
PreferenceGroup(
|
||||
title = stringRes(R.string.pref__theme__night),
|
||||
enabledIf = { prefs.theme.mode isNotEqualTo ThemeMode.ALWAYS_DAY },
|
||||
) {
|
||||
Preference(
|
||||
icon = Icons.Default.DarkMode,
|
||||
title = stringRes(R.string.pref__theme__any_theme__label),
|
||||
summary = nightThemeId.toString(),
|
||||
onClick = {
|
||||
navController.navigate(Routes.Settings.ThemeManager(ThemeManagerScreenAction.SELECT_NIGHT))
|
||||
},
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.theme.nightThemeAdaptToApp,
|
||||
icon = Icons.Default.FormatPaint,
|
||||
title = stringRes(R.string.pref__theme__any_theme_adapt_to_app__label),
|
||||
summary = stringRes(R.string.pref__theme__any_theme_adapt_to_app__summary),
|
||||
visibleIf = { false },
|
||||
)
|
||||
}
|
||||
AddonManagementReferenceBox(type = ExtensionListScreenType.EXT_THEME)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,27 +23,27 @@ import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.lib.UnicodeCtrlChar
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.snygg.Snygg
|
||||
import dev.patrickgold.florisboard.lib.snygg.SnyggLevel
|
||||
import dev.patrickgold.florisboard.lib.snygg.SnyggRule
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.RgbaColor
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggCircleShapeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggCutCornerDpShapeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggCutCornerPercentShapeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggDefinedVarValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggDpSizeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggExplicitInheritValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggImplicitInheritValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggMaterialYouDarkColorValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggMaterialYouLightColorValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggPercentageSizeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggRectangleShapeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggRoundedCornerDpShapeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggRoundedCornerPercentShapeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggSolidColorValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggSpSizeValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggValue
|
||||
import dev.patrickgold.florisboard.lib.snygg.value.SnyggValueEncoder
|
||||
import org.florisboard.lib.snygg.Snygg
|
||||
import org.florisboard.lib.snygg.SnyggLevel
|
||||
import org.florisboard.lib.snygg.SnyggRule
|
||||
import org.florisboard.lib.snygg.value.RgbaColor
|
||||
import org.florisboard.lib.snygg.value.SnyggCircleShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggCutCornerDpShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggCutCornerPercentShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDefinedVarValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDpSizeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggExplicitInheritValue
|
||||
import org.florisboard.lib.snygg.value.SnyggImplicitInheritValue
|
||||
import org.florisboard.lib.snygg.value.SnyggMaterialYouDarkColorValue
|
||||
import org.florisboard.lib.snygg.value.SnyggMaterialYouLightColorValue
|
||||
import org.florisboard.lib.snygg.value.SnyggPercentageSizeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggRectangleShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggRoundedCornerDpShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggRoundedCornerPercentShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggSolidColorValue
|
||||
import org.florisboard.lib.snygg.value.SnyggSpSizeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggValue
|
||||
import org.florisboard.lib.snygg.value.SnyggValueEncoder
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -23,25 +23,25 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.HelpOutline
|
||||
import androidx.compose.material.icons.automirrored.filled.HelpOutline
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.android.AndroidSettings
|
||||
import dev.patrickgold.florisboard.lib.android.launchActivity
|
||||
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
|
||||
|
||||
@Composable
|
||||
fun SpellCheckerServiceSelector(florisSpellCheckerEnabled: MutableState<Boolean>) {
|
||||
@@ -106,7 +106,7 @@ fun SpellCheckerServiceSelector(florisSpellCheckerEnabled: MutableState<Boolean>
|
||||
modifier = Modifier
|
||||
.padding(end = 8.dp)
|
||||
.requiredSize(32.dp),
|
||||
imageVector = Icons.Default.HelpOutline,
|
||||
imageVector = Icons.AutoMirrored.Filled.HelpOutline,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,13 +18,13 @@ package dev.patrickgold.florisboard.app.settings.typing
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.LibraryBooks
|
||||
import androidx.compose.material.icons.filled.Contacts
|
||||
import androidx.compose.material.icons.filled.Language
|
||||
import androidx.compose.material.icons.filled.LibraryBooks
|
||||
import androidx.compose.material.icons.filled.SpaceBar
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -32,8 +32,11 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
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.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.nlp.SpellingLanguageMode
|
||||
import dev.patrickgold.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisErrorCard
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisHyperlinkText
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
@@ -42,6 +45,7 @@ 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
|
||||
|
||||
@@ -51,6 +55,8 @@ fun TypingScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.settings__typing__title)
|
||||
previewFieldVisible = true
|
||||
|
||||
val navController = LocalNavController.current
|
||||
|
||||
content {
|
||||
// This card is temporary and is therefore not using a string resource
|
||||
FlorisErrorCard(
|
||||
@@ -146,7 +152,7 @@ fun TypingScreen() = FlorisScreen {
|
||||
prefs.spelling.languageMode,
|
||||
icon = Icons.Default.Language,
|
||||
title = stringRes(R.string.pref__spelling__language_mode__label),
|
||||
entries = SpellingLanguageMode.listEntries(),
|
||||
entries = enumDisplayEntriesOf(SpellingLanguageMode::class),
|
||||
enabledIf = { florisSpellCheckerEnabled.value },
|
||||
)
|
||||
SwitchPreference(
|
||||
@@ -159,12 +165,20 @@ fun TypingScreen() = FlorisScreen {
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.spelling.useUdmEntries,
|
||||
icon = Icons.Default.LibraryBooks,
|
||||
icon = Icons.AutoMirrored.Filled.LibraryBooks,
|
||||
title = stringRes(R.string.pref__spelling__use_udm_entries__label),
|
||||
summary = stringRes(R.string.pref__spelling__use_udm_entries__summary),
|
||||
enabledIf = { florisSpellCheckerEnabled.value },
|
||||
visibleIf = { false }, // For now
|
||||
)
|
||||
}
|
||||
|
||||
PreferenceGroup(title = stringRes(R.string.settings__dictionary__title)) {
|
||||
Preference(
|
||||
icon = Icons.AutoMirrored.Filled.LibraryBooks,
|
||||
title = stringRes(R.string.settings__dictionary__title),
|
||||
onClick = { navController.navigate(Routes.Settings.Dictionary) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -45,9 +45,9 @@ import dev.patrickgold.florisboard.app.FlorisAppActivity
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.lib.android.AndroidVersion
|
||||
import dev.patrickgold.florisboard.lib.android.launchActivity
|
||||
import dev.patrickgold.florisboard.lib.android.launchUrl
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import dev.patrickgold.florisboard.lib.util.launchActivity
|
||||
import dev.patrickgold.florisboard.lib.util.launchUrl
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisBulletSpacer
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreenScope
|
||||
|
||||
@@ -46,18 +46,18 @@ import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
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.ToggleOff
|
||||
import androidx.compose.material.icons.filled.ToggleOn
|
||||
import androidx.compose.material.icons.filled.Videocam
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -81,7 +81,6 @@ import androidx.compose.ui.text.style.TextDirection
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.apptheme.Green500
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.ime.ImeUiMode
|
||||
@@ -89,13 +88,11 @@ import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardItem
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ItemType
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.media.KeyboardLikeButton
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.android.AndroidKeyguardManager
|
||||
import dev.patrickgold.florisboard.lib.android.AndroidVersion
|
||||
import dev.patrickgold.florisboard.lib.android.showShortToast
|
||||
import dev.patrickgold.florisboard.lib.android.systemService
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButtonWithInnerPadding
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisStaggeredVerticalGrid
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisTextButton
|
||||
@@ -105,16 +102,21 @@ import dev.patrickgold.florisboard.lib.compose.rippleClickable
|
||||
import dev.patrickgold.florisboard.lib.compose.safeTimes
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.florisboard.lib.snygg.SnyggPropertySet
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.SnyggSurface
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.snyggBackground
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.snyggBorder
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.snyggClip
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.snyggShadow
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.solidColor
|
||||
import dev.patrickgold.florisboard.lib.snygg.ui.spSize
|
||||
import dev.patrickgold.florisboard.lib.util.NetworkUtils
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.android.AndroidKeyguardManager
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.systemService
|
||||
import org.florisboard.lib.snygg.SnyggPropertySet
|
||||
import org.florisboard.lib.snygg.ui.SnyggButton
|
||||
import org.florisboard.lib.snygg.ui.SnyggSurface
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import org.florisboard.lib.snygg.ui.snyggBorder
|
||||
import org.florisboard.lib.snygg.ui.snyggClip
|
||||
import org.florisboard.lib.snygg.ui.snyggShadow
|
||||
import org.florisboard.lib.snygg.ui.solidColor
|
||||
import org.florisboard.lib.snygg.ui.spSize
|
||||
|
||||
private val ContentPadding = PaddingValues(horizontal = 4.dp)
|
||||
private val ItemMargin = PaddingValues(all = 6.dp)
|
||||
@@ -144,6 +146,7 @@ fun ClipboardInputLayout(
|
||||
val headerStyle = FlorisImeTheme.style.get(FlorisImeUi.ClipboardHeader)
|
||||
val itemStyle = FlorisImeTheme.style.get(FlorisImeUi.ClipboardItem)
|
||||
val popupStyle = FlorisImeTheme.style.get(FlorisImeUi.ClipboardItemPopup)
|
||||
val enableHistoryButtonStyle = FlorisImeTheme.style.get(FlorisImeUi.ClipboardEnableHistoryButton)
|
||||
|
||||
fun isPopupSurfaceActive() = popupItem != null || showClearAllHistory
|
||||
|
||||
@@ -158,8 +161,7 @@ fun ClipboardInputLayout(
|
||||
) {
|
||||
FlorisIconButtonWithInnerPadding(
|
||||
onClick = { keyboardManager.activeState.imeUiMode = ImeUiMode.TEXT },
|
||||
modifier = Modifier.autoMirrorForRtl(),
|
||||
icon = Icons.Default.ArrowBack,
|
||||
icon = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
iconColor = headerStyle.foreground.solidColor(context),
|
||||
)
|
||||
Text(
|
||||
@@ -194,6 +196,13 @@ fun ClipboardInputLayout(
|
||||
iconColor = headerStyle.foreground.solidColor(context),
|
||||
enabled = !deviceLocked && historyEnabled && !isPopupSurfaceActive(),
|
||||
)
|
||||
KeyboardLikeButton(
|
||||
inputEventDispatcher = keyboardManager.inputEventDispatcher,
|
||||
keyData = TextKeyData.DELETE,
|
||||
element = FlorisImeUi.ClipboardHeader,
|
||||
) {
|
||||
Icon(Icons.AutoMirrored.Outlined.Backspace, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +222,7 @@ fun ClipboardInputLayout(
|
||||
clip = true,
|
||||
clickAndSemanticsModifier = Modifier.combinedClickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = rememberRipple(),
|
||||
indication = ripple(),
|
||||
enabled = popupItem == null,
|
||||
onLongClick = {
|
||||
popupItem = item
|
||||
@@ -308,7 +317,7 @@ fun ClipboardInputLayout(
|
||||
.fillMaxWidth()
|
||||
.run { if (contentScrollInsteadOfClip) this.florisVerticalScroll() else this }
|
||||
.padding(ItemPadding),
|
||||
text = text,
|
||||
text = item.displayText(),
|
||||
style = TextStyle(textDirection = TextDirection.ContentOrLtr),
|
||||
color = style.foreground.solidColor(context),
|
||||
fontSize = style.fontSize.spSize(),
|
||||
@@ -537,21 +546,14 @@ fun ClipboardInputLayout(
|
||||
color = itemStyle.foreground.solidColor(context),
|
||||
fontSize = itemStyle.fontSize.spSize(),
|
||||
)
|
||||
Button(
|
||||
SnyggButton(
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp)
|
||||
.align(Alignment.End),
|
||||
onClick = { prefs.clipboard.historyEnabled.set(true) },
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
backgroundColor = Green500,
|
||||
contentColor = Color.White,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(R.string.clipboard__disabled__enable_button),
|
||||
fontSize = itemStyle.fontSize.spSize(),
|
||||
)
|
||||
}
|
||||
style = enableHistoryButtonStyle,
|
||||
text = stringRes(R.string.clipboard__disabled__enable_button)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -585,7 +587,7 @@ fun ClipboardInputLayout(
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight(),
|
||||
.height(FlorisImeSizing.imeUiHeight()),
|
||||
) {
|
||||
HeaderRow()
|
||||
if (deviceLocked) {
|
||||
|
||||
@@ -28,11 +28,6 @@ import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardHistoryDao
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardHistoryDatabase
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardItem
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ItemType
|
||||
import dev.patrickgold.florisboard.lib.android.AndroidClipboardManager
|
||||
import dev.patrickgold.florisboard.lib.android.AndroidClipboardManager_OnPrimaryClipChangedListener
|
||||
import dev.patrickgold.florisboard.lib.android.setOrClearPrimaryClip
|
||||
import dev.patrickgold.florisboard.lib.android.showShortToast
|
||||
import dev.patrickgold.florisboard.lib.android.systemService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@@ -45,6 +40,11 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.florisboard.lib.android.AndroidClipboardManager
|
||||
import org.florisboard.lib.android.AndroidClipboardManager_OnPrimaryClipChangedListener
|
||||
import org.florisboard.lib.android.setOrClearPrimaryClip
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.systemService
|
||||
import org.florisboard.lib.kotlin.tryOrNull
|
||||
import java.io.Closeable
|
||||
|
||||
@@ -110,7 +110,9 @@ class ClipboardManager(
|
||||
val primaryClipFlow = _primaryClipFlow.asStateFlow()
|
||||
inline var primaryClip
|
||||
get() = primaryClipFlow.value
|
||||
private set(v) { _primaryClipFlow.value = v }
|
||||
private set(v) {
|
||||
_primaryClipFlow.value = v
|
||||
}
|
||||
|
||||
init {
|
||||
systemClipboardManager.addPrimaryClipChangedListener(this)
|
||||
@@ -278,6 +280,9 @@ class ClipboardManager(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all unpinned items from the clipboard history
|
||||
*/
|
||||
fun clearHistory() {
|
||||
ioScope.launch {
|
||||
for (item in history().all) {
|
||||
@@ -287,6 +292,9 @@ class ClipboardManager(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the full clipboard history
|
||||
*/
|
||||
fun clearFullHistory() {
|
||||
ioScope.launch {
|
||||
for (item in history().all) {
|
||||
@@ -296,6 +304,23 @@ class ClipboardManager(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Restore the clipboard history from a [List]
|
||||
*
|
||||
* @param items the [ClipboardItem] list with the new items
|
||||
*/
|
||||
fun restoreHistory(items: List<ClipboardItem>) {
|
||||
ioScope.launch {
|
||||
val currentHistory = this@ClipboardManager.history().all
|
||||
for (item in items) {
|
||||
if (!currentHistory.map { it.copy(id = 0) }.contains(item.copy(id = 0))) {
|
||||
this@ClipboardManager.insertClip(item.copy(id = 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteClip(item: ClipboardItem) {
|
||||
ioScope.launch {
|
||||
clipHistoryDao?.delete(item)
|
||||
@@ -316,7 +341,7 @@ class ClipboardManager(
|
||||
|
||||
fun unpinClip(item: ClipboardItem) {
|
||||
ioScope.launch {
|
||||
clipHistoryDao?.update(item.copy(isPinned = false))
|
||||
clipHistoryDao?.update(item.copy(isPinned = false))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
package dev.patrickgold.florisboard.ime.clipboard
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.apptheme.FlorisAppTheme
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.lib.compose.ProvideLocalizedResources
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.android.AndroidClipboardManager
|
||||
import org.florisboard.lib.android.stringRes
|
||||
import org.florisboard.lib.android.systemService
|
||||
|
||||
class FlorisCopyToClipboardActivity : ComponentActivity() {
|
||||
private var error: CopyToClipboardError? = null
|
||||
private var bitmap: Bitmap? = null
|
||||
|
||||
internal enum class CopyToClipboardError {
|
||||
UNKNOWN_ERROR,
|
||||
ANDROID_VERSION_TO_OLD_ERROR,
|
||||
TYPE_NOT_SUPPORTED_ERROR;
|
||||
|
||||
@Composable
|
||||
fun showError(): String {
|
||||
val textId = when (this) {
|
||||
UNKNOWN_ERROR -> R.string.send_to_clipboard__unknown_error
|
||||
TYPE_NOT_SUPPORTED_ERROR -> R.string.send_to_clipboard__type_not_supported_error
|
||||
ANDROID_VERSION_TO_OLD_ERROR -> R.string.send_to_clipboard__android_version_to_old_error
|
||||
}
|
||||
return stringRes(id = textId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
finish()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val systemClipboardManager = this.systemService(AndroidClipboardManager::class)
|
||||
val type = intent.type
|
||||
val action = intent.action
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
|
||||
if (Intent.ACTION_SEND != action || type == null) {
|
||||
error = CopyToClipboardError.UNKNOWN_ERROR
|
||||
} else {
|
||||
if (type.startsWith("image/")) {
|
||||
val hasExtraStream = intent.hasExtra(Intent.EXTRA_STREAM)
|
||||
if (!hasExtraStream) {
|
||||
error = CopyToClipboardError.TYPE_NOT_SUPPORTED_ERROR
|
||||
} else {
|
||||
// pasting images via virtual keyboard only available since Android 7.1 (API 25)
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) {
|
||||
error = CopyToClipboardError.ANDROID_VERSION_TO_OLD_ERROR
|
||||
} else {
|
||||
val uri: Uri? =
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
@Suppress("DEPRECATION")
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM)
|
||||
} else {
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
|
||||
}
|
||||
val clip = ClipData.newUri(contentResolver, "image", uri)
|
||||
systemClipboardManager.setPrimaryClip(clip)
|
||||
bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, uri)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error = CopyToClipboardError.TYPE_NOT_SUPPORTED_ERROR
|
||||
}
|
||||
}
|
||||
|
||||
setContent {
|
||||
ProvideLocalizedResources(this, forceLayoutDirection = LayoutDirection.Ltr) {
|
||||
val theme by prefs.advanced.settingsTheme.observeAsState()
|
||||
val isMaterialYouAware by prefs.advanced.useMaterialYou.observeAsState()
|
||||
FlorisAppTheme(theme, isMaterialYouAware) {
|
||||
BottomSheet {
|
||||
Row {
|
||||
Text(
|
||||
text = error?.showError()
|
||||
?: bitmap?.let { stringRes(id = R.string.send_to_clipboard__description__copied_image_to_clipboard) }
|
||||
?: stringRes(R.string.send_to_clipboard__unknown_error),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
bitmap?.let {
|
||||
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.padding(start = 64.dp, end = 64.dp, top = 32.dp, bottom = 8.dp),
|
||||
bitmap = bitmap!!.asImageBitmap(),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
internal fun BottomSheet(
|
||||
content: @Composable ColumnScope.() -> Unit,
|
||||
) {
|
||||
ModalBottomSheet(
|
||||
modifier = Modifier.navigationBarsPadding(),
|
||||
onDismissRequest = { finish() }
|
||||
) {
|
||||
Column {
|
||||
content()
|
||||
Button(
|
||||
modifier = Modifier.align(Alignment.End).padding(16.dp),
|
||||
onClick = { finish() },
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
//containerColor = buttonContainer.background.solidColor(context = context),
|
||||
)
|
||||
) {
|
||||
Text(text = stringRes(id = R.string.action__ok))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@
|
||||
package dev.patrickgold.florisboard.ime.clipboard.provider
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipDescription.EXTRA_IS_REMOTE_DEVICE
|
||||
import android.content.ClipDescription.EXTRA_IS_SENSITIVE
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
@@ -24,6 +26,8 @@ import android.net.Uri
|
||||
import android.provider.BaseColumns
|
||||
import android.provider.MediaStore.Images.Media
|
||||
import android.provider.OpenableColumns
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.database.getStringOrNull
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.ColumnInfo
|
||||
@@ -39,7 +43,14 @@ import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverter
|
||||
import androidx.room.TypeConverters
|
||||
import androidx.room.Update
|
||||
import dev.patrickgold.florisboard.lib.android.query
|
||||
import dev.patrickgold.florisboard.R
|
||||
import kotlinx.serialization.EncodeDefault
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.UriSerializer
|
||||
import org.florisboard.lib.android.query
|
||||
import org.florisboard.lib.android.stringRes
|
||||
import org.florisboard.lib.kotlin.tryOrNull
|
||||
|
||||
private const val CLIPBOARD_HISTORY_TABLE = "clipboard_history"
|
||||
@@ -63,17 +74,23 @@ enum class ItemType(val value: Int) {
|
||||
* If type == ItemType.IMAGE there must be a uri set
|
||||
* if type == ItemType.TEXT there must be a text set
|
||||
*/
|
||||
@Serializable
|
||||
@Entity(tableName = CLIPBOARD_HISTORY_TABLE)
|
||||
data class ClipboardItem(
|
||||
data class ClipboardItem @OptIn(ExperimentalSerializationApi::class) constructor(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = BaseColumns._ID, index = true)
|
||||
var id: Long = 0,
|
||||
val type: ItemType,
|
||||
val text: String?,
|
||||
@Serializable(with = UriSerializer::class)
|
||||
val uri: Uri?,
|
||||
val creationTimestampMs: Long,
|
||||
val isPinned: Boolean,
|
||||
val mimeTypes: Array<String>,
|
||||
@EncodeDefault
|
||||
val isSensitive: Boolean = false,
|
||||
@EncodeDefault
|
||||
val isRemoteDevice: Boolean = false,
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
@@ -109,6 +126,18 @@ data class ClipboardItem(
|
||||
else -> ItemType.TEXT
|
||||
}
|
||||
|
||||
val isSensitive = if (AndroidVersion.ATLEAST_API33_T) {
|
||||
data.description?.extras?.getBoolean(EXTRA_IS_SENSITIVE) ?: false
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
val isRemoteDevice = if (AndroidVersion.ATLEAST_API34_U) {
|
||||
data.description?.extras?.getBoolean(EXTRA_IS_REMOTE_DEVICE) ?: false
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
val uri = if (type == ItemType.IMAGE || type == ItemType.VIDEO) {
|
||||
if (dataItem.uri.authority == ClipboardMediaProvider.AUTHORITY || !cloneUri) {
|
||||
dataItem.uri
|
||||
@@ -147,7 +176,21 @@ data class ClipboardItem(
|
||||
}
|
||||
}
|
||||
|
||||
return ClipboardItem(0, type, text, uri, System.currentTimeMillis(), false, mimeTypes)
|
||||
return ClipboardItem(0, type, text, uri, System.currentTimeMillis(), false, mimeTypes, isSensitive, isRemoteDevice)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
inline fun displayText(): String {
|
||||
val context = LocalContext.current
|
||||
return displayText(context)
|
||||
}
|
||||
|
||||
fun displayText(context: Context): String {
|
||||
return if (isSensitive) {
|
||||
context.stringRes(R.string.clipboard__sensitive_clip_content)
|
||||
} else {
|
||||
stringRepresentation()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,11 +325,14 @@ interface ClipboardHistoryDao {
|
||||
@Query("DELETE FROM $CLIPBOARD_HISTORY_TABLE")
|
||||
fun deleteAll()
|
||||
|
||||
@Query("DELETE FROM $CLIPBOARD_HISTORY_TABLE WHERE type = :type")
|
||||
fun deleteAllFromType(type: ItemType)
|
||||
|
||||
@Query("DELETE FROM $CLIPBOARD_HISTORY_TABLE WHERE NOT isPinned")
|
||||
fun deleteAllUnpinned()
|
||||
}
|
||||
|
||||
@Database(entities = [ClipboardItem::class], version = 2)
|
||||
@Database(entities = [ClipboardItem::class], version = 3)
|
||||
@TypeConverters(Converters::class)
|
||||
abstract class ClipboardHistoryDatabase : RoomDatabase() {
|
||||
abstract fun clipboardItemDao(): ClipboardHistoryDao
|
||||
@@ -303,6 +349,7 @@ abstract class ClipboardHistoryDatabase : RoomDatabase() {
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@Entity(tableName = CLIPBOARD_FILES_TABLE)
|
||||
data class ClipboardFileInfo(
|
||||
@PrimaryKey @ColumnInfo(name=BaseColumns._ID, index=true) val id: Long,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user