Compare commits
115 Commits
v0.4.0-bet
...
v0.4.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ddc4f7f1ba | ||
|
|
afea8c721f | ||
|
|
7dedfd4f7a | ||
|
|
ef37194900 | ||
|
|
58134b1ceb | ||
|
|
53cfbad404 | ||
|
|
9cffcea246 | ||
|
|
5acf80db0f | ||
|
|
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 | ||
|
|
80fb20885b | ||
|
|
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 -->
|
||||
|
||||
@@ -16,6 +16,8 @@ This includes, but is not exclusive to:
|
||||
- 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)
|
||||
- - Upgrade Settings UI to Material 3 (v0.4.0-beta03)
|
||||
- Add support for importing extensions via system file handler APIs (relevant for Addons store) (v0.4.0-beta03)
|
||||
|
||||
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 :)
|
||||
|
||||
@@ -32,7 +34,6 @@ Note that the previous versioning scheme has been dropped in favor of using a ma
|
||||
- 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
|
||||
@@ -52,7 +53,6 @@ Note that the previous versioning scheme has been dropped in favor of using a ma
|
||||
|
||||
**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
|
||||
|
||||
@@ -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": "]" }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,49 @@
|
||||
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.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
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 +75,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 +94,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 +118,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 +144,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 +181,11 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.statusBarsPadding()
|
||||
//.statusBarsPadding()
|
||||
.navigationBarsPadding()
|
||||
.conditional(LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
displayCutoutPadding()
|
||||
}
|
||||
.imePadding(),
|
||||
) {
|
||||
Routes.AppNavHost(
|
||||
@@ -168,6 +198,24 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
SideEffect {
|
||||
navController.setOnBackPressedDispatcher(this.onBackPressedDispatcher)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -18,16 +18,18 @@ package dev.patrickgold.florisboard.ime.clipboard.provider
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import dev.patrickgold.florisboard.lib.android.readToFile
|
||||
import dev.patrickgold.florisboard.lib.devtools.LogTopic
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogDebug
|
||||
import dev.patrickgold.florisboard.lib.io.FsFile
|
||||
import dev.patrickgold.florisboard.lib.io.subFile
|
||||
import org.florisboard.lib.android.readToFile
|
||||
import org.florisboard.lib.kotlin.io.FsFile
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
|
||||
/**
|
||||
* Backend helper object which is used by [ClipboardMediaProvider] to serve content.
|
||||
*/
|
||||
object ClipboardFileStorage {
|
||||
const val CLIPBOARD_FILES_PATH = "clipboard_files"
|
||||
|
||||
private val Context.clipboardFilesDir: FsFile
|
||||
get() = FsFile(this.noBackupFilesDir, "clipboard_files").also { it.mkdirs() }
|
||||
|
||||
@@ -58,4 +60,29 @@ object ClipboardFileStorage {
|
||||
fun getFileForId(context: Context, id: Long): FsFile {
|
||||
return context.clipboardFilesDir.subFile(id.toString())
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Insert file from backup if not existing
|
||||
*
|
||||
* @param context the application context
|
||||
* @param file the file to be inserted
|
||||
*/
|
||||
fun insertFileFromBackupIfNotExisting(context: Context, file: FsFile) {
|
||||
if (!context.clipboardFilesDir.subFile(file.name).isFile) {
|
||||
file.copyTo(context.clipboardFilesDir.subFile(file.name), overwrite = false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all files from the clipboard subdirectory
|
||||
*
|
||||
* @param context the application context
|
||||
*/
|
||||
fun resetClipboardFileStorage(context: Context) {
|
||||
context.clipboardFilesDir.listFiles()?.forEach {
|
||||
it.delete()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user