Compare commits
50 Commits
v0.5.0-alp
...
test/inlin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11a41f30e4 | ||
|
|
c035e19cfc | ||
|
|
8d30403676 | ||
|
|
2886a24fcc | ||
|
|
352c98205c | ||
|
|
18c76b7195 | ||
|
|
ea0712225a | ||
|
|
1132eaf59b | ||
|
|
cba85f1c0e | ||
|
|
15dba9f33a | ||
|
|
3859528120 | ||
|
|
046d253382 | ||
|
|
c55a87862f | ||
|
|
ee08c58db2 | ||
|
|
05d4a5cf62 | ||
|
|
c5c8f7a4c3 | ||
|
|
ce3aee93d6 | ||
|
|
96340f7277 | ||
|
|
d0dcf5be38 | ||
|
|
15450a760e | ||
|
|
de0027d87e | ||
|
|
c7e83fca21 | ||
|
|
115dc5c42a | ||
|
|
c78cf84d6c | ||
|
|
5e59f144dd | ||
|
|
564c075763 | ||
|
|
86365d393b | ||
|
|
45a0c9ef63 | ||
|
|
25da6be1a4 | ||
|
|
976afc2e51 | ||
|
|
d994da8c97 | ||
|
|
40a22a762a | ||
|
|
264b9ff98b | ||
|
|
f1b7ddedb8 | ||
|
|
b1665f61e5 | ||
|
|
0fa663f658 | ||
|
|
b7f5bf267d | ||
|
|
8563f37c98 | ||
|
|
d3a2e50dda | ||
|
|
29562c9980 | ||
|
|
5bbb5cfbce | ||
|
|
4ada583418 | ||
|
|
200a195ae6 | ||
|
|
fa910692bc | ||
|
|
c3545d1625 | ||
|
|
300ca755ec | ||
|
|
3efb760787 | ||
|
|
647f7c106b | ||
|
|
d998f8fe0c | ||
|
|
b7020d7cc9 |
11
.dockerignore
Normal file
11
.dockerignore
Normal file
@@ -0,0 +1,11 @@
|
||||
**/.*/
|
||||
!.git/
|
||||
**/build/
|
||||
**/dist/
|
||||
**/out/
|
||||
**/target/
|
||||
utils/
|
||||
!utils/repr_build/scripts/
|
||||
.env
|
||||
gradlew.bat
|
||||
local.properties
|
||||
4
.github/workflows/android.yml
vendored
4
.github/workflows/android.yml
vendored
@@ -25,14 +25,14 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: gradle/actions/wrapper-validation@v3
|
||||
- uses: gradle/actions/wrapper-validation@v4
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
- name: Set up CMake and Ninja
|
||||
uses: lukka/get-cmake@latest
|
||||
uses: lukka/get-cmake@v4.0.2
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew clean assembleDebug
|
||||
- uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -64,7 +64,7 @@ fully respecting your privacy. Currently in early-beta state.
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Beginning with v0.6.0 FlorisBoard will enter the public beta on Google Play.
|
||||
Beginning with v0.7 FlorisBoard will enter the public beta on Google Play.
|
||||
|
||||
## Highlighted features
|
||||
- Integrated clipboard manager / history
|
||||
@@ -74,7 +74,7 @@ Beginning with v0.6.0 FlorisBoard will enter the public beta on Google Play.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Word suggestions/spell checking are not included in the current releases
|
||||
> and are a major goal for the v0.5 milestone.
|
||||
> and are a major goal for the v0.6 milestone.
|
||||
|
||||
Feature roadmap: See [ROADMAP.md](ROADMAP.md)
|
||||
|
||||
|
||||
45
ROADMAP.md
45
ROADMAP.md
@@ -6,34 +6,49 @@ Each major milestone has associated alpha/beta releases, so if you are intereste
|
||||
|
||||
## 0.5 (currently in development)
|
||||
|
||||
- [x] Theme rework part II / Snygg v2
|
||||
- See https://github.com/florisboard/florisboard/pull/2855
|
||||
> [!NOTE]
|
||||
> The milestone 0.5 was split, thus the word suggestions now come with version 0.6. The old version 0.6 has been moved down and is now 0.7. The time it takes to implement word suggestions will not change, but we can now release the new theme editor earlier, which would otherwise lie dormant.
|
||||
|
||||
- [ ] Theme rework part II / Snygg v2
|
||||
- [x] See https://github.com/florisboard/florisboard/pull/2855
|
||||
- [x] Spaces in URI bug (See https://github.com/florisboard/florisboard/issues/2898)
|
||||
- [ ] Rework cache manager (See https://github.com/florisboard/florisboard/issues/2870)
|
||||
- [x] Re-add time based theme switching (See https://github.com/florisboard/florisboard/pull/2977)
|
||||
- [ ] Add support for any remaining new features introduced with Android 13 / 14
|
||||
- [ ] Proper physical keyboard support (See https://github.com/florisboard/florisboard/issues/2815)
|
||||
- [x] Raise minimum required Android version from Android 7 (SDK level 24) to Android 8 (SDK level 26)
|
||||
|
||||
## 0.6
|
||||
|
||||
- [ ] Implement predictive text support / spell checking
|
||||
- [ ] Add new extension type: Language Pack
|
||||
- Basically groups all locale-relevant data (predictive base model, emoji suggestion data, ...)
|
||||
in a dynamically importable extension file
|
||||
- [ ] Add support for any remaining new features introduced with Android 13 / 14
|
||||
- [x] Raise minimum required Android version from Android 7 (SDK level 24) to Android 8 (SDK level 26)
|
||||
- Basically groups all locale-relevant data (predictive base model, emoji suggestion data, ...)
|
||||
in a dynamically importable extension file
|
||||
|
||||
## k3lp
|
||||
|
||||
> [!NOTE]
|
||||
> The development of k3lp is not tied to a florisboard version and takes place on [codeberg.org](https://codeberg.org/k3lp/k3lp) simultaneously.
|
||||
|
||||
- [ ] New keyboard layout engine + file syntax based on the upcoming Unicode Keyboard v3 standard
|
||||
- [ ] Add Tablet mode / Optimizations for landscape input based on new keyboard layout engine
|
||||
- [ ] Not bound to a specific FlorisBoard version
|
||||
|
||||
## 0.6
|
||||
## 0.7+
|
||||
|
||||
> [!NOTE]
|
||||
> From 0.6 onwards we plan to have more stable 0.X releases but with at most one large feature per release, thus having a much quicker iteration of new features on the stable track, which is a benefit for everyone involved.
|
||||
|
||||
- [ ] Add floating keyboard mode
|
||||
- [ ] New text processing logic
|
||||
- [ ] Complete rework of the Emoji panel
|
||||
- Emoji search
|
||||
- Fully scrollable emoji list (soft category borders)
|
||||
- Side scrollable emoji list (swipe for next category)
|
||||
- More granular theming options
|
||||
- Layout customization (e.g. placement of category buttons)
|
||||
- Maybe: consider upgrading to emoji2 for better unified system-wide emoji styles
|
||||
- [ ] Emoji search
|
||||
- [ ] Fully scrollable emoji list (soft category borders)
|
||||
- [ ] Side scrollable emoji list (swipe for next category)
|
||||
- [ ] More granular theming options
|
||||
- [ ] Layout customization (e.g. placement of category buttons)
|
||||
- [ ] Maybe: consider upgrading to emoji2 for better unified system-wide emoji styles
|
||||
- [ ] Reimplementation of glide typing with the new layout engine and predictive text core
|
||||
- [ ] Prepare FlorisBoard repository and app store presence for public beta release on Google Play (will go live with stable 0.6)
|
||||
- [ ] Prepare FlorisBoard repository and app store presence for public beta release on Google Play (will go live with stable 0.7)
|
||||
- [ ] Rework branding images and texts of FlorisBoard for the app stores
|
||||
- [ ] Focus on stability and experience improvements of the app and keyboard
|
||||
- [ ] Add support for new features introduced with Android 15 / 16
|
||||
|
||||
@@ -28,8 +28,6 @@ plugins {
|
||||
val projectMinSdk: String by project
|
||||
val projectTargetSdk: String by project
|
||||
val projectCompileSdk: String by project
|
||||
val projectBuildToolsVersion: String by project
|
||||
val projectNdkVersion: String by project
|
||||
val projectVersionCode: String by project
|
||||
val projectVersionName: String by project
|
||||
val projectVersionNameSuffix = projectVersionName.substringAfter("-", "").let { suffix ->
|
||||
@@ -43,8 +41,8 @@ val projectVersionNameSuffix = projectVersionName.substringAfter("-", "").let {
|
||||
android {
|
||||
namespace = "dev.patrickgold.florisboard"
|
||||
compileSdk = projectCompileSdk.toInt()
|
||||
buildToolsVersion = projectBuildToolsVersion
|
||||
ndkVersion = projectNdkVersion
|
||||
buildToolsVersion = tools.versions.buildTools.get()
|
||||
ndkVersion = tools.versions.ndk.get()
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
@@ -159,6 +157,10 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
lint {
|
||||
baseline = file("lint.xml")
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
isIncludeAndroidResources = true
|
||||
@@ -204,6 +206,7 @@ dependencies {
|
||||
implementation(libs.mikepenz.aboutlibraries.compose)
|
||||
implementation(libs.patrickgold.compose.tooltip)
|
||||
implementation(libs.patrickgold.jetpref.datastore.model)
|
||||
ksp(libs.patrickgold.jetpref.datastore.model.processor)
|
||||
implementation(libs.patrickgold.jetpref.datastore.ui)
|
||||
implementation(libs.patrickgold.jetpref.material.ui)
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<lint>
|
||||
<issue id="UsingMaterialAndMaterial3Libraries" severity="ignore" />
|
||||
<issue id="ExtraTranslation" severity="warning"/>
|
||||
</lint>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"label": "Armenian Alt Phonetic",
|
||||
"authors": [ "MikayelB" ],
|
||||
"direction": "ltr",
|
||||
"modifier": "org.florisboard.layouts:armenian_alt_phonetic"
|
||||
"modifier": "org.florisboard.layouts:armenian"
|
||||
},
|
||||
{
|
||||
"id": "western_armenian",
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 2535, "label": "১", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2536, "label": "২", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2537, "label": "৩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2538, "label": "৪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2539, "label": "৫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 2540, "label": "৬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2542, "label": "৮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2543, "label": "৯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2534, "label": "০", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
{ "code": 32902, "label": "肆" },
|
||||
{ "code": 18825, "label": "䦉" },
|
||||
{ "code": 20118, "label": "亖" },
|
||||
{ "code": 65300, "label": "3" }
|
||||
{ "code": 65300, "label": "4" }
|
||||
]
|
||||
} },
|
||||
{ "code": 20116, "label": "五", "type": "numeric", "popup": {
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 2407, "label": "१", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2408, "label": "२", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2409, "label": "३", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2410, "label": "४", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2411, "label": "५", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 2412, "label": "६", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2414, "label": "८", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2415, "label": "९", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2406, "label": "०", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 1633, "label": "١", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1634, "label": "٢", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1635, "label": "٣", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1636, "label": "٤", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1637, "label": "٥", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 1638, "label": "٦", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1640, "label": "٨", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1641, "label": "٩", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1632, "label": "٠", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 2791, "label": "૧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2792, "label": "૨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2793, "label": "૩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2794, "label": "૪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2795, "label": "૫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 2796, "label": "૬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2798, "label": "૮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2799, "label": "૯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2790, "label": "૦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 2663, "label": "੧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2664, "label": "੨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2665, "label": "੩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2666, "label": "੪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2667, "label": "੫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 2668, "label": "੬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2670, "label": "੮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2671, "label": "੯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2662, "label": "੦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 3303, "label": "೧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3304, "label": "೨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3305, "label": "೩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3306, "label": "೪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3307, "label": "೫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 3308, "label": "೬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3310, "label": "೮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3311, "label": "೯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3302, "label": "೦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 3431, "label": "൧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3432, "label": "൨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3433, "label": "൩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3434, "label": "൪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3435, "label": "൫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 3436, "label": "൬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3438, "label": "൮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3439, "label": "൯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3430, "label": "൦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 2919, "label": "୧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2920, "label": "୨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2921, "label": "୩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2922, "label": "୪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2923, "label": "୫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 2924, "label": "୬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2926, "label": "୮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2927, "label": "୯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 2918, "label": "୦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 1777, "label": "۱", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1778, "label": "۲", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1779, "label": "۳", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1780, "label": "۴", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1781, "label": "۵", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 1782, "label": "۶", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1784, "label": "۸", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1785, "label": "۹", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 1776, "label": "۰", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 3047, "label": "௧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3048, "label": "௨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3049, "label": "௩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3050, "label": "௪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3051, "label": "௫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 3052, "label": "௬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3054, "label": "௮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3055, "label": "௯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3046, "label": "௦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 3175, "label": "౧", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3176, "label": "౨", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3177, "label": "౩", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3178, "label": "౪", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3179, "label": "౫", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 3180, "label": "౬", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3182, "label": "౮", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3183, "label": "౯", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3174, "label": "౦", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,20 +3,23 @@
|
||||
{ "code": 3665, "label": "๑", "type": "numeric", "popup": {
|
||||
"main": { "code": 185, "label": "¹" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3666, "label": "๒", "type": "numeric", "popup": {
|
||||
"main": { "code": 178, "label": "²" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
@@ -24,44 +27,59 @@
|
||||
{ "code": 3667, "label": "๓", "type": "numeric", "popup": {
|
||||
"main": { "code": 179, "label": "³" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3668, "label": "๔", "type": "numeric", "popup": {
|
||||
"main": { "code": 8308, "label": "⁴" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3669, "label": "๕", "type": "numeric", "popup": {
|
||||
"main": { "code": 8309, "label": "⁵" },
|
||||
"relevant": [
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3670, "label": "๖", "type": "numeric", "popup": {
|
||||
"main": { "code": 8310, "label": "⁶" }
|
||||
"main": { "code": 8310, "label": "⁶" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3671, "label": "๗", "type": "numeric", "popup": {
|
||||
"main": { "code": 8311, "label": "⁷" },
|
||||
"relevant": [
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8542, "label": "⅞" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3672, "label": "๘", "type": "numeric", "popup": {
|
||||
"main": { "code": 8312, "label": "⁸" }
|
||||
"main": { "code": 8312, "label": "⁸" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3673, "label": "๙", "type": "numeric", "popup": {
|
||||
"main": { "code": 8313, "label": "⁹" }
|
||||
"main": { "code": 8313, "label": "⁹" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" }
|
||||
]
|
||||
} },
|
||||
{ "code": 3664, "label": "๐", "type": "numeric", "popup": {
|
||||
"main": { "code": 8304, "label": "⁰" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -3,46 +3,52 @@
|
||||
{ "code": 71905, "label": "𑣡", "type": "numeric", "popup": {
|
||||
"main": { "code": 49, "label": "1" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 185, "label": "¹" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71906, "label": "𑣢", "type": "numeric", "popup": {
|
||||
"main": { "code": 50, "label": "2" },
|
||||
"relevant": [
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 178, "label": "²" },
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71907, "label": "𑣣", "type": "numeric", "popup": {
|
||||
"main": { "code": 51, "label": "3" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 179, "label": "³" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 179, "label": "³" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71908, "label": "𑣤", "type": "numeric", "popup": {
|
||||
"main": { "code": 52, "label": "4" },
|
||||
"relevant": [
|
||||
{ "code": 8536, "label": "⅘" },
|
||||
{ "code": 8308, "label": "⁴" }
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8308, "label": "⁴" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71909, "label": "𑣥", "type": "numeric", "popup": {
|
||||
"main": { "code": 53, "label": "5" },
|
||||
"relevant": [
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8309, "label": "⁵" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -50,6 +56,7 @@
|
||||
{ "code": 71910, "label": "𑣦", "type": "numeric", "popup": {
|
||||
"main": { "code": 54, "label": "6" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" },
|
||||
{ "code": 8310, "label": "⁶" }
|
||||
]
|
||||
} },
|
||||
@@ -57,27 +64,32 @@
|
||||
"main": { "code": 55, "label": "7" },
|
||||
"relevant": [
|
||||
{ "code": 8542, "label": "⅞" },
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8311, "label": "⁷" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71912, "label": "𑣨", "type": "numeric", "popup": {
|
||||
"main": { "code": 56, "label": "8" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" },
|
||||
{ "code": 8312, "label": "⁸" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71913, "label": "𑣩", "type": "numeric", "popup": {
|
||||
"main": { "code": 57, "label": "9" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" },
|
||||
{ "code": 8313, "label": "⁹" }
|
||||
]
|
||||
} },
|
||||
{ "code": 71904, "label": "𑣠", "type": "numeric", "popup": {
|
||||
"main": { "code": 48, "label": "0" },
|
||||
"relevant": [
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8304, "label": "⁰" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8304, "label": "⁰" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
} }
|
||||
]
|
||||
|
||||
@@ -10,15 +10,17 @@
|
||||
"code": 49, "label": "1", "type": "numeric", "popup": {
|
||||
"main": { "code": 185, "label": "¹" },
|
||||
"relevant": [
|
||||
{ "code": 8537, "label": "⅙" },
|
||||
{ "code": 8321, "label": "₁" },
|
||||
{ "code": 8528, "label": "⅐" },
|
||||
{ "code": 8539, "label": "⅛" },
|
||||
{ "code": 8529, "label": "⅑" },
|
||||
{ "code": 8530, "label": "⅒" },
|
||||
{ "code": 8543, "label": "⅟" },
|
||||
{ "code": 189, "label": "½" },
|
||||
{ "code": 8531, "label": "⅓" },
|
||||
{ "code": 188, "label": "¼" },
|
||||
{ "code": 8533, "label": "⅕" }
|
||||
{ "code": 8533, "label": "⅕" },
|
||||
{ "code": 8537, "label": "⅙" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -29,6 +31,7 @@
|
||||
"code": 50, "label": "2", "type": "numeric", "popup": {
|
||||
"main": { "code": 178, "label": "²" },
|
||||
"relevant": [
|
||||
{ "code": 8322, "label": "₂"},
|
||||
{ "code": 8532, "label": "⅔" },
|
||||
{ "code": 8534, "label": "⅖" }
|
||||
]
|
||||
@@ -45,9 +48,10 @@
|
||||
"code": 51, "label": "3", "type": "numeric", "popup": {
|
||||
"main": { "code": 179, "label": "³" },
|
||||
"relevant": [
|
||||
{ "code": 8535, "label": "⅗" },
|
||||
{ "code": 8540, "label": "⅜" },
|
||||
{ "code": 8323, "label": "₃"},
|
||||
{ "code": 190, "label": "¾" },
|
||||
{ "code": 8540, "label": "⅜" }
|
||||
{ "code": 8535, "label": "⅗" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -68,6 +72,7 @@
|
||||
"code": 52, "label": "4", "type": "numeric", "popup": {
|
||||
"main": { "code": 8308, "label": "⁴" },
|
||||
"relevant": [
|
||||
{ "code": 8324, "label": "₄" },
|
||||
{ "code": 8536, "label": "⅘" }
|
||||
]
|
||||
}
|
||||
@@ -86,6 +91,7 @@
|
||||
"code": 53, "label": "5", "type": "numeric", "popup": {
|
||||
"main": { "code": 8309, "label": "⁵" },
|
||||
"relevant": [
|
||||
{ "code": 8325, "label": "₅" },
|
||||
{ "code": 8538, "label": "⅚" },
|
||||
{ "code": 8541, "label": "⅝" }
|
||||
]
|
||||
@@ -105,7 +111,10 @@
|
||||
},
|
||||
"default": {
|
||||
"code": 54, "label": "6", "type": "numeric", "popup": {
|
||||
"main": { "code": 8310, "label": "⁶" }
|
||||
"main": { "code": 8310, "label": "⁶" },
|
||||
"relevant": [
|
||||
{ "code": 8326, "label": "₆" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -115,6 +124,7 @@
|
||||
"code": 55, "label": "7", "type": "numeric", "popup": {
|
||||
"main": { "code": 8311, "label": "⁷" },
|
||||
"relevant": [
|
||||
{ "code": 8327, "label": "₇" },
|
||||
{ "code": 8542, "label": "⅞" }
|
||||
]
|
||||
}
|
||||
@@ -132,7 +142,10 @@
|
||||
},
|
||||
"default": {
|
||||
"code": 56, "label": "8", "type": "numeric", "popup": {
|
||||
"main": { "code": 8312, "label": "⁸" }
|
||||
"main": { "code": 8312, "label": "⁸" },
|
||||
"relevant": [
|
||||
{ "code": 8328, "label": "₈" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -160,7 +173,10 @@
|
||||
},
|
||||
"default": {
|
||||
"code": 57, "label": "9", "type": "numeric", "popup": {
|
||||
"main": { "code": 8313, "label": "⁹" }
|
||||
"main": { "code": 8313, "label": "⁹" },
|
||||
"relevant": [
|
||||
{ "code": 8329, "label": "₉" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -191,7 +207,9 @@
|
||||
"main": { "code": 8304, "label": "⁰" },
|
||||
"relevant": [
|
||||
{ "code": 8709, "label": "∅" },
|
||||
{ "code": 8319, "label": "ⁿ" }
|
||||
{ "code": 8319, "label": "ⁿ" },
|
||||
{ "code": 8585, "label": "↉" },
|
||||
{ "code": 8320, "label": "₀" }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,6 +401,16 @@
|
||||
"characters": "org.florisboard.layouts:qwertz"
|
||||
}
|
||||
},
|
||||
{
|
||||
"languageTag": "hy",
|
||||
"composer": "org.florisboard.composers:appender",
|
||||
"currencySet": "org.florisboard.currencysets:armenian_dram",
|
||||
"popupMapping": "org.florisboard.localization:hy",
|
||||
"preferred": {
|
||||
"characters": "org.florisboard.layouts:armenian_alt_phonetic",
|
||||
"symbols": "org.florisboard.layouts:armenian"
|
||||
}
|
||||
},
|
||||
{
|
||||
"languageTag": "hy",
|
||||
"composer": "org.florisboard.composers:appender",
|
||||
|
||||
@@ -23,8 +23,10 @@ import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Handler
|
||||
import android.util.Log
|
||||
import androidx.core.os.UserManagerCompat
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.clipboard.ClipboardManager
|
||||
import dev.patrickgold.florisboard.ime.core.SubtypeManager
|
||||
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
|
||||
@@ -40,7 +42,11 @@ import dev.patrickgold.florisboard.lib.devtools.Flog
|
||||
import dev.patrickgold.florisboard.lib.devtools.LogTopic
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogError
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import dev.patrickgold.jetpref.datastore.runtime.initAndroid
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
|
||||
import org.florisboard.lib.kotlin.tryOrNull
|
||||
import org.florisboard.libnative.dummyAdd
|
||||
@@ -63,8 +69,9 @@ class FlorisApplication : Application() {
|
||||
}
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val mainHandler by lazy { Handler(mainLooper) }
|
||||
private val scope = CoroutineScope(Dispatchers.Default)
|
||||
val preferenceStoreLoaded = MutableStateFlow(false)
|
||||
|
||||
val cacheManager = lazy { CacheManager(this) }
|
||||
val clipboardManager = lazy { ClipboardManager(this) }
|
||||
@@ -80,7 +87,6 @@ class FlorisApplication : Application() {
|
||||
super.onCreate()
|
||||
FlorisApplicationReference = WeakReference(this)
|
||||
try {
|
||||
JetPref.configure(saveIntervalMs = 500)
|
||||
Flog.install(
|
||||
context = this,
|
||||
isFloggingEnabled = BuildConfig.DEBUG,
|
||||
@@ -108,7 +114,14 @@ class FlorisApplication : Application() {
|
||||
|
||||
fun init() {
|
||||
cacheDir?.deleteContentsRecursively()
|
||||
prefs.initializeBlocking(this)
|
||||
scope.launch {
|
||||
val result = FlorisPreferenceStore.initAndroid(
|
||||
context = this@FlorisApplication,
|
||||
datastoreName = FlorisPreferenceModel.NAME,
|
||||
)
|
||||
Log.i("PREFS", result.toString())
|
||||
preferenceStoreLoaded.value = true
|
||||
}
|
||||
extensionManager.value.init()
|
||||
clipboardManager.value.initializeForContext(this)
|
||||
DictionaryManager.init(this)
|
||||
|
||||
@@ -75,8 +75,8 @@ import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import dev.patrickgold.florisboard.app.FlorisAppActivity
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.devtools.DevtoolsOverlay
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.ImeUiMode
|
||||
import dev.patrickgold.florisboard.ime.clipboard.ClipboardInputLayout
|
||||
import dev.patrickgold.florisboard.ime.core.SelectSubtypePanel
|
||||
@@ -119,8 +119,9 @@ import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.isOrientationLandscape
|
||||
import org.florisboard.lib.android.isOrientationPortrait
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.android.systemServiceOrNull
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
import org.florisboard.lib.kotlin.collectIn
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggButton
|
||||
import org.florisboard.lib.snygg.ui.SnyggRow
|
||||
@@ -246,12 +247,12 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
}
|
||||
}
|
||||
}
|
||||
ims.showShortToast("Failed to find voice IME, do you have one installed?")
|
||||
ims.showShortToastSync("Failed to find voice IME, do you have one installed?")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val editorInstance by editorInstance()
|
||||
private val keyboardManager by keyboardManager()
|
||||
private val nlpManager by nlpManager()
|
||||
@@ -277,20 +278,23 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
super.onCreate()
|
||||
FlorisImeServiceReference = WeakReference(this)
|
||||
WindowCompat.setDecorFitsSystemWindows(window.window!!, false)
|
||||
subtypeManager.activeSubtypeFlow.collectLatestIn(lifecycleScope) { subtype ->
|
||||
subtypeManager.activeSubtypeFlow.collectIn(lifecycleScope) { subtype ->
|
||||
val config = Configuration(resources.configuration)
|
||||
if (prefs.localization.displayKeyboardLabelsInSubtypeLanguage.get()) {
|
||||
config.setLocale(subtype.primaryLocale.base)
|
||||
}
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
prefs.localization.displayKeyboardLabelsInSubtypeLanguage.observeForever { shouldSync ->
|
||||
prefs.localization.displayKeyboardLabelsInSubtypeLanguage.asFlow().collectIn(lifecycleScope) { shouldSync ->
|
||||
val config = Configuration(resources.configuration)
|
||||
if (shouldSync) {
|
||||
config.setLocale(subtypeManager.activeSubtype.primaryLocale.base)
|
||||
}
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
prefs.physicalKeyboard.showOnScreenKeyboard.asFlow().collectIn(lifecycleScope) {
|
||||
updateInputViewShown()
|
||||
}
|
||||
@Suppress("DEPRECATION") // We do not retrieve the wallpaper but only listen to changes
|
||||
registerReceiver(wallpaperChangeReceiver, IntentFilter(Intent.ACTION_WALLPAPER_CHANGED))
|
||||
}
|
||||
@@ -356,6 +360,13 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEvaluateInputViewShown(): Boolean {
|
||||
val config = resources.configuration
|
||||
return super.onEvaluateInputViewShown()
|
||||
|| config.keyboard == Configuration.KEYBOARD_NOKEYS
|
||||
|| prefs.physicalKeyboard.showOnScreenKeyboard.get()
|
||||
}
|
||||
|
||||
override fun onUpdateSelection(
|
||||
oldSelStart: Int,
|
||||
oldSelEnd: Int,
|
||||
@@ -398,7 +409,6 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
flogInfo(LogTopic.IMS_EVENTS)
|
||||
}
|
||||
isWindowShown = true
|
||||
themeManager.updateActiveTheme()
|
||||
inputFeedbackController.updateSystemPrefsState()
|
||||
}
|
||||
|
||||
@@ -759,7 +769,8 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
SnyggBox(FlorisImeUi.ExtractedLandscapeInputLayout.elementName,
|
||||
SnyggBox(
|
||||
elementName = FlorisImeUi.ExtractedLandscapeInputLayout.elementName,
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(1f),
|
||||
|
||||
@@ -20,7 +20,7 @@ import android.service.textservice.SpellCheckerService
|
||||
import android.view.textservice.SentenceSuggestionsInfo
|
||||
import android.view.textservice.SuggestionsInfo
|
||||
import android.view.textservice.TextInfo
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
|
||||
import dev.patrickgold.florisboard.ime.nlp.SpellingLanguageMode
|
||||
@@ -33,7 +33,7 @@ import kotlinx.coroutines.runBlocking
|
||||
import org.florisboard.lib.kotlin.map
|
||||
|
||||
class FlorisSpellCheckerService : SpellCheckerService() {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val dictionaryManager get() = DictionaryManager.default()
|
||||
private val nlpManager by nlpManager()
|
||||
private val subtypeManager by subtypeManager()
|
||||
|
||||
@@ -57,7 +57,9 @@ import dev.patrickgold.florisboard.lib.compose.ColorPreferenceSerializer
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
|
||||
import dev.patrickgold.florisboard.lib.observeAsTransformingState
|
||||
import dev.patrickgold.florisboard.lib.util.VersionName
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import dev.patrickgold.jetpref.datastore.annotations.Preferences
|
||||
import dev.patrickgold.jetpref.datastore.jetprefDataStoreOf
|
||||
import dev.patrickgold.jetpref.datastore.model.LocalTime
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceData
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceMigrationEntry
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceModel
|
||||
@@ -68,9 +70,14 @@ import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.isOrientationPortrait
|
||||
import org.florisboard.lib.color.DEFAULT_GREEN
|
||||
|
||||
fun florisPreferenceModel() = JetPref.getOrCreatePreferenceModel(AppPrefs::class, ::AppPrefs)
|
||||
val FlorisPreferenceStore = jetprefDataStoreOf(FlorisPreferenceModel::class)
|
||||
|
||||
@Preferences
|
||||
abstract class FlorisPreferenceModel : PreferenceModel() {
|
||||
companion object {
|
||||
const val NAME = "florisboard-app-prefs"
|
||||
}
|
||||
|
||||
class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
val clipboard = Clipboard()
|
||||
inner class Clipboard {
|
||||
val useInternalClipboard = boolean(
|
||||
@@ -638,6 +645,14 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
)
|
||||
}
|
||||
|
||||
val physicalKeyboard = PhysicalKeyboard()
|
||||
inner class PhysicalKeyboard {
|
||||
val showOnScreenKeyboard = boolean(
|
||||
key = "physical_keyboard__show_on_screen_keyboard",
|
||||
default = false,
|
||||
)
|
||||
}
|
||||
|
||||
val smartbar = Smartbar()
|
||||
inner class Smartbar {
|
||||
val enabled = boolean(
|
||||
@@ -749,14 +764,14 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
},
|
||||
serializer = ColorPreferenceSerializer,
|
||||
)
|
||||
//val sunriseTime = localTime(
|
||||
// key = "theme__sunrise_time",
|
||||
// default = LocalTime.of(6, 0),
|
||||
//)
|
||||
//val sunsetTime = localTime(
|
||||
// key = "theme__sunset_time",
|
||||
// default = LocalTime.of(18, 0),
|
||||
//)
|
||||
val sunriseTime = localTime(
|
||||
key = "theme__sunrise_time",
|
||||
default = LocalTime(6, 0),
|
||||
)
|
||||
val sunsetTime = localTime(
|
||||
key = "theme__sunset_time",
|
||||
default = LocalTime(18, 0),
|
||||
)
|
||||
val editorColorRepresentation = enum(
|
||||
key = "theme__editor_color_representation",
|
||||
default = ColorRepresentation.HEX,
|
||||
@@ -819,12 +834,10 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
|
||||
//Migrate one hand mode prefs keep until: 0.7 dev cycle
|
||||
"keyboard__one_handed_mode" -> {
|
||||
if (entry.rawValue != "OFF") {
|
||||
val prefs by florisPreferenceModel()
|
||||
prefs.keyboard.oneHandedModeEnabled.set(true)
|
||||
entry.keepAsIs()
|
||||
} else {
|
||||
if (entry.rawValue == "OFF") {
|
||||
entry.reset()
|
||||
} else {
|
||||
entry.keepAsIs()
|
||||
}
|
||||
}
|
||||
"smartbar__action_arrangement" -> {
|
||||
|
||||
@@ -39,12 +39,14 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.apptheme.FlorisAppTheme
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionImportScreenType
|
||||
import dev.patrickgold.florisboard.app.setup.NotificationPermissionState
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.cacheManager
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import dev.patrickgold.florisboard.lib.compose.LocalPreviewFieldController
|
||||
@@ -59,6 +61,8 @@ import dev.patrickgold.jetpref.datastore.ui.ProvideDefaultDialogPrefStrings
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.hideAppIcon
|
||||
import org.florisboard.lib.android.showAppIcon
|
||||
import org.florisboard.lib.kotlin.collectIn
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
enum class AppTheme(val id: String) {
|
||||
AUTO("auto"),
|
||||
@@ -73,7 +77,8 @@ val LocalNavController = staticCompositionLocalOf<NavController> {
|
||||
}
|
||||
|
||||
class FlorisAppActivity : ComponentActivity() {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val appContext by appContext()
|
||||
private val cacheManager by cacheManager()
|
||||
private var appTheme by mutableStateOf(AppTheme.AUTO)
|
||||
private var showAppIcon = true
|
||||
@@ -83,37 +88,37 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// Splash screen should be installed before calling super.onCreate()
|
||||
installSplashScreen().apply {
|
||||
setKeepOnScreenCondition { !prefs.datastoreReadyStatus.get() }
|
||||
setKeepOnScreenCondition { !appContext.preferenceStoreLoaded.value }
|
||||
}
|
||||
super.onCreate(savedInstanceState)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
prefs.other.settingsTheme.observe(this) {
|
||||
prefs.other.settingsTheme.asFlow().collectIn(lifecycleScope) {
|
||||
appTheme = it
|
||||
}
|
||||
prefs.other.settingsLanguage.observe(this) {
|
||||
prefs.other.settingsLanguage.asFlow().collectIn(lifecycleScope) {
|
||||
val config = Configuration(resources.configuration)
|
||||
val locale = if (it == "auto") FlorisLocale.default() else FlorisLocale.fromTag(it)
|
||||
config.setLocale(locale.base)
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
if (AndroidVersion.ATMOST_API28_P) {
|
||||
prefs.other.showAppIcon.observe(this) {
|
||||
prefs.other.showAppIcon.asFlow().collectIn(lifecycleScope) {
|
||||
showAppIcon = it
|
||||
}
|
||||
}
|
||||
|
||||
//Check if android 13+ is running and the NotificationPermission is not set
|
||||
if (AndroidVersion.ATLEAST_API33_T &&
|
||||
prefs.internal.notificationPermissionState.get() == NotificationPermissionState.NOT_SET
|
||||
) {
|
||||
// update pref value to show the setup screen again again
|
||||
prefs.internal.isImeSetUp.set(false)
|
||||
}
|
||||
|
||||
// We defer the setContent call until the datastore model is loaded, until then the splash screen stays drawn
|
||||
prefs.datastoreReadyStatus.observe(this) { isModelLoaded ->
|
||||
if (!isModelLoaded) return@observe
|
||||
val isModelLoaded = AtomicBoolean(false)
|
||||
appContext.preferenceStoreLoaded.collectIn(lifecycleScope) { loaded ->
|
||||
if (!loaded || isModelLoaded.getAndSet(true)) return@collectIn
|
||||
// Check if android 13+ is running and the NotificationPermission is not set
|
||||
if (AndroidVersion.ATLEAST_API33_T &&
|
||||
prefs.internal.notificationPermissionState.get() == NotificationPermissionState.NOT_SET
|
||||
) {
|
||||
// update pref value to show the setup screen again
|
||||
prefs.internal.isImeSetUp.set(false)
|
||||
}
|
||||
AppVersionUtils.updateVersionOnInstallAndLastUse(this, prefs)
|
||||
setContent {
|
||||
ProvideLocalizedResources(resourcesContext) {
|
||||
|
||||
@@ -47,8 +47,9 @@ import dev.patrickgold.florisboard.app.settings.HomeScreen
|
||||
import dev.patrickgold.florisboard.app.settings.about.AboutScreen
|
||||
import dev.patrickgold.florisboard.app.settings.about.ProjectLicenseScreen
|
||||
import dev.patrickgold.florisboard.app.settings.about.ThirdPartyLicensesScreen
|
||||
import dev.patrickgold.florisboard.app.settings.advanced.OtherScreen
|
||||
import dev.patrickgold.florisboard.app.settings.advanced.BackupScreen
|
||||
import dev.patrickgold.florisboard.app.settings.advanced.OtherScreen
|
||||
import dev.patrickgold.florisboard.app.settings.advanced.PhysicalKeyboardScreen
|
||||
import dev.patrickgold.florisboard.app.settings.advanced.RestoreScreen
|
||||
import dev.patrickgold.florisboard.app.settings.clipboard.ClipboardScreen
|
||||
import dev.patrickgold.florisboard.app.settings.dictionary.DictionaryScreen
|
||||
@@ -111,6 +112,7 @@ object Routes {
|
||||
const val Media = "settings/media"
|
||||
|
||||
const val Other = "settings/other"
|
||||
const val PhysicalKeyboard = "settings/other/physical-keyboard"
|
||||
const val Backup = "settings/other/backup"
|
||||
const val Restore = "settings/other/restore"
|
||||
|
||||
@@ -240,6 +242,7 @@ object Routes {
|
||||
composableWithDeepLink(Settings.Media) { MediaScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.Other) { OtherScreen() }
|
||||
composableWithDeepLink(Settings.PhysicalKeyboard) { PhysicalKeyboardScreen() }
|
||||
composableWithDeepLink(Settings.Backup) { BackupScreen() }
|
||||
composableWithDeepLink(Settings.Restore) { RestoreScreen() }
|
||||
|
||||
|
||||
@@ -29,9 +29,8 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
import dev.patrickgold.florisboard.app.AppTheme
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.color.ColorMappings
|
||||
|
||||
/*private val AmoledDarkColorPalette = darkColorScheme(
|
||||
@@ -79,7 +78,7 @@ fun getColorScheme(
|
||||
context: Context,
|
||||
theme: AppTheme,
|
||||
): ColorScheme {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val accentColor by prefs.other.accentColor.observeAsState()
|
||||
val isDark = isSystemInDarkTheme()
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import java.util.Locale
|
||||
@@ -66,9 +67,9 @@ fun AndroidLocalesScreen() = FlorisScreen {
|
||||
out.appendLine()
|
||||
}
|
||||
}
|
||||
context.showLongToast("Exported available system locales to \"${txtFile.path}\"")
|
||||
context.showLongToastSync("Exported available system locales to \"${txtFile.path}\"")
|
||||
} catch (e: Exception) {
|
||||
context.showLongToast(
|
||||
context.showLongToastSync(
|
||||
R.string.error__snackbar_message_template,
|
||||
"error_message" to e.message.toString(),
|
||||
)
|
||||
|
||||
@@ -29,7 +29,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -40,7 +39,7 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.ime.keyboard.CachedLayout
|
||||
@@ -64,7 +63,7 @@ private val DateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss", FlorisLocale.
|
||||
@Composable
|
||||
fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
val context = LocalContext.current
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val themeManager by context.themeManager()
|
||||
|
||||
@@ -75,7 +74,7 @@ fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
val showInlineAutofillOverlay by prefs.devtools.showInlineAutofillOverlay.observeAsState()
|
||||
|
||||
val debugLayoutResult by keyboardManager.layoutManager.debugLayoutComputationResultFlow.collectAsState()
|
||||
val themeInfo by themeManager.activeThemeInfo.observeAsState()
|
||||
val themeInfo by themeManager.activeThemeInfo.collectAsState()
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides Color.White,
|
||||
@@ -97,7 +96,7 @@ fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
if (devtoolsEnabled && showInlineAutofillOverlay && AndroidVersion.ATLEAST_API30_R) {
|
||||
DevtoolsInlineAutofillOverlay()
|
||||
}
|
||||
val loadFailure = themeInfo?.loadFailure
|
||||
val loadFailure = themeInfo.loadFailure
|
||||
if (loadFailure != null) {
|
||||
DevtoolsStylesheetFailedToLoadOverlay(loadFailure)
|
||||
}
|
||||
@@ -163,13 +162,13 @@ private fun DevtoolsLastLayoutComputationOverlay(debugLayoutResult: DebugLayoutC
|
||||
return@DevtoolsOverlayBox
|
||||
}
|
||||
DevtoolsSubGroup(title = "main") {
|
||||
PrintResult(debugLayoutResult!!.main)
|
||||
PrintResult(debugLayoutResult.main)
|
||||
}
|
||||
DevtoolsSubGroup(title = "mod") {
|
||||
PrintResult(debugLayoutResult!!.mod)
|
||||
PrintResult(debugLayoutResult.mod)
|
||||
}
|
||||
DevtoolsSubGroup(title = "ext") {
|
||||
PrintResult(debugLayoutResult!!.ext)
|
||||
PrintResult(debugLayoutResult.ext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
@@ -38,7 +39,9 @@ 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 kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
|
||||
class DebugOnPurposeCrashException : Exception(
|
||||
"Success! The app crashed purposely to display this beautiful screen we all love :)"
|
||||
@@ -52,6 +55,7 @@ fun DevtoolsScreen() = FlorisScreen {
|
||||
val context = LocalContext.current
|
||||
val navController = LocalNavController.current
|
||||
val extensionManager by context.extensionManager()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val (showDialog, setShowDialog) = remember { mutableStateOf(false) }
|
||||
|
||||
@@ -110,15 +114,17 @@ fun DevtoolsScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.devtools__reset_quick_actions_to_default__label),
|
||||
summary = stringRes(R.string.devtools__reset_quick_actions_to_default__summary),
|
||||
onClick = {
|
||||
prefs.smartbar.actionArrangement.set(QuickActionArrangement.Default)
|
||||
context.showLongToast(R.string.devtools__reset_quick_actions_to_default__toast_success)
|
||||
scope.launch {
|
||||
prefs.smartbar.actionArrangement.set(QuickActionArrangement.Default)
|
||||
}
|
||||
context.showLongToastSync(R.string.devtools__reset_quick_actions_to_default__toast_success)
|
||||
},
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
)
|
||||
Preference(
|
||||
title = stringRes(R.string.devtools__reset_flag__label, "flag_name" to "isImeSetUp"),
|
||||
summary = stringRes(R.string.devtools__reset_flag_is_ime_set_up__summary),
|
||||
onClick = { prefs.internal.isImeSetUp.set(false) },
|
||||
onClick = { scope.launch { prefs.internal.isImeSetUp.set(false) } },
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
)
|
||||
Preference(
|
||||
@@ -200,14 +206,14 @@ fun DevtoolsScreen() = FlorisScreen {
|
||||
title = "keyboardExtensions",
|
||||
summary = extensionManager.keyboardExtensions.internalModuleDir.absolutePath,
|
||||
onClick = {
|
||||
context.showLongToast(extensionManager.keyboardExtensions.internalModuleDir.absolutePath)
|
||||
context.showLongToastSync(extensionManager.keyboardExtensions.internalModuleDir.absolutePath)
|
||||
},
|
||||
)
|
||||
Preference(
|
||||
title = "themes",
|
||||
summary = extensionManager.themes.internalModuleDir.absolutePath,
|
||||
onClick = {
|
||||
context.showLongToast(extensionManager.themes.internalModuleDir.absolutePath)
|
||||
context.showLongToastSync(extensionManager.themes.internalModuleDir.absolutePath)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.sp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
@@ -47,6 +47,7 @@ import dev.patrickgold.florisboard.lib.compose.florisScrollbar
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.devtools.Devtools
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
|
||||
// TODO: This screen is just a quick thrown-together thing and needs further enhancing in the UI
|
||||
@Composable
|
||||
@@ -54,7 +55,7 @@ fun ExportDebugLogScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.devtools__debuglog__title)
|
||||
scrollable = false
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val clipboardManager by context.clipboardManager()
|
||||
|
||||
@@ -74,7 +75,7 @@ fun ExportDebugLogScreen() = FlorisScreen {
|
||||
FlorisButton(
|
||||
onClick = {
|
||||
clipboardManager.addNewPlaintext(debugLog!!.joinToString("\n"))
|
||||
context.showShortToast(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
context.showShortToastSync(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
},
|
||||
modifier = Modifier,
|
||||
text = stringRes(R.string.devtools__debuglog__copy_log),
|
||||
@@ -83,7 +84,7 @@ fun ExportDebugLogScreen() = FlorisScreen {
|
||||
FlorisButton(
|
||||
onClick = {
|
||||
clipboardManager.addNewPlaintext(formattedDebugLog!!.joinToString("\n"))
|
||||
context.showShortToast(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
context.showShortToastSync(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
},
|
||||
text = stringRes(R.string.devtools__debuglog__copy_for_github),
|
||||
enabled = debugLog != null,
|
||||
|
||||
@@ -46,7 +46,6 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.MimeTypeFilter
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.cache.CacheManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
|
||||
@@ -60,24 +59,26 @@ import java.util.*
|
||||
import org.florisboard.lib.android.query
|
||||
import org.florisboard.lib.android.readToFile
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.kotlin.io.parentDir
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import org.florisboard.lib.kotlin.mimeTypeFilterOf
|
||||
|
||||
const val FONTS = "fonts"
|
||||
const val IMAGES = "images"
|
||||
|
||||
val MIME_TYPES = mapOf(
|
||||
FONTS to listOf(
|
||||
FONTS to mimeTypeFilterOf(
|
||||
// Source: https://www.alienfactory.co.uk/articles/mime-types-for-web-fonts-in-bedsheet#mimeTypes
|
||||
"font/*",
|
||||
"application/vnd.ms-fontobject", // .eot
|
||||
"application/font-woff", // .woff
|
||||
"application/x-font-truetype", // .ttf
|
||||
"application/x-font-opentype", // .otf
|
||||
"application/font-*",
|
||||
"application/x-font-*",
|
||||
"application/vnd.ms-fontobject",
|
||||
),
|
||||
IMAGES to listOf(
|
||||
IMAGES to mimeTypeFilterOf(
|
||||
"image/*",
|
||||
),
|
||||
)
|
||||
@@ -118,9 +119,9 @@ fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = Fl
|
||||
val tempFile = context.cacheDir.subFile("temp_${UUID.randomUUID()}")
|
||||
context.contentResolver.readToFile(uri, tempFile)
|
||||
val mimeType = context.contentResolver.getType(uri)
|
||||
val types = MIME_TYPES[currentImportDest!!]!!
|
||||
checkNotNull(MimeTypeFilter.matches(mimeType, types.toTypedArray())) {
|
||||
"Given file mime type was '$mimeType', expected one of $types"
|
||||
val filter = MIME_TYPES[currentImportDest!!]!!
|
||||
check(filter.matches(mimeType)) {
|
||||
"Given file mime type was '$mimeType', expected one of ${filter.types}"
|
||||
}
|
||||
val fileName = context.contentResolver.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME)).use { cursor ->
|
||||
if (cursor == null || !cursor.moveToFirst()) return@use null
|
||||
@@ -187,9 +188,9 @@ fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = Fl
|
||||
allowOutsideDismissal = true,
|
||||
onNeutral = {
|
||||
if (file.delete()) {
|
||||
context.showShortToast("Successfully deleted")
|
||||
context.showShortToastSync("Successfully deleted")
|
||||
} else {
|
||||
context.showShortToast("Failed to delete")
|
||||
context.showShortToastSync("Failed to delete")
|
||||
}
|
||||
dialogFile = null
|
||||
version++
|
||||
@@ -197,18 +198,18 @@ fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = Fl
|
||||
onConfirm = {
|
||||
val newFile = file.parentFile!!.subFile(fileNameInput).canonicalFile
|
||||
if (newFile.parentFile != file.canonicalFile.parentFile) {
|
||||
context.showLongToast("Invalid file name!")
|
||||
context.showLongToastSync("Invalid file name!")
|
||||
return@JetPrefAlertDialog
|
||||
}
|
||||
if (newFile.exists()) {
|
||||
context.showShortToast("Filename already exists.")
|
||||
context.showShortToastSync("Filename already exists.")
|
||||
return@JetPrefAlertDialog
|
||||
}
|
||||
val success = file.renameTo(newFile)
|
||||
if (success) {
|
||||
context.showShortToast("Successfully renamed")
|
||||
context.showShortToastSync("Successfully renamed")
|
||||
} else {
|
||||
context.showShortToast("Failed to rename the file.")
|
||||
context.showShortToastSync("Failed to rename the file.")
|
||||
}
|
||||
dialogFile = null
|
||||
version++
|
||||
@@ -258,13 +259,13 @@ fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = Fl
|
||||
dir.mkdirs()
|
||||
val file = dir.subFile(fileName)
|
||||
if (file.parentDir != workspace.extDir.subDir(dest)) {
|
||||
context.showShortToast("Invalid file name")
|
||||
context.showShortToastSync("Invalid file name")
|
||||
} else if (file.exists()) {
|
||||
context.showShortToast("File already exists")
|
||||
context.showShortToastSync("File already exists")
|
||||
} else {
|
||||
val tempFile = result.first
|
||||
if (!tempFile.renameTo(file)) {
|
||||
context.showShortToast("Failed to rename file")
|
||||
context.showShortToastSync("Failed to rename file")
|
||||
tempFile.delete()
|
||||
}
|
||||
currentImportDest = null
|
||||
|
||||
@@ -90,6 +90,7 @@ import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import java.util.*
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
@@ -287,7 +288,7 @@ private fun EditScreen(
|
||||
stylesheetFile.writeText(stylesheet)
|
||||
}.onFailure {
|
||||
// TODO: better error handling
|
||||
context.showLongToast(it.message.toString())
|
||||
context.showLongToastSync(it.message.toString())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
@@ -665,7 +666,7 @@ private fun <T : ExtensionComponent> CreateComponentScreen(
|
||||
when (createFrom) {
|
||||
CreateFrom.EMPTY -> {
|
||||
if (editor.themes.any { it.id == newId.trim() }) {
|
||||
context.showLongToast("A theme with this ID already exists!")
|
||||
context.showLongToastSync("A theme with this ID already exists!")
|
||||
} else {
|
||||
val componentEditor = ThemeExtensionComponentEditor(
|
||||
id = newId.trim(),
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.florisboard.lib.android.showLongToast
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.ext.Extension
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionDefaults
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
|
||||
@Composable
|
||||
fun ExtensionExportScreen(id: String) {
|
||||
@@ -61,9 +62,9 @@ private fun ExportScreen(ext: Extension) = FlorisScreen {
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
runCatching { extensionManager.export(ext, uri) }.onSuccess {
|
||||
context.showLongToast(R.string.ext__export__success)
|
||||
context.showLongToastSync(R.string.ext__export__success)
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(R.string.ext__export__failure, "error_message" to error.localizedMessage)
|
||||
context.showLongToastSync(R.string.ext__export__failure, "error_message" to error.localizedMessage)
|
||||
}
|
||||
navController.popBackStack()
|
||||
},
|
||||
|
||||
@@ -61,6 +61,7 @@ import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.io.FileRegistry
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.kotlin.resultOk
|
||||
|
||||
enum class ExtensionImportScreenType(
|
||||
@@ -188,10 +189,10 @@ fun ExtensionImportScreen(type: ExtensionImportScreenType, initUuid: String?) =
|
||||
}
|
||||
}.onSuccess {
|
||||
workspace.close()
|
||||
context.showLongToast(R.string.ext__import__success)
|
||||
context.showLongToastSync(R.string.ext__import__success)
|
||||
navController.popBackStack()
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(R.string.ext__import__failure, "error_message" to error.localizedMessage)
|
||||
context.showLongToastSync(R.string.ext__import__failure, "error_message" to error.localizedMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ import dev.patrickgold.florisboard.lib.ext.Extension
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionMaintainer
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionMeta
|
||||
import dev.patrickgold.florisboard.lib.io.FlorisRef
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
|
||||
@Composable
|
||||
fun ExtensionViewScreen(id: String) {
|
||||
@@ -202,7 +203,7 @@ private fun ViewScreen(ext: Extension) = FlorisScreen {
|
||||
}.onSuccess {
|
||||
navController.popBackStack()
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(
|
||||
context.showLongToastSync(
|
||||
R.string.error__snackbar_message,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
|
||||
@@ -19,7 +19,6 @@ package dev.patrickgold.florisboard.app.settings
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.Assignment
|
||||
import androidx.compose.material.icons.filled.Adb
|
||||
import androidx.compose.material.icons.filled.Extension
|
||||
import androidx.compose.material.icons.filled.Gesture
|
||||
import androidx.compose.material.icons.filled.Language
|
||||
|
||||
@@ -32,6 +32,7 @@ import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -41,6 +42,8 @@ import androidx.core.app.ShareCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.cacheManager
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
@@ -57,11 +60,14 @@ import dev.patrickgold.florisboard.lib.devtools.flogError
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
|
||||
import dev.patrickgold.florisboard.lib.io.FileRegistry
|
||||
import dev.patrickgold.florisboard.lib.io.ZipUtils
|
||||
import dev.patrickgold.jetpref.datastore.jetprefDatastoreDir
|
||||
import dev.patrickgold.jetpref.datastore.runtime.AndroidAppDataStorage
|
||||
import dev.patrickgold.jetpref.datastore.runtime.FileBasedStorage
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.android.writeFromFile
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
@@ -136,6 +142,7 @@ fun BackupScreen() = FlorisScreen {
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val cacheManager by context.cacheManager()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
var backupDestination by remember { mutableStateOf(Backup.Destination.FILE_SYS) }
|
||||
val backupFilesSelector = remember { Backup.FilesSelector() }
|
||||
@@ -155,22 +162,24 @@ fun BackupScreen() = FlorisScreen {
|
||||
context.contentResolver.writeFromFile(uri, backupWorkspace!!.zipFile)
|
||||
backupWorkspace!!.close()
|
||||
}.onSuccess {
|
||||
context.showLongToast(R.string.backup_and_restore__back_up__success)
|
||||
context.showLongToastSync(R.string.backup_and_restore__back_up__success)
|
||||
navController.popBackStack()
|
||||
}.onFailure { error ->
|
||||
flogError { error.stackTraceToString() }
|
||||
context.showLongToast(R.string.backup_and_restore__back_up__failure, "error_message" to error.message)
|
||||
context.showLongToastSync(R.string.backup_and_restore__back_up__failure, "error_message" to error.message)
|
||||
backupWorkspace = null
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
fun prepareBackupWorkspace() {
|
||||
suspend fun prepareBackupWorkspace() {
|
||||
val workspace = cacheManager.backupAndRestore.new()
|
||||
if (backupFilesSelector.jetprefDatastore) {
|
||||
context.jetprefDatastoreDir.let { dir ->
|
||||
dir.copyRecursively(workspace.inputDir.subDir(dir.name))
|
||||
}
|
||||
val fileBasedStorage = workspace.inputDir
|
||||
.subDir(AndroidAppDataStorage.JETPREF_DIR_NAME)
|
||||
.subFile("${FlorisPreferenceModel.NAME}.${AndroidAppDataStorage.JETPREF_FILE_EXT}")
|
||||
.let { FileBasedStorage(it.path) }
|
||||
FlorisPreferenceStore.export(fileBasedStorage).getOrThrow()
|
||||
}
|
||||
val workspaceFilesDir = workspace.inputDir.subDir("files")
|
||||
if (backupFilesSelector.imeKeyboard) {
|
||||
@@ -225,7 +234,7 @@ fun BackupScreen() = FlorisScreen {
|
||||
backupWorkspace = workspace
|
||||
}
|
||||
|
||||
fun prepareAndPerformBackup() {
|
||||
suspend fun prepareAndPerformBackup() {
|
||||
runCatching {
|
||||
if (backupWorkspace == null || backupWorkspace!!.isClosed()) {
|
||||
prepareBackupWorkspace()
|
||||
@@ -265,7 +274,7 @@ fun BackupScreen() = FlorisScreen {
|
||||
)
|
||||
ButtonBarButton(
|
||||
onClick = {
|
||||
prepareAndPerformBackup()
|
||||
scope.launch { prepareAndPerformBackup() }
|
||||
},
|
||||
text = stringRes(R.string.action__back_up),
|
||||
enabled = backupFilesSelector.atLeastOneSelected(),
|
||||
|
||||
@@ -45,6 +45,7 @@ import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.isMaterialYou
|
||||
import dev.patrickgold.jetpref.datastore.ui.listPrefEntries
|
||||
import dev.patrickgold.jetpref.datastore.ui.vectorResource
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.color.ColorMappings
|
||||
|
||||
@@ -154,6 +155,11 @@ fun OtherScreen() = FlorisScreen {
|
||||
},
|
||||
enabledIf = { AndroidVersion.ATMOST_API28_P },
|
||||
)
|
||||
Preference(
|
||||
icon = vectorResource(R.drawable.ic_keyboard_keys),
|
||||
title = stringRes(R.string.physical_keyboard__title),
|
||||
onClick = { navController.navigate(Routes.Settings.PhysicalKeyboard) },
|
||||
)
|
||||
Preference(
|
||||
icon = Icons.Default.Adb,
|
||||
title = stringRes(R.string.devtools__title),
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The FlorisBoard Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.app.settings.advanced
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.provider.Settings
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
|
||||
@Composable
|
||||
fun PhysicalKeyboardScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.physical_keyboard__title)
|
||||
|
||||
val context = LocalContext.current
|
||||
val physicalKeyboardAttached by remember {
|
||||
mutableStateOf(context.resources.configuration.keyboard != Configuration.KEYBOARD_NOKEYS)
|
||||
}
|
||||
|
||||
val activityForResult = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { }
|
||||
|
||||
content {
|
||||
if (physicalKeyboardAttached) {
|
||||
Preference(
|
||||
title = stringRes(R.string.physical_keyboard__system_settings__title),
|
||||
summary = stringRes(R.string.physical_keyboard__system_settings__summary),
|
||||
onClick = {
|
||||
activityForResult.launch(Intent(Settings.ACTION_HARD_KEYBOARD_SETTINGS))
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Preference(
|
||||
title = stringRes(R.string.physical_keyboard__system_settings__title),
|
||||
summary = stringRes(R.string.physical_keyboard__system_settings__summary_not_attached),
|
||||
)
|
||||
}
|
||||
SwitchPreference(
|
||||
pref = prefs.physicalKeyboard.showOnScreenKeyboard,
|
||||
title = stringRes(R.string.physical_keyboard__show_on_screen_keyboard__title),
|
||||
summary = stringRes(R.string.physical_keyboard__show_on_screen_keyboard__summary),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -43,8 +43,9 @@ import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.cacheManager
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
|
||||
@@ -60,13 +61,16 @@ import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
|
||||
import dev.patrickgold.florisboard.lib.io.ZipUtils
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import dev.patrickgold.jetpref.datastore.runtime.AndroidAppDataStorage
|
||||
import dev.patrickgold.jetpref.datastore.runtime.FileBasedStorage
|
||||
import dev.patrickgold.jetpref.datastore.runtime.ImportStrategy
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.readToFile
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
|
||||
import org.florisboard.lib.kotlin.io.readJson
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
@@ -79,11 +83,6 @@ object Restore {
|
||||
const val MIN_VERSION_CODE = 64
|
||||
const val PACKAGE_NAME = "dev.patrickgold.florisboard"
|
||||
const val BACKUP_ARCHIVE_FILE_NAME = "backup.zip"
|
||||
|
||||
enum class Mode {
|
||||
MERGE,
|
||||
ERASE_AND_OVERWRITE;
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -91,13 +90,12 @@ fun RestoreScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.backup_and_restore__restore__title)
|
||||
previewFieldVisible = false
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val cacheManager by context.cacheManager()
|
||||
|
||||
val restoreFilesSelector = remember { Backup.FilesSelector() }
|
||||
var restoreMode by remember { mutableStateOf(Restore.Mode.MERGE) }
|
||||
var importStrategy by remember { mutableStateOf(ImportStrategy.Merge) }
|
||||
// TODO: rememberCoroutineScope() is unusable because it provides the scope in a cancelled state, which does
|
||||
// not make sense at all. I suspect that this is a bug and once it is resolved we can use it here again.
|
||||
val restoreScope = remember { CoroutineScope(Dispatchers.Main) }
|
||||
@@ -138,7 +136,7 @@ fun RestoreScreen() = FlorisScreen {
|
||||
}
|
||||
restoreWorkspace = workspace
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(
|
||||
context.showLongToastSync(
|
||||
R.string.backup_and_restore__restore__failure,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
@@ -148,15 +146,13 @@ fun RestoreScreen() = FlorisScreen {
|
||||
|
||||
suspend fun performRestore() {
|
||||
val workspace = restoreWorkspace!!
|
||||
val shouldReset = restoreMode == Restore.Mode.ERASE_AND_OVERWRITE
|
||||
val shouldReset = importStrategy == ImportStrategy.Erase
|
||||
if (restoreFilesSelector.jetprefDatastore) {
|
||||
val datastoreFile = workspace.outputDir
|
||||
.subDir(JetPref.JETPREF_DIR_NAME)
|
||||
.subFile("${prefs.name}.${JetPref.JETPREF_FILE_EXT}")
|
||||
if (datastoreFile.exists()) {
|
||||
prefs.datastorePersistenceHandler?.loadPrefs(datastoreFile, shouldReset)
|
||||
prefs.datastorePersistenceHandler?.persistPrefs()
|
||||
}
|
||||
val fileBasedStorage = workspace.outputDir
|
||||
.subDir(AndroidAppDataStorage.JETPREF_DIR_NAME)
|
||||
.subFile("${FlorisPreferenceModel.NAME}.${AndroidAppDataStorage.JETPREF_FILE_EXT}")
|
||||
.let { FileBasedStorage(it.path) }
|
||||
FlorisPreferenceStore.import(importStrategy, fileBasedStorage).getOrThrow()
|
||||
}
|
||||
val workspaceFilesDir = workspace.outputDir.subDir("files")
|
||||
if (restoreFilesSelector.imeKeyboard) {
|
||||
@@ -275,16 +271,16 @@ fun RestoreScreen() = FlorisScreen {
|
||||
) {
|
||||
RadioListItem(
|
||||
onClick = {
|
||||
restoreMode = Restore.Mode.MERGE
|
||||
importStrategy = ImportStrategy.Merge
|
||||
},
|
||||
selected = restoreMode == Restore.Mode.MERGE,
|
||||
selected = importStrategy == ImportStrategy.Merge,
|
||||
text = stringRes(R.string.backup_and_restore__restore__mode_merge),
|
||||
)
|
||||
RadioListItem(
|
||||
onClick = {
|
||||
restoreMode = Restore.Mode.ERASE_AND_OVERWRITE
|
||||
importStrategy = ImportStrategy.Erase
|
||||
},
|
||||
selected = restoreMode == Restore.Mode.ERASE_AND_OVERWRITE,
|
||||
selected = importStrategy == ImportStrategy.Erase,
|
||||
text = stringRes(R.string.backup_and_restore__restore__mode_erase_and_overwrite),
|
||||
)
|
||||
}
|
||||
@@ -293,7 +289,7 @@ fun RestoreScreen() = FlorisScreen {
|
||||
runCatching {
|
||||
restoreDataFromFileSystemLauncher.launch("*/*")
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(
|
||||
context.showLongToastSync(
|
||||
R.string.backup_and_restore__restore__failure,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
|
||||
@@ -68,6 +68,7 @@ import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.android.stringRes
|
||||
|
||||
private val AllLanguagesLocale = FlorisLocale.from(language = "zz")
|
||||
@@ -143,16 +144,16 @@ fun UserDictionaryScreen(type: UserDictionaryType) = FlorisScreen {
|
||||
UserDictionaryType.SYSTEM -> dictionaryManager.systemUserDictionaryDatabase()
|
||||
}
|
||||
if (db == null) {
|
||||
context.showLongToast("Database handle is null, failed to import")
|
||||
context.showLongToastSync("Database handle is null, failed to import")
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
runCatching {
|
||||
db.importCombinedList(context, uri)
|
||||
}.onSuccess {
|
||||
buildUi()
|
||||
context.showLongToast(R.string.settings__udm__dictionary_import_success)
|
||||
context.showLongToastSync(R.string.settings__udm__dictionary_import_success)
|
||||
}.onFailure { error ->
|
||||
context.showLongToast("Error: ${error.localizedMessage}")
|
||||
context.showLongToastSync("Error: ${error.localizedMessage}")
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -168,15 +169,15 @@ fun UserDictionaryScreen(type: UserDictionaryType) = FlorisScreen {
|
||||
UserDictionaryType.SYSTEM -> dictionaryManager.systemUserDictionaryDatabase()
|
||||
}
|
||||
if (db == null) {
|
||||
context.showLongToast("Database handle is null, failed to export")
|
||||
context.showLongToastSync("Database handle is null, failed to export")
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
runCatching {
|
||||
db.exportCombinedList(context, uri)
|
||||
}.onSuccess {
|
||||
context.showLongToast(R.string.settings__udm__dictionary_export_success)
|
||||
context.showLongToastSync(R.string.settings__udm__dictionary_export_success)
|
||||
}.onFailure { error ->
|
||||
context.showLongToast("Error: ${error.localizedMessage}")
|
||||
context.showLongToastSync("Error: ${error.localizedMessage}")
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -20,11 +20,8 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.input.InputFeedbackActivationMode
|
||||
import dev.patrickgold.florisboard.ime.input.HapticVibrationMode
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.systemVibratorOrNull
|
||||
import org.florisboard.lib.android.vibrate
|
||||
import dev.patrickgold.florisboard.ime.input.InputFeedbackActivationMode
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference
|
||||
@@ -32,6 +29,8 @@ import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import org.florisboard.lib.android.systemVibratorOrNull
|
||||
import org.florisboard.lib.android.vibrate
|
||||
|
||||
@OptIn(ExperimentalJetPrefDatastoreUi::class)
|
||||
@Composable
|
||||
@@ -138,8 +137,6 @@ fun InputFeedbackScreen() = FlorisScreen {
|
||||
summary = { strength ->
|
||||
if (vibrator == null || !vibrator.hasVibrator()) {
|
||||
stringRes(R.string.pref__input_feedback__haptic_vibration_strength__summary_no_vibrator)
|
||||
} else if (AndroidVersion.ATMOST_API25_N_MR1) {
|
||||
stringRes(R.string.pref__input_feedback__haptic_vibration_strength__summary_unsupported_android_version)
|
||||
} else if (!vibrator.hasAmplitudeControl()) {
|
||||
stringRes(R.string.pref__input_feedback__haptic_vibration_strength__summary_no_amplitude_ctrl)
|
||||
} else {
|
||||
@@ -157,7 +154,7 @@ fun InputFeedbackScreen() = FlorisScreen {
|
||||
prefs.inputFeedback.hapticEnabled isEqualTo true &&
|
||||
prefs.inputFeedback.hapticVibrationMode isEqualTo HapticVibrationMode.USE_VIBRATOR_DIRECTLY &&
|
||||
vibrator != null && vibrator.hasVibrator() &&
|
||||
AndroidVersion.ATLEAST_API26_O && vibrator.hasAmplitudeControl()
|
||||
vibrator.hasAmplitudeControl()
|
||||
},
|
||||
)
|
||||
SwitchPreference(
|
||||
|
||||
@@ -41,10 +41,10 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionImportScreenType
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.nlp.LanguagePackComponent
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
@@ -61,6 +61,7 @@ import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
|
||||
enum class LanguagePackManagerScreenAction(val id: String) {
|
||||
MANAGE("manage-installed-language-packs");
|
||||
@@ -75,7 +76,7 @@ fun LanguagePackManagerScreen(action: LanguagePackManagerScreenAction?) = Floris
|
||||
else -> error("LanguagePack manager screen action must not be null")
|
||||
})
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val extensionManager by context.extensionManager()
|
||||
@@ -199,7 +200,7 @@ fun LanguagePackManagerScreen(action: LanguagePackManagerScreenAction?) = Floris
|
||||
runCatching {
|
||||
extensionManager.delete(languagePackExtToDelete!!)
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(
|
||||
context.showLongToastSync(
|
||||
R.string.error__snackbar_message,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
|
||||
@@ -44,8 +44,8 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
@@ -61,7 +61,7 @@ fun SelectLocaleScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.settings__localization__subtype_select_locale)
|
||||
scrollable = false
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val navController = LocalNavController.current
|
||||
|
||||
val displayLanguageNamesIn by prefs.localization.displayLanguageNamesIn.observeAsState()
|
||||
|
||||
@@ -58,9 +58,9 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.core.SubtypeJsonConfig
|
||||
@@ -86,7 +86,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.encodeToString
|
||||
|
||||
private val SelectComponentName = ExtensionComponentName("00", "00")
|
||||
private val SelectNlpProviderId = SelectComponentName.toString()
|
||||
@@ -186,7 +185,7 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
val selectValue = stringRes(R.string.settings__localization__subtype_select_placeholder)
|
||||
val selectListValues = remember(selectValue) { listOf(selectValue) }
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val configuration = LocalConfiguration.current
|
||||
|
||||
@@ -27,8 +27,8 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistory
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistoryHelper
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSkinTone
|
||||
@@ -52,7 +52,7 @@ fun MediaScreen() = FlorisScreen {
|
||||
previewFieldVisible = true
|
||||
iconSpaceReserved = true
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
|
||||
var shouldDelete by remember { mutableStateOf<ShouldDelete?>(null) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
@@ -68,7 +68,7 @@ fun SmartbarScreen() = FlorisScreen {
|
||||
// TODO: schedule to remove this preference in the future, but keep it for now so users
|
||||
// know why the setting is not available anymore. Also force enable it for UI display.
|
||||
SideEffect {
|
||||
prefs.smartbar.sharedActionsAutoExpandCollapse.set(true)
|
||||
// prefs.smartbar.sharedActionsAutoExpandCollapse.set(true)
|
||||
}
|
||||
SwitchPreference(
|
||||
prefs.smartbar.sharedActionsAutoExpandCollapse,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.settings.theme
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
@@ -655,7 +656,7 @@ private fun PropertyValueEditor(
|
||||
JetPrefListItem(
|
||||
modifier = Modifier.clickable {
|
||||
val relPath = file.path.removePrefix(workspace.extDir.path)
|
||||
inputStr = "flex:$relPath"
|
||||
inputStr = "flex:" + Uri.encode(relPath, "/")
|
||||
onValueChange(SnyggUriValue(inputStr))
|
||||
showSelectFileDialog = false
|
||||
},
|
||||
|
||||
@@ -96,6 +96,7 @@ import dev.patrickgold.jetpref.material.ui.JetPrefDropdown
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefTextFieldDefaults
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.android.stringRes
|
||||
import org.florisboard.lib.kotlin.curlyFormat
|
||||
import org.florisboard.lib.snygg.SnyggAnnotationRule
|
||||
@@ -406,7 +407,7 @@ private fun EditCodeValueDialog(
|
||||
}
|
||||
if (!isFlorisBoardEnabled || !isFlorisBoardSelected) {
|
||||
lastRecordingToast?.cancel()
|
||||
lastRecordingToast = context.showShortToast(
|
||||
lastRecordingToast = context.showShortToastSync(
|
||||
R.string.settings__theme_editor__code_recording_requires_default_ime_floris,
|
||||
"app_name" to context.stringRes(R.string.floris_app_name),
|
||||
)
|
||||
@@ -432,12 +433,12 @@ private fun EditCodeValueDialog(
|
||||
val defaultReceiver = keyboardManager.inputEventDispatcher.keyEventReceiver
|
||||
keyboardManager.inputEventDispatcher.keyEventReceiver = receiver
|
||||
lastRecordingToast?.cancel()
|
||||
lastRecordingToast = context.showShortToast(R.string.settings__theme_editor__code_recording_started)
|
||||
lastRecordingToast = context.showShortToastSync(R.string.settings__theme_editor__code_recording_started)
|
||||
focusRequester.requestFocus()
|
||||
onDispose {
|
||||
keyboardManager.inputEventDispatcher.keyEventReceiver = defaultReceiver
|
||||
lastRecordingToast?.cancel()
|
||||
lastRecordingToast = context.showShortToast(R.string.settings__theme_editor__code_recording_stopped)
|
||||
lastRecordingToast = context.showShortToastSync(R.string.settings__theme_editor__code_recording_stopped)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceLayout
|
||||
@@ -37,7 +37,7 @@ fun FineTuneDialog(onDismiss: () -> Unit) {
|
||||
onDismiss = onDismiss,
|
||||
contentPadding = FineTuneContentPadding,
|
||||
) {
|
||||
PreferenceLayout(florisPreferenceModel(), iconSpaceReserved = false) {
|
||||
PreferenceLayout(FlorisPreferenceStore, iconSpaceReserved = false) {
|
||||
ListPreference(
|
||||
listPref = prefs.theme.editorLevel,
|
||||
title = stringRes(R.string.settings__theme_editor__fine_tune__level),
|
||||
|
||||
@@ -30,7 +30,6 @@ import androidx.compose.material.icons.automirrored.filled.FormatAlignLeft
|
||||
import androidx.compose.material.icons.automirrored.filled.FormatAlignRight
|
||||
import androidx.compose.material.icons.automirrored.filled.WrapText
|
||||
import androidx.compose.material.icons.filled.AttachFile
|
||||
import androidx.compose.material.icons.filled.CheckBox
|
||||
import androidx.compose.material.icons.filled.CheckBoxOutlineBlank
|
||||
import androidx.compose.material.icons.filled.FontDownload
|
||||
import androidx.compose.material.icons.filled.FormatAlignCenter
|
||||
@@ -57,7 +56,7 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.snygg.value.SnyggCutCornerDpShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDefinedVarValue
|
||||
@@ -121,7 +120,7 @@ internal fun SnyggValueIcon(
|
||||
modifier: Modifier = Modifier,
|
||||
spec: SnyggValueIcon.Spec = SnyggValueIcon.Normal,
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val accentColor by prefs.theme.accentColor.observeAsState()
|
||||
|
||||
|
||||
@@ -70,9 +70,9 @@ import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.apptheme.Shapes
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionComponentView
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponentEditor
|
||||
@@ -100,6 +100,7 @@ import dev.patrickgold.jetpref.material.ui.JetPrefTextField
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import org.florisboard.lib.snygg.SnyggAnnotationRule
|
||||
import org.florisboard.lib.snygg.SnyggElementRule
|
||||
@@ -145,7 +146,7 @@ fun ThemeEditorScreen(
|
||||
title = stringRes(R.string.ext__editor__edit_component__title_theme)
|
||||
scrollable = false
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
val themeManager by context.themeManager()
|
||||
@@ -309,14 +310,14 @@ fun ThemeEditorScreen(
|
||||
}
|
||||
|
||||
DisposableEffect(workspace.version) {
|
||||
themeManager.previewThemeInfo = ThemeManager.ThemeInfo.DEFAULT.copy(
|
||||
themeManager.previewThemeInfo.value = ThemeManager.ThemeInfo.DEFAULT.copy(
|
||||
name = extPreviewTheme(System.currentTimeMillis().toString()),
|
||||
config = editor.build(),
|
||||
stylesheet = stylesheetEditor.build(),
|
||||
loadedDir = workspace.extDir,
|
||||
)
|
||||
onDispose {
|
||||
themeManager.previewThemeInfo = null
|
||||
themeManager.previewThemeInfo.value = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -633,7 +634,7 @@ private fun ComponentMetaEditorDialog(
|
||||
if (!allFieldsValid) {
|
||||
showValidationErrors = true
|
||||
} else if (id != editor.id && (workspace.editor as? ThemeExtensionEditor)?.themes?.find { it.id == id.trim() } != null) {
|
||||
context.showLongToast("A theme with this ID already exists!")
|
||||
context.showLongToastSync("A theme with this ID already exists!")
|
||||
} else {
|
||||
workspace.update {
|
||||
editor.id = id.trim()
|
||||
|
||||
@@ -29,10 +29,11 @@ import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
|
||||
@@ -45,6 +46,7 @@ import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.florisboard.themeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
enum class ThemeManagerScreenAction(val id: String) {
|
||||
SELECT_DAY("select-day"),
|
||||
@@ -60,10 +62,11 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
})
|
||||
previewFieldVisible = true
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val extensionManager by context.extensionManager()
|
||||
val themeManager by context.themeManager()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val indexedThemeExtensions by extensionManager.themes.observeAsNonNullState()
|
||||
val extGroupedThemes = remember(indexedThemeExtensions) {
|
||||
@@ -83,7 +86,7 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
val extComponentName = ExtensionComponentName(extId, componentId)
|
||||
when (action) {
|
||||
ThemeManagerScreenAction.SELECT_DAY,
|
||||
ThemeManagerScreenAction.SELECT_NIGHT -> {
|
||||
ThemeManagerScreenAction.SELECT_NIGHT -> scope.launch {
|
||||
getThemeIdPref().set(extComponentName)
|
||||
}
|
||||
}
|
||||
@@ -96,9 +99,9 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
|
||||
content {
|
||||
DisposableEffect(activeThemeId) {
|
||||
themeManager.previewThemeId = activeThemeId
|
||||
themeManager.previewThemeId.value = activeThemeId
|
||||
onDispose {
|
||||
themeManager.previewThemeId = null
|
||||
themeManager.previewThemeId.value = null
|
||||
}
|
||||
}
|
||||
val grayColor = LocalContentColor.current.copy(alpha = 0.56f)
|
||||
|
||||
@@ -16,19 +16,18 @@
|
||||
|
||||
package dev.patrickgold.florisboard.app.settings.theme
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Brightness2
|
||||
import androidx.compose.material.icons.filled.BrightnessAuto
|
||||
import androidx.compose.material.icons.filled.ColorLens
|
||||
import androidx.compose.material.icons.filled.DarkMode
|
||||
import androidx.compose.material.icons.filled.LightMode
|
||||
import androidx.compose.material.icons.filled.WbTwilight
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
@@ -37,7 +36,6 @@ import dev.patrickgold.florisboard.app.ext.AddonManagementReferenceBox
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionListScreenType
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeManager
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeMode
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisInfoCard
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
|
||||
@@ -45,6 +43,7 @@ import dev.patrickgold.florisboard.themeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.ColorPickerPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.LocalTimePickerPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.isMaterialYou
|
||||
import org.florisboard.lib.color.ColorMappings
|
||||
@@ -60,41 +59,21 @@ fun ThemeScreen() = FlorisScreen {
|
||||
|
||||
@Composable
|
||||
fun ThemeManager.getThemeLabel(id: ExtensionComponentName): String {
|
||||
val configs by indexedThemeConfigs.observeAsState()
|
||||
configs?.get(id)?.let { return it.label }
|
||||
val configs by indexedThemeConfigs.collectAsState()
|
||||
configs[id]?.let { return it.label }
|
||||
return id.toString()
|
||||
}
|
||||
|
||||
content {
|
||||
val themeMode by prefs.theme.mode.observeAsState()
|
||||
val dayThemeId by prefs.theme.dayThemeId.observeAsState()
|
||||
val nightThemeId by prefs.theme.nightThemeId.observeAsState()
|
||||
|
||||
/*Card(modifier = Modifier.padding(8.dp)) {
|
||||
Column(modifier = Modifier.padding(8.dp)) {
|
||||
Text("If you want to give feedback on the new stylesheet editor and theme engine, please do so in below linked feedback thread:\n")
|
||||
Button(onClick = {
|
||||
context.launchUrl("https://github.com/florisboard/florisboard/discussions/1531")
|
||||
}) {
|
||||
Text("Open Feedback Thread")
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
ListPreference(
|
||||
prefs.theme.mode,
|
||||
icon = Icons.Default.BrightnessAuto,
|
||||
title = stringRes(R.string.pref__theme__mode__label),
|
||||
entries = enumDisplayEntriesOf(ThemeMode::class),
|
||||
)
|
||||
if (themeMode == ThemeMode.FOLLOW_TIME) {
|
||||
FlorisInfoCard(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
text = """
|
||||
The theme mode "Follow time" is not available in this beta release.
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
Preference(
|
||||
icon = Icons.Default.LightMode,
|
||||
title = stringRes(R.string.pref__theme__day),
|
||||
@@ -113,6 +92,18 @@ fun ThemeScreen() = FlorisScreen {
|
||||
navController.navigate(Routes.Settings.ThemeManager(ThemeManagerScreenAction.SELECT_NIGHT))
|
||||
},
|
||||
)
|
||||
LocalTimePickerPreference(
|
||||
pref = prefs.theme.sunriseTime,
|
||||
title = stringRes(R.string.pref__theme__sunrise_time__label),
|
||||
icon = Icons.Default.WbTwilight,
|
||||
enabledIf = { prefs.theme.mode isEqualTo ThemeMode.FOLLOW_TIME },
|
||||
)
|
||||
LocalTimePickerPreference(
|
||||
pref = prefs.theme.sunsetTime,
|
||||
title = stringRes(R.string.pref__theme__sunset_time__label),
|
||||
icon = Icons.Default.Brightness2,
|
||||
enabledIf = { prefs.theme.mode isEqualTo ThemeMode.FOLLOW_TIME },
|
||||
)
|
||||
ColorPickerPreference(
|
||||
pref = prefs.theme.accentColor,
|
||||
title = stringRes(R.string.pref__theme__theme_accent_color__label),
|
||||
|
||||
@@ -33,6 +33,7 @@ import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -40,11 +41,11 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.AppPrefs
|
||||
import dev.patrickgold.florisboard.app.FlorisAppActivity
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisBulletSpacer
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreenScope
|
||||
@@ -57,10 +58,11 @@ import dev.patrickgold.florisboard.lib.util.launchActivity
|
||||
import dev.patrickgold.florisboard.lib.util.launchUrl
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceUiScope
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
|
||||
|
||||
@Composable
|
||||
fun SetupScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.setup__title)
|
||||
@@ -70,7 +72,8 @@ fun SetupScreen() = FlorisScreen {
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val isFlorisBoardEnabled by InputMethodUtils.observeIsFlorisboardEnabled(foregroundOnly = true)
|
||||
val isFlorisBoardSelected by InputMethodUtils.observeIsFlorisboardSelected(foregroundOnly = true)
|
||||
@@ -78,10 +81,12 @@ fun SetupScreen() = FlorisScreen {
|
||||
|
||||
val requestNotification =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||
if (isGranted) {
|
||||
prefs.internal.notificationPermissionState.set(NotificationPermissionState.GRANTED)
|
||||
} else {
|
||||
prefs.internal.notificationPermissionState.set(NotificationPermissionState.DENIED)
|
||||
scope.launch {
|
||||
if (isGranted) {
|
||||
prefs.internal.notificationPermissionState.set(NotificationPermissionState.GRANTED)
|
||||
} else {
|
||||
prefs.internal.notificationPermissionState.set(NotificationPermissionState.DENIED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +96,8 @@ fun SetupScreen() = FlorisScreen {
|
||||
context,
|
||||
navController,
|
||||
requestNotification,
|
||||
hasNotificationPermission
|
||||
hasNotificationPermission,
|
||||
scope,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -103,6 +109,7 @@ private fun FlorisScreenScope.content(
|
||||
navController: NavController,
|
||||
requestNotification: ManagedActivityResultLauncher<String, Boolean>,
|
||||
hasNotificationPermission: NotificationPermissionState,
|
||||
scope: CoroutineScope,
|
||||
) {
|
||||
|
||||
val stepState = rememberSaveable(saver = FlorisStepState.Saver) {
|
||||
@@ -158,7 +165,7 @@ private fun FlorisScreenScope.content(
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
},
|
||||
steps = steps(
|
||||
context, navController, requestNotification
|
||||
context, navController, requestNotification, scope
|
||||
),
|
||||
footer = {
|
||||
footer(context)
|
||||
@@ -189,10 +196,11 @@ private fun footer(context: Context) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PreferenceUiScope<AppPrefs>.steps(
|
||||
private fun PreferenceUiScope<FlorisPreferenceModel>.steps(
|
||||
context: Context,
|
||||
navController: NavController,
|
||||
requestNotification: ManagedActivityResultLauncher<String, Boolean>,
|
||||
scope: CoroutineScope,
|
||||
): List<FlorisStep> {
|
||||
|
||||
return listOfNotNull(
|
||||
@@ -232,7 +240,7 @@ private fun PreferenceUiScope<AppPrefs>.steps(
|
||||
StepText(stringRes(R.string.setup__finish_up__description_p1))
|
||||
StepText(stringRes(R.string.setup__finish_up__description_p2))
|
||||
StepButton(label = stringRes(R.string.setup__finish_up__finish_btn)) {
|
||||
this@steps.prefs.internal.isImeSetUp.set(true)
|
||||
scope.launch { this@steps.prefs.internal.isImeSetUp.set(true) }
|
||||
navController.navigate(Routes.Settings.Home) {
|
||||
popUpTo(Routes.Setup.Screen) {
|
||||
inclusive = true
|
||||
|
||||
@@ -74,6 +74,7 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.mutableStateSetOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -87,7 +88,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.ime.ImeUiMode
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
|
||||
@@ -112,9 +113,11 @@ import dev.patrickgold.florisboard.lib.observeAsTransformingState
|
||||
import dev.patrickgold.florisboard.lib.util.NetworkUtils
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.AndroidKeyguardManager
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.android.systemService
|
||||
import org.florisboard.lib.snygg.SnyggQueryAttributes
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
@@ -136,7 +139,8 @@ const val CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO: Int = 0
|
||||
fun ClipboardInputLayout(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val scope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
val clipboardManager by context.clipboardManager()
|
||||
val keyboardManager by context.keyboardManager()
|
||||
@@ -203,7 +207,7 @@ fun ClipboardInputLayout(
|
||||
)
|
||||
SnyggIconButton(
|
||||
elementName = FlorisImeUi.ClipboardHeaderButton.elementName,
|
||||
onClick = { prefs.clipboard.historyEnabled.set(!historyEnabled) },
|
||||
onClick = { scope.launch { prefs.clipboard.historyEnabled.set(!historyEnabled) } },
|
||||
modifier = sizeModifier.autoMirrorForRtl(),
|
||||
enabled = !deviceLocked && !isPopupSurfaceActive(),
|
||||
) {
|
||||
@@ -583,7 +587,7 @@ fun ClipboardInputLayout(
|
||||
attributes = mapOf("action" to "yes"),
|
||||
onClick = {
|
||||
clipboardManager.clearHistory()
|
||||
context.showShortToast(R.string.clipboard__cleared_history)
|
||||
context.showShortToastSync(R.string.clipboard__cleared_history)
|
||||
showClearAllHistory = false
|
||||
isFilterRowShown = false
|
||||
},
|
||||
@@ -629,7 +633,7 @@ fun ClipboardInputLayout(
|
||||
text = stringRes(R.string.clipboard__disabled__message),
|
||||
)
|
||||
SnyggButton(FlorisImeUi.ClipboardHistoryDisabledButton.elementName,
|
||||
onClick = { prefs.clipboard.historyEnabled.set(true) },
|
||||
onClick = { scope.launch { prefs.clipboard.historyEnabled.set(true) } },
|
||||
modifier = Modifier.align(Alignment.End),
|
||||
) {
|
||||
SnyggText(
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
package dev.patrickgold.florisboard.ime.clipboard
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardHistoryDao
|
||||
@@ -44,6 +43,7 @@ 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.showShortToastSync
|
||||
import org.florisboard.lib.android.systemService
|
||||
import org.florisboard.lib.kotlin.tryOrNull
|
||||
import java.io.Closeable
|
||||
@@ -91,7 +91,7 @@ class ClipboardManager(
|
||||
}
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val appContext by context.appContext()
|
||||
private val editorInstance by context.editorInstance()
|
||||
private val systemClipboardManager = context.systemService(AndroidClipboardManager::class)
|
||||
@@ -355,7 +355,7 @@ class ClipboardManager(
|
||||
val editorInstance by appContext.editorInstance()
|
||||
editorInstance.commitClipboardItem(item).also { result ->
|
||||
if (!result) {
|
||||
appContext.showShortToast("Failed to paste item.")
|
||||
appContext.showShortToastSync("Failed to paste item.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ package dev.patrickgold.florisboard.ime.clipboard
|
||||
import android.content.ClipData
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.ImageDecoder
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import androidx.activity.ComponentActivity
|
||||
@@ -47,22 +47,25 @@ 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.FlorisPreferenceStore
|
||||
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.AndroidVersion
|
||||
import org.florisboard.lib.android.stringRes
|
||||
import org.florisboard.lib.android.systemService
|
||||
import org.florisboard.lib.kotlin.mimeTypeFilterOf
|
||||
|
||||
class FlorisCopyToClipboardActivity : ComponentActivity() {
|
||||
private var error: CopyToClipboardError? = null
|
||||
private var bitmap: Bitmap? = null
|
||||
private val clipboardManager by lazy { systemService(AndroidClipboardManager::class) }
|
||||
private val filter = mimeTypeFilterOf("image/*")
|
||||
|
||||
internal enum class CopyToClipboardError {
|
||||
UNKNOWN_ERROR,
|
||||
ANDROID_VERSION_TO_OLD_ERROR,
|
||||
TYPE_NOT_SUPPORTED_ERROR;
|
||||
|
||||
@Composable
|
||||
@@ -70,88 +73,100 @@ class FlorisCopyToClipboardActivity : ComponentActivity() {
|
||||
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 onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
handleIntent(intent)
|
||||
|
||||
setContent {
|
||||
Content()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
finish()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val systemClipboardManager = this.systemService(AndroidClipboardManager::class)
|
||||
private fun handleIntent(intent: Intent) {
|
||||
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
|
||||
}
|
||||
return
|
||||
}
|
||||
if (!filter.matches(type) || !intent.hasExtra(Intent.EXTRA_STREAM)) {
|
||||
error = CopyToClipboardError.TYPE_NOT_SUPPORTED_ERROR
|
||||
return
|
||||
}
|
||||
|
||||
setContent {
|
||||
ProvideLocalizedResources(this, forceLayoutDirection = LayoutDirection.Ltr) {
|
||||
val theme by prefs.other.settingsTheme.observeAsState()
|
||||
FlorisAppTheme(theme) {
|
||||
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),
|
||||
val uri: Uri? =
|
||||
if (AndroidVersion.ATLEAST_API33_T) {
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
intent.getParcelableExtra(Intent.EXTRA_STREAM)
|
||||
}
|
||||
|
||||
if (uri == null) {
|
||||
error = CopyToClipboardError.TYPE_NOT_SUPPORTED_ERROR
|
||||
return
|
||||
}
|
||||
bitmap = uriToBitmap(uri)
|
||||
}
|
||||
|
||||
private fun uriToBitmap(uri: Uri): Bitmap {
|
||||
val clip = ClipData.newUri(contentResolver, "image", uri)
|
||||
clipboardManager.setPrimaryClip(clip)
|
||||
return if (AndroidVersion.ATLEAST_API28_P) {
|
||||
val source = ImageDecoder.createSource(contentResolver, uri)
|
||||
ImageDecoder.decodeBitmap(source)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
MediaStore.Images.Media.getBitmap(contentResolver, uri)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content() {
|
||||
val prefs by FlorisPreferenceStore
|
||||
ProvideLocalizedResources(this, forceLayoutDirection = LayoutDirection.Ltr) {
|
||||
val theme by prefs.other.settingsTheme.observeAsState()
|
||||
FlorisAppTheme(theme) {
|
||||
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
|
||||
)
|
||||
}
|
||||
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(
|
||||
private fun BottomSheet(
|
||||
content: @Composable ColumnScope.() -> Unit,
|
||||
) {
|
||||
ModalBottomSheet(
|
||||
@@ -165,9 +180,7 @@ class FlorisCopyToClipboardActivity : ComponentActivity() {
|
||||
.align(Alignment.End)
|
||||
.padding(16.dp),
|
||||
onClick = { finish() },
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
//containerColor = buttonContainer.background.solidColor(context = context),
|
||||
)
|
||||
colors = ButtonDefaults.textButtonColors(),
|
||||
) {
|
||||
Text(text = stringRes(id = R.string.action__ok))
|
||||
}
|
||||
|
||||
@@ -17,15 +17,18 @@
|
||||
package dev.patrickgold.florisboard.ime.core
|
||||
|
||||
import android.content.Context
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.keyboard.CurrencySet
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogDebug
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
|
||||
val SubtypeJsonConfig = Json {
|
||||
encodeDefaults = true
|
||||
@@ -38,8 +41,9 @@ val SubtypeJsonConfig = Json {
|
||||
* helper methods for the in-keyboard language switch process.
|
||||
*/
|
||||
class SubtypeManager(context: Context) {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
private val scope = CoroutineScope(Dispatchers.Default)
|
||||
|
||||
private val _subtypesFlow = MutableStateFlow(listOf<Subtype>())
|
||||
val subtypesFlow = _subtypesFlow.asStateFlow()
|
||||
@@ -54,7 +58,7 @@ class SubtypeManager(context: Context) {
|
||||
private set(v) { _activeSubtypeFlow.value = v }
|
||||
|
||||
init {
|
||||
prefs.localization.subtypes.observeForever { listRaw ->
|
||||
prefs.localization.subtypes.asFlow().collectLatestIn(scope) { listRaw ->
|
||||
flogDebug { listRaw }
|
||||
val list = if (listRaw.isNotBlank()) {
|
||||
SubtypeJsonConfig.decodeFromString<List<Subtype>>(listRaw)
|
||||
@@ -66,7 +70,7 @@ class SubtypeManager(context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun persistNewSubtypeList(list: List<Subtype>) {
|
||||
private fun persistNewSubtypeList(list: List<Subtype>) = scope.launch {
|
||||
val listRaw = SubtypeJsonConfig.encodeToString(list)
|
||||
prefs.localization.subtypes.set(listRaw)
|
||||
}
|
||||
@@ -78,7 +82,7 @@ class SubtypeManager(context: Context) {
|
||||
* @return The active subtype or null, if the subtype list is empty or no new active subtype
|
||||
* could be determined.
|
||||
*/
|
||||
private fun evaluateActiveSubtype(list: List<Subtype>) {
|
||||
private fun evaluateActiveSubtype(list: List<Subtype>) = scope.launch {
|
||||
val activeSubtypeId = prefs.localization.activeSubtypeId.get()
|
||||
val subtype = list.find { it.id == activeSubtypeId } ?: list.firstOrNull() ?: Subtype.DEFAULT
|
||||
if (subtype.id != activeSubtypeId) {
|
||||
@@ -184,7 +188,7 @@ class SubtypeManager(context: Context) {
|
||||
/**
|
||||
* Switch to the previous subtype in the subtype list if possible.
|
||||
*/
|
||||
fun switchToPrevSubtype() {
|
||||
fun switchToPrevSubtype() = scope.launch {
|
||||
val subtypeList = subtypes
|
||||
val cachedActiveSubtype = activeSubtype
|
||||
var triggerNextSubtype = false
|
||||
@@ -207,7 +211,7 @@ class SubtypeManager(context: Context) {
|
||||
/**
|
||||
* Switch to the next subtype in the subtype list if possible.
|
||||
*/
|
||||
fun switchToNextSubtype() {
|
||||
fun switchToNextSubtype() = scope.launch {
|
||||
val subtypeList = subtypes
|
||||
val cachedActiveSubtype = activeSubtype
|
||||
var triggerNextSubtype = false
|
||||
@@ -227,7 +231,7 @@ class SubtypeManager(context: Context) {
|
||||
activeSubtype = newActiveSubtype
|
||||
}
|
||||
|
||||
fun switchToSubtypeById(id: Long) {
|
||||
fun switchToSubtypeById(id: Long) = scope.launch {
|
||||
if (subtypes.any { it.id == id }) {
|
||||
activeSubtype = getSubtypeById(id)!!
|
||||
prefs.localization.activeSubtypeId.set(id)
|
||||
|
||||
@@ -18,7 +18,7 @@ package dev.patrickgold.florisboard.ime.dictionary
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.nlp.SuggestionCandidate
|
||||
import dev.patrickgold.florisboard.ime.nlp.WordSuggestionCandidate
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
@@ -29,7 +29,7 @@ import java.lang.ref.WeakReference
|
||||
*/
|
||||
class DictionaryManager private constructor(context: Context) {
|
||||
private val applicationContext: WeakReference<Context> = WeakReference(context.applicationContext ?: context)
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
|
||||
private var florisUserDictionaryDatabase: FlorisUserDictionaryDatabase? = null
|
||||
private var systemUserDictionaryDatabase: SystemUserDictionaryDatabase? = null
|
||||
|
||||
@@ -19,39 +19,38 @@ package dev.patrickgold.florisboard.ime.editor
|
||||
import android.content.ClipDescription
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.KeyEvent
|
||||
import androidx.core.view.inputmethod.InputConnectionCompat
|
||||
import androidx.core.view.inputmethod.InputContentInfoCompat
|
||||
import dev.patrickgold.florisboard.FlorisImeService
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.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.KeyboardMode
|
||||
import dev.patrickgold.florisboard.ime.input.InputShiftState
|
||||
import dev.patrickgold.florisboard.ime.keyboard.IncognitoMode
|
||||
import dev.patrickgold.florisboard.ime.keyboard.KeyboardMode
|
||||
import dev.patrickgold.florisboard.ime.nlp.SuggestionCandidate
|
||||
import dev.patrickgold.florisboard.ime.text.composing.Appender
|
||||
import dev.patrickgold.florisboard.ime.text.composing.Composer
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyVariation
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
|
||||
import dev.patrickgold.florisboard.nlpManager
|
||||
import dev.patrickgold.florisboard.subtypeManager
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
|
||||
class EditorInstance(context: Context) : AbstractEditorInstance(context) {
|
||||
companion object {
|
||||
private const val SPACE = " "
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val appContext by context.appContext()
|
||||
private val clipboardManager by context.clipboardManager()
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
@@ -324,16 +323,7 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
|
||||
)
|
||||
val ic = currentInputConnection() ?: return false
|
||||
ic.finishComposingText()
|
||||
var flags = 0
|
||||
if (AndroidVersion.ATLEAST_API25_N_MR1) {
|
||||
flags = flags or InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION
|
||||
} else {
|
||||
appContext.grantUriPermission(
|
||||
activeInfo.packageName,
|
||||
item.uri,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION,
|
||||
)
|
||||
}
|
||||
val flags = InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION
|
||||
InputConnectionCompat.commitContent(ic, activeInfo.base, inputContentInfo, flags, null)
|
||||
}
|
||||
}
|
||||
@@ -404,7 +394,7 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
|
||||
if (text != null) {
|
||||
clipboardManager.addNewPlaintext(text.toString())
|
||||
} else {
|
||||
appContext.showShortToast("Failed to retrieve selected text requested to cut: Eiter selection state is invalid or an error occurred within the input connection.")
|
||||
appContext.showShortToastSync("Failed to retrieve selected text requested to cut: Eiter selection state is invalid or an error occurred within the input connection.")
|
||||
}
|
||||
return deleteBackwards()
|
||||
}
|
||||
@@ -422,7 +412,7 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
|
||||
if (text != null) {
|
||||
clipboardManager.addNewPlaintext(text.toString())
|
||||
} else {
|
||||
appContext.showShortToast("Failed to retrieve selected text requested to copy: Eiter selection state is invalid or an error occurred within the input connection.")
|
||||
appContext.showShortToastSync("Failed to retrieve selected text requested to copy: Eiter selection state is invalid or an error occurred within the input connection.")
|
||||
}
|
||||
val activeSelection = activeContent.selection
|
||||
return setSelection(activeSelection.end, activeSelection.end)
|
||||
@@ -439,7 +429,7 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
|
||||
phantomSpace.setInactive()
|
||||
return commitClipboardItem(clipboardManager.primaryClip).also { result ->
|
||||
if (!result) {
|
||||
appContext.showShortToast("Failed to paste item.")
|
||||
appContext.showShortToastSync("Failed to paste item.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import android.view.ViewConfiguration
|
||||
import androidx.collection.SparseArrayCompat
|
||||
import androidx.collection.isNotEmpty
|
||||
import androidx.collection.set
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.keyboard.KeyData
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
@@ -48,7 +48,7 @@ class InputEventDispatcher private constructor(private val repeatableKeyCodes: I
|
||||
fun new(repeatableKeyCodes: IntArray = intArrayOf()) = InputEventDispatcher(repeatableKeyCodes.clone())
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
|
||||
|
||||
private val pressedKeys = guardedByLock { SparseArrayCompat<PressedKeyInfo>() }
|
||||
|
||||
@@ -21,7 +21,7 @@ import android.media.AudioManager
|
||||
import android.provider.Settings
|
||||
import android.view.HapticFeedbackConstants
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.keyboard.KeyData
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
@@ -46,7 +46,7 @@ class InputFeedbackController private constructor(private val ims: InputMethodSe
|
||||
fun new(ims: InputMethodService) = InputFeedbackController(ims)
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
|
||||
private val audioManager = ims.systemServiceOrNull(AudioManager::class)
|
||||
private val vibrator = ims.systemVibratorOrNull()
|
||||
|
||||
@@ -36,8 +36,7 @@ import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.smartbar.ExtendedActionsPlacement
|
||||
import dev.patrickgold.florisboard.ime.smartbar.SmartbarLayout
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyboard
|
||||
@@ -80,7 +79,7 @@ object FlorisImeSizing {
|
||||
|
||||
@Composable
|
||||
fun smartbarUiHeight(): Dp {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val smartbarEnabled by prefs.smartbar.enabled.observeAsState()
|
||||
val smartbarLayout by prefs.smartbar.layout.observeAsState()
|
||||
val extendedActionsExpanded by prefs.smartbar.extendedActionsExpanded.observeAsState()
|
||||
@@ -113,7 +112,7 @@ object FlorisImeSizing {
|
||||
|
||||
@Composable
|
||||
fun ProvideKeyboardRowBaseHeight(content: @Composable () -> Unit) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val resources = LocalContext.current.resources
|
||||
val configuration = LocalConfiguration.current
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import dev.patrickgold.florisboard.FlorisImeService
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
@@ -74,7 +74,9 @@ import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.florisboard.lib.android.AndroidKeyguardManager
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.android.systemService
|
||||
import org.florisboard.lib.kotlin.collectIn
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
@@ -83,7 +85,7 @@ import java.util.concurrent.atomic.AtomicInteger
|
||||
private val DoubleSpacePeriodMatcher = """([^.!?‽\s]\s)""".toRegex()
|
||||
|
||||
class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val appContext by context.appContext()
|
||||
private val clipboardManager by context.clipboardManager()
|
||||
private val editorInstance by context.editorInstance()
|
||||
@@ -129,21 +131,21 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
keyboardCache.clear()
|
||||
}
|
||||
}
|
||||
prefs.keyboard.numberRow.observeForever {
|
||||
prefs.keyboard.numberRow.asFlow().collectLatestIn(scope) {
|
||||
updateActiveEvaluators {
|
||||
keyboardCache.clear(KeyboardMode.CHARACTERS)
|
||||
}
|
||||
}
|
||||
prefs.keyboard.hintedNumberRowEnabled.observeForever {
|
||||
prefs.keyboard.hintedNumberRowEnabled.asFlow().collectLatestIn(scope) {
|
||||
updateActiveEvaluators()
|
||||
}
|
||||
prefs.keyboard.hintedSymbolsEnabled.observeForever {
|
||||
prefs.keyboard.hintedSymbolsEnabled.asFlow().collectLatestIn(scope) {
|
||||
updateActiveEvaluators()
|
||||
}
|
||||
prefs.keyboard.utilityKeyEnabled.observeForever {
|
||||
prefs.keyboard.utilityKeyEnabled.asFlow().collectLatestIn(scope) {
|
||||
updateActiveEvaluators()
|
||||
}
|
||||
prefs.keyboard.utilityKeyAction.observeForever {
|
||||
prefs.keyboard.utilityKeyAction.asFlow().collectLatestIn(scope) {
|
||||
updateActiveEvaluators()
|
||||
}
|
||||
activeState.collectLatestIn(scope) {
|
||||
@@ -164,10 +166,10 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
editorInstance.activeContentFlow.collectIn(scope) { content ->
|
||||
resetSuggestions(content)
|
||||
}
|
||||
prefs.devtools.enabled.observeForever {
|
||||
prefs.devtools.enabled.asFlow().collectLatestIn(scope) {
|
||||
reevaluateDebugFlags()
|
||||
}
|
||||
prefs.devtools.showDragAndDropHelpers.observeForever {
|
||||
prefs.devtools.showDragAndDropHelpers.asFlow().collectLatestIn(scope) {
|
||||
reevaluateDebugFlags()
|
||||
}
|
||||
}
|
||||
@@ -237,7 +239,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
return subtypeManager.subtypes.size > 1
|
||||
}
|
||||
|
||||
fun toggleOneHandedMode() {
|
||||
suspend fun toggleOneHandedMode() {
|
||||
prefs.keyboard.oneHandedModeEnabled.set(!prefs.keyboard.oneHandedModeEnabled.get())
|
||||
}
|
||||
|
||||
@@ -580,7 +582,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
/**
|
||||
* Handles a [KeyCode.TOGGLE_INCOGNITO_MODE] event.
|
||||
*/
|
||||
private fun handleToggleIncognitoMode() {
|
||||
private suspend fun handleToggleIncognitoMode() {
|
||||
prefs.suggestion.forceIncognitoModeFromDynamic.set(!prefs.suggestion.forceIncognitoModeFromDynamic.get())
|
||||
val newState = !activeState.isIncognitoMode
|
||||
activeState.isIncognitoMode = newState
|
||||
@@ -606,7 +608,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
private fun handleToggleAutocorrect() {
|
||||
lastToastReference.get()?.cancel()
|
||||
lastToastReference = WeakReference(
|
||||
appContext.showLongToast("Autocorrect toggle is a placeholder and not yet implemented")
|
||||
appContext.showLongToastSync("Autocorrect toggle is a placeholder and not yet implemented")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -716,14 +718,14 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
clipboardManager.primaryClip?.let { clipboardManager.deleteClip(it) }
|
||||
}
|
||||
clipboardManager.updatePrimaryClip(null)
|
||||
appContext.showShortToast(R.string.clipboard__cleared_primary_clip)
|
||||
appContext.showShortToastSync(R.string.clipboard__cleared_primary_clip)
|
||||
}
|
||||
KeyCode.TOGGLE_COMPACT_LAYOUT -> toggleOneHandedMode()
|
||||
KeyCode.COMPACT_LAYOUT_TO_LEFT -> {
|
||||
KeyCode.TOGGLE_COMPACT_LAYOUT -> scope.launch { toggleOneHandedMode() }
|
||||
KeyCode.COMPACT_LAYOUT_TO_LEFT -> scope.launch {
|
||||
prefs.keyboard.oneHandedMode.set(OneHandedMode.START)
|
||||
toggleOneHandedMode()
|
||||
}
|
||||
KeyCode.COMPACT_LAYOUT_TO_RIGHT -> {
|
||||
KeyCode.COMPACT_LAYOUT_TO_RIGHT -> scope.launch {
|
||||
prefs.keyboard.oneHandedMode.set(OneHandedMode.END)
|
||||
toggleOneHandedMode()
|
||||
}
|
||||
@@ -753,7 +755,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
}
|
||||
KeyCode.SYSTEM_PREV_INPUT_METHOD -> FlorisImeService.switchToPrevInputMethod()
|
||||
KeyCode.SYSTEM_NEXT_INPUT_METHOD -> FlorisImeService.switchToNextInputMethod()
|
||||
KeyCode.TOGGLE_SMARTBAR_VISIBILITY -> {
|
||||
KeyCode.TOGGLE_SMARTBAR_VISIBILITY -> scope.launch {
|
||||
prefs.smartbar.enabled.let { it.set(!it.get()) }
|
||||
}
|
||||
KeyCode.TOGGLE_ACTIONS_OVERFLOW -> {
|
||||
@@ -762,7 +764,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
KeyCode.TOGGLE_ACTIONS_EDITOR -> {
|
||||
activeState.isActionsEditorVisible = !activeState.isActionsEditorVisible
|
||||
}
|
||||
KeyCode.TOGGLE_INCOGNITO_MODE -> handleToggleIncognitoMode()
|
||||
KeyCode.TOGGLE_INCOGNITO_MODE -> scope.launch { handleToggleIncognitoMode() }
|
||||
KeyCode.TOGGLE_AUTOCORRECT -> handleToggleAutocorrect()
|
||||
KeyCode.UNDO -> editorInstance.performUndo()
|
||||
KeyCode.VIEW_CHARACTERS -> activeState.keyboardMode = KeyboardMode.CHARACTERS
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package dev.patrickgold.florisboard.ime.keyboard
|
||||
|
||||
import android.content.Context
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
@@ -78,7 +78,7 @@ data class DebugLayoutComputationResult(
|
||||
* Class which manages layout loading and caching.
|
||||
*/
|
||||
class LayoutManager(context: Context) {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val appContext by context.appContext()
|
||||
private val extensionManager by context.extensionManager()
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
|
||||
@@ -16,13 +16,12 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.media.emoji
|
||||
|
||||
import dev.patrickgold.florisboard.app.AppPrefs
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogError
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceSerializer
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@Serializable
|
||||
@@ -76,7 +75,7 @@ data class EmojiHistory(
|
||||
object EmojiHistoryHelper {
|
||||
private var emojiGuard = Mutex(locked = false)
|
||||
|
||||
suspend fun markEmojiUsed(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
suspend fun markEmojiUsed(prefs: FlorisPreferenceModel, emoji: Emoji): Unit = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
@@ -121,7 +120,7 @@ object EmojiHistoryHelper {
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun pinEmoji(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
suspend fun pinEmoji(prefs: FlorisPreferenceModel, emoji: Emoji): Unit = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
@@ -138,7 +137,7 @@ object EmojiHistoryHelper {
|
||||
prefs.emoji.historyData.set(dataMut.build())
|
||||
}
|
||||
|
||||
suspend fun unpinEmoji(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
suspend fun unpinEmoji(prefs: FlorisPreferenceModel, emoji: Emoji): Unit = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
@@ -155,7 +154,7 @@ object EmojiHistoryHelper {
|
||||
prefs.emoji.historyData.set(dataMut.build())
|
||||
}
|
||||
|
||||
suspend fun moveEmoji(prefs: AppPrefs, emoji: Emoji, offset: Int) = emojiGuard.withLock {
|
||||
suspend fun moveEmoji(prefs: FlorisPreferenceModel, emoji: Emoji, offset: Int): Unit = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get() || offset == 0) {
|
||||
return
|
||||
}
|
||||
@@ -175,7 +174,7 @@ object EmojiHistoryHelper {
|
||||
prefs.emoji.historyData.set(dataMut.build())
|
||||
}
|
||||
|
||||
suspend fun removeEmoji(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
suspend fun removeEmoji(prefs: FlorisPreferenceModel, emoji: Emoji): Unit = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
@@ -195,7 +194,7 @@ object EmojiHistoryHelper {
|
||||
prefs.emoji.historyData.set(dataMut.build())
|
||||
}
|
||||
|
||||
suspend fun deleteHistory(prefs: AppPrefs) = emojiGuard.withLock {
|
||||
suspend fun deleteHistory(prefs: FlorisPreferenceModel): Unit = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
@@ -203,7 +202,7 @@ object EmojiHistoryHelper {
|
||||
prefs.emoji.historyData.set(EmojiHistory(pinned = dataMut.pinned, listOf()))
|
||||
}
|
||||
|
||||
suspend fun deletePinned(prefs: AppPrefs) = emojiGuard.withLock {
|
||||
suspend fun deletePinned(prefs: FlorisPreferenceModel): Unit = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -46,8 +45,6 @@ import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.PushPin
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.TabRowDefaults
|
||||
@@ -83,7 +80,7 @@ import androidx.compose.ui.window.Popup
|
||||
import androidx.emoji2.text.EmojiCompat
|
||||
import androidx.emoji2.widget.EmojiTextView
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.ime.input.LocalInputFeedbackController
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
@@ -133,7 +130,7 @@ fun EmojiPaletteView(
|
||||
fullEmojiMappings: EmojiData,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val editorInstance by context.editorInstance()
|
||||
val keyboardManager by context.keyboardManager()
|
||||
@@ -497,7 +494,7 @@ private fun EmojiHistoryPopup(
|
||||
onHistoryAction: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val scope = rememberCoroutineScope()
|
||||
val emojiKeyHeight = FlorisImeSizing.smartbarHeight
|
||||
val context = LocalContext.current
|
||||
|
||||
@@ -20,7 +20,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.stream.Collectors
|
||||
import android.content.Context
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.editor.EditorContent
|
||||
import dev.patrickgold.florisboard.ime.nlp.EmojiSuggestionCandidate
|
||||
@@ -41,7 +41,7 @@ import io.github.reactivecircus.cache4k.Cache
|
||||
class EmojiSuggestionProvider(private val context: Context) : SuggestionProvider {
|
||||
override val providerId = "org.florisboard.nlp.providers.emoji"
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val lettersRegex = "^[A-Za-z]*$".toRegex()
|
||||
|
||||
private val cachedEmojiMappings = Cache.Builder().build<FlorisLocale, EmojiDataBySkinTone>()
|
||||
|
||||
@@ -20,7 +20,7 @@ import android.content.Context
|
||||
import android.os.SystemClock
|
||||
import android.util.LruCache
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardItem
|
||||
@@ -43,8 +43,8 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
import org.florisboard.lib.kotlin.guardedByLock
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.properties.Delegates
|
||||
@@ -54,7 +54,7 @@ private const val BLANK_STR_PATTERN = "^\\s*$"
|
||||
class NlpManager(context: Context) {
|
||||
private val blankStrRegex = Regex(BLANK_STR_PATTERN)
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val clipboardManager by context.clipboardManager()
|
||||
private val editorInstance by context.editorInstance()
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
@@ -93,13 +93,13 @@ class NlpManager(context: Context) {
|
||||
clipboardManager.primaryClipFlow.collectLatestIn(scope) {
|
||||
assembleCandidates()
|
||||
}
|
||||
prefs.suggestion.enabled.observeForever {
|
||||
prefs.suggestion.enabled.asFlow().collectLatestIn(scope) {
|
||||
assembleCandidates()
|
||||
}
|
||||
prefs.clipboard.suggestionEnabled.observeForever {
|
||||
prefs.clipboard.suggestionEnabled.asFlow().collectLatestIn(scope) {
|
||||
assembleCandidates()
|
||||
}
|
||||
prefs.emoji.suggestionEnabled.observeForever {
|
||||
prefs.emoji.suggestionEnabled.asFlow().collectLatestIn(scope) {
|
||||
assembleCandidates()
|
||||
}
|
||||
subtypeManager.activeSubtypeFlow.collectLatestIn(scope) { subtype ->
|
||||
@@ -317,8 +317,10 @@ class NlpManager(context: Context) {
|
||||
}*/
|
||||
val isSelection = editorInstance.activeContent.selection.isSelectionMode
|
||||
val isExpanded = list1.isNullOrEmpty() && list2.isNullOrEmpty() || isSelection
|
||||
prefs.smartbar.sharedActionsExpandWithAnimation.set(false)
|
||||
prefs.smartbar.sharedActionsExpanded.set(isExpanded)
|
||||
scope.launch {
|
||||
prefs.smartbar.sharedActionsExpandWithAnimation.set(false)
|
||||
prefs.smartbar.sharedActionsExpanded.set(isExpanded)
|
||||
}
|
||||
}
|
||||
|
||||
fun addToDebugOverlay(word: String, info: SpellingResult) {
|
||||
|
||||
@@ -26,14 +26,16 @@ import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.filled.ZoomOutMap
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.input.LocalInputFeedbackController
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.snygg.ui.SnyggColumn
|
||||
import org.florisboard.lib.snygg.ui.SnyggIcon
|
||||
import org.florisboard.lib.snygg.ui.SnyggIconButton
|
||||
@@ -44,7 +46,8 @@ fun RowScope.OneHandedPanel(
|
||||
panelSide: OneHandedMode,
|
||||
weight: Float,
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val scope = rememberCoroutineScope()
|
||||
val inputFeedbackController = LocalInputFeedbackController.current
|
||||
|
||||
SnyggColumn(
|
||||
@@ -58,8 +61,10 @@ fun RowScope.OneHandedPanel(
|
||||
SnyggIconButton(
|
||||
FlorisImeUi.OneHandedPanelButton.elementName,
|
||||
onClick = {
|
||||
inputFeedbackController.keyPress()
|
||||
prefs.keyboard.oneHandedModeEnabled.set(false)
|
||||
scope.launch {
|
||||
inputFeedbackController.keyPress()
|
||||
prefs.keyboard.oneHandedModeEnabled.set(false)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -74,8 +79,10 @@ fun RowScope.OneHandedPanel(
|
||||
SnyggIconButton(
|
||||
FlorisImeUi.OneHandedPanelButton.elementName,
|
||||
onClick = {
|
||||
inputFeedbackController.keyPress()
|
||||
prefs.keyboard.oneHandedMode.set(panelSide)
|
||||
scope.launch {
|
||||
inputFeedbackController.keyPress()
|
||||
prefs.keyboard.oneHandedMode.set(panelSide)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
|
||||
@@ -28,6 +28,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import dev.patrickgold.florisboard.ime.keyboard.Key
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import org.florisboard.lib.snygg.SnyggQueryAttributes
|
||||
@@ -58,7 +59,7 @@ fun PopupBaseBox(
|
||||
.align(Alignment.TopCenter),
|
||||
) {
|
||||
SnyggText(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
modifier = Modifier.align(Alignment.Center).zIndex(100f),
|
||||
text = label,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.zIndex
|
||||
import dev.patrickgold.florisboard.ime.keyboard.ComputingEvaluator
|
||||
import dev.patrickgold.florisboard.ime.keyboard.DefaultComputingEvaluator
|
||||
import dev.patrickgold.florisboard.ime.keyboard.Key
|
||||
@@ -38,6 +39,7 @@ import dev.patrickgold.florisboard.ime.keyboard.KeyData
|
||||
import dev.patrickgold.florisboard.ime.keyboard.computeImageVector
|
||||
import dev.patrickgold.florisboard.ime.keyboard.computeLabel
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSet
|
||||
import dev.patrickgold.florisboard.ime.smartbar.Temp
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyHintConfiguration
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKey
|
||||
@@ -452,11 +454,12 @@ class PopupUiController(
|
||||
FlorisImeUi.Attr.Mode to evaluator.keyboard.mode.toString(),
|
||||
FlorisImeUi.Attr.ShiftState to evaluator.state.inputShiftState.toString(),
|
||||
)
|
||||
Temp = !(baseRenderInfo != null || extRenderInfo != null)
|
||||
baseRenderInfo?.let { renderInfo ->
|
||||
PopupBaseBox(
|
||||
modifier = Modifier
|
||||
.requiredSize(renderInfo.bounds.size.toDpSize())
|
||||
.absoluteOffset { renderInfo.bounds.topLeft.toIntOffset() },
|
||||
.absoluteOffset { renderInfo.bounds.topLeft.toIntOffset() }.zIndex(100f),
|
||||
attributes = attributes,
|
||||
key = renderInfo.key,
|
||||
shouldIndicateExtendedPopups = renderInfo.shouldIndicateExtendedPopups && extRenderInfo == null,
|
||||
|
||||
@@ -20,7 +20,6 @@ import androidx.compose.foundation.gestures.awaitEachGesture
|
||||
import androidx.compose.foundation.gestures.awaitFirstDown
|
||||
import androidx.compose.foundation.gestures.waitForUpOrCancellation
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.width
|
||||
@@ -40,7 +39,7 @@ import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.nlp.ClipboardSuggestionCandidate
|
||||
import dev.patrickgold.florisboard.ime.nlp.SuggestionCandidate
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
@@ -62,7 +61,7 @@ val CandidatesRowScrollbarHeight = 2.dp
|
||||
|
||||
@Composable
|
||||
fun CandidatesRow(modifier: Modifier = Modifier) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val nlpManager by context.nlpManager()
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.smartbar
|
||||
|
||||
import android.graphics.PixelFormat
|
||||
import android.os.Build
|
||||
import android.view.SurfaceView
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
@@ -32,16 +35,18 @@ import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.layout.positionInParent
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.view.forEach
|
||||
import dev.patrickgold.florisboard.ime.nlp.NlpInlineAutofillSuggestion
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
|
||||
import dev.patrickgold.florisboard.lib.toIntOffset
|
||||
import org.florisboard.lib.snygg.SnyggPropertySet
|
||||
import org.florisboard.lib.snygg.SnyggSinglePropertySet
|
||||
import org.florisboard.lib.snygg.ui.rememberSnyggThemeQuery
|
||||
|
||||
var CachedInlineSuggestionsChipStyleSet: SnyggSinglePropertySet? = null
|
||||
|
||||
var Temp: Boolean = false
|
||||
|
||||
@Composable
|
||||
fun InlineSuggestionsStyleCache() {
|
||||
val chipStyleSet = rememberSnyggThemeQuery(FlorisImeUi.InlineAutofillChip.elementName)
|
||||
@@ -59,13 +64,16 @@ fun InlineSuggestionsUi(
|
||||
val scrollState = rememberScrollState()
|
||||
val almostEmptyRect = remember { android.graphics.Rect(0, 0, 1, 1) }
|
||||
|
||||
val backgroundColor = rememberSnyggThemeQuery(FlorisImeUi.SmartbarCandidatesRow.elementName).background()
|
||||
|
||||
Row(
|
||||
modifier
|
||||
.fillMaxSize()
|
||||
.florisHorizontalScroll(
|
||||
state = scrollState,
|
||||
scrollbarHeight = CandidatesRowScrollbarHeight,
|
||||
),
|
||||
)
|
||||
.background(backgroundColor),
|
||||
) {
|
||||
val xMin = scrollState.value
|
||||
val xMax = scrollState.value + scrollState.viewportSize
|
||||
@@ -73,6 +81,16 @@ fun InlineSuggestionsUi(
|
||||
if (inlineSuggestion.view == null) {
|
||||
continue
|
||||
}
|
||||
//inlineSuggestion.view.background = ColorDrawable(backgroundColor.toArgb())
|
||||
inlineSuggestion.view.forEach {
|
||||
with (it as SurfaceView) {
|
||||
//this.setBackgroundColor(backgroundColor.toArgb())
|
||||
setZOrderOnTop(false)
|
||||
holder.setFormat(PixelFormat.OPAQUE)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
var chipPos by remember { mutableStateOf(IntOffset.Zero) }
|
||||
AndroidView(
|
||||
modifier = Modifier.onGloballyPositioned { chipPos = it.positionInParent().toIntOffset() },
|
||||
|
||||
@@ -24,7 +24,6 @@ import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.absoluteOffset
|
||||
@@ -43,6 +42,7 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
@@ -52,7 +52,7 @@ import androidx.compose.ui.graphics.isUnspecified
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.nlp.NlpInlineAutofill
|
||||
import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionButton
|
||||
@@ -65,6 +65,7 @@ import dev.patrickgold.florisboard.lib.compose.verticalTween
|
||||
import dev.patrickgold.florisboard.nlpManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.vectorResource
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggColumn
|
||||
@@ -89,7 +90,7 @@ private val NoAnimationTween = tween<Float>(0)
|
||||
|
||||
@Composable
|
||||
fun Smartbar() {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val smartbarEnabled by prefs.smartbar.enabled.observeAsState()
|
||||
val extendedActionsPlacement by prefs.smartbar.extendedActionsPlacement.observeAsState()
|
||||
|
||||
@@ -138,10 +139,12 @@ fun Smartbar() {
|
||||
|
||||
@Composable
|
||||
private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val activeEvaluator by keyboardManager.activeEvaluator.collectAsState()
|
||||
val nlpManager by context.nlpManager()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val inlineSuggestions by NlpInlineAutofill.suggestions.collectAsState()
|
||||
LaunchedEffect(inlineSuggestions) {
|
||||
@@ -164,7 +167,9 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
if (/* was */ sharedActionsExpanded) {
|
||||
keyboardManager.activeState.isActionsOverflowVisible = false
|
||||
}
|
||||
prefs.smartbar.sharedActionsExpanded.set(!sharedActionsExpanded)
|
||||
scope.launch {
|
||||
prefs.smartbar.sharedActionsExpanded.set(!sharedActionsExpanded)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.sizeIn(maxHeight = FlorisImeSizing.smartbarHeight).aspectRatio(1f)
|
||||
) {
|
||||
@@ -244,7 +249,9 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
if (/* was */ extendedActionsExpanded) {
|
||||
keyboardManager.activeState.isActionsOverflowVisible = false
|
||||
}
|
||||
prefs.smartbar.extendedActionsExpanded.set(!extendedActionsExpanded)
|
||||
scope.launch {
|
||||
prefs.smartbar.extendedActionsExpanded.set(!extendedActionsExpanded)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.sizeIn(maxHeight = FlorisImeSizing.smartbarHeight).aspectRatio(1f)
|
||||
) {
|
||||
@@ -304,7 +311,9 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
|
||||
SideEffect {
|
||||
if (!shouldAnimate) {
|
||||
prefs.smartbar.sharedActionsExpandWithAnimation.set(true)
|
||||
scope.launch {
|
||||
prefs.smartbar.sharedActionsExpandWithAnimation.set(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,7 +368,7 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
|
||||
@Composable
|
||||
private fun SmartbarSecondaryRow(modifier: Modifier = Modifier) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val smartbarLayout by prefs.smartbar.layout.observeAsState()
|
||||
val secondaryRowStyle = rememberSnyggThemeQuery(FlorisImeUi.SmartbarExtendedActionsRow.elementName)
|
||||
val windowStyle = rememberSnyggThemeQuery(FlorisImeUi.Window.elementName)
|
||||
|
||||
@@ -25,7 +25,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
||||
import androidx.compose.foundation.lazy.grid.LazyGridItemInfo
|
||||
@@ -40,6 +39,7 @@ import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -52,7 +52,7 @@ import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.toSize
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
@@ -60,8 +60,8 @@ import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.toIntOffset
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggButton
|
||||
import org.florisboard.lib.snygg.ui.SnyggColumn
|
||||
import org.florisboard.lib.snygg.ui.SnyggIcon
|
||||
import org.florisboard.lib.snygg.ui.SnyggIconButton
|
||||
@@ -74,8 +74,9 @@ private val DragMarkerAction = QuickAction.InsertKey(TextKeyData(code = KeyCode.
|
||||
|
||||
@Composable
|
||||
fun QuickActionsEditorPanel() {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val keyboardManager by context.keyboardManager()
|
||||
|
||||
// We get the current arrangement once and do not observe on purpose
|
||||
@@ -235,7 +236,9 @@ fun QuickActionsEditorPanel() {
|
||||
dynamicActions.filter { it != NoopAction && it != DragMarkerAction },
|
||||
hiddenActions.filter { it != NoopAction && it != DragMarkerAction },
|
||||
)
|
||||
prefs.smartbar.actionArrangement.set(newActionArrangement)
|
||||
scope.launch {
|
||||
prefs.smartbar.actionArrangement.set(newActionArrangement)
|
||||
}
|
||||
if (keyboardManager.activeState.isActionsEditorVisible) {
|
||||
keyboardManager.activeState.isActionsEditorVisible = false
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
@@ -42,7 +42,7 @@ import org.florisboard.lib.snygg.ui.SnyggText
|
||||
|
||||
@Composable
|
||||
fun QuickActionsOverflowPanel() {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.smartbar.quickaction
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@@ -28,7 +29,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.smartbar.SmartbarLayout
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
@@ -37,12 +38,13 @@ import org.florisboard.lib.snygg.ui.SnyggRow
|
||||
|
||||
internal val ToggleOverflowPanelAction = QuickAction.InsertKey(TextKeyData.TOGGLE_ACTIONS_OVERFLOW)
|
||||
|
||||
@SuppressLint("UnusedBoxWithConstraintsScope")
|
||||
@Composable
|
||||
fun QuickActionsRow(
|
||||
elementName: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) = with(LocalDensity.current) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.smartbar.IncognitoDisplayMode
|
||||
import dev.patrickgold.florisboard.ime.smartbar.InlineSuggestionsStyleCache
|
||||
import dev.patrickgold.florisboard.ime.smartbar.Smartbar
|
||||
@@ -49,7 +49,7 @@ fun TextInputLayout(
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
|
||||
val state by keyboardManager.activeState.collectAsState()
|
||||
val evaluator by keyboardManager.activeEvaluator.collectAsState()
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package dev.patrickgold.florisboard.ime.text.gestures
|
||||
|
||||
import android.content.Context
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.nlp.WordSuggestionCandidate
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKey
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
@@ -39,7 +39,7 @@ class GlideTypingManager(context: Context) : GlideTypingGesture.Listener {
|
||||
private const val MAX_SUGGESTION_COUNT = 8
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
private val nlpManager by context.nlpManager()
|
||||
private val subtypeManager by context.subtypeManager()
|
||||
|
||||
@@ -19,7 +19,7 @@ package dev.patrickgold.florisboard.ime.text.gestures
|
||||
import android.view.MotionEvent
|
||||
import android.view.VelocityTracker
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.lib.Pointer
|
||||
import dev.patrickgold.florisboard.lib.PointerMap
|
||||
import dev.patrickgold.florisboard.lib.devtools.LogTopic
|
||||
@@ -39,7 +39,7 @@ abstract class SwipeGesture {
|
||||
* @property listener The listener to report detected swipes to.
|
||||
*/
|
||||
class Detector(private val listener: Listener) {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
|
||||
var isEnabled: Boolean = true
|
||||
private var pointerMap: PointerMap<GesturePointer> = PointerMap { GesturePointer() }
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.text.keyboard
|
||||
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.keyboard.AbstractKeyData
|
||||
import dev.patrickgold.florisboard.ime.keyboard.ComputingEvaluator
|
||||
import dev.patrickgold.florisboard.ime.keyboard.Key
|
||||
@@ -246,7 +246,7 @@ class TextKey(override val data: AbstractKeyData) : Key(data) {
|
||||
else -> null
|
||||
}
|
||||
} else if (!data.isSpaceKey() || data.type == KeyType.NUMERIC) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
computedPopups.getPopupKeys(prefs.keyboard.keyHintConfiguration()).hint.let { hintData ->
|
||||
if (hintData?.isSpaceKey() == false) {
|
||||
hintedLabel = hintData.asString(isForDisplay = true)
|
||||
|
||||
@@ -56,7 +56,7 @@ import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.toSize
|
||||
import dev.patrickgold.florisboard.FlorisImeService
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.glideTypingManager
|
||||
import dev.patrickgold.florisboard.ime.input.InputEventDispatcher
|
||||
@@ -104,7 +104,7 @@ fun TextKeyboardLayout(
|
||||
evaluator: ComputingEvaluator,
|
||||
isPreview: Boolean = false,
|
||||
): Unit = with(LocalDensity.current) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val configuration = LocalConfiguration.current
|
||||
val glideTypingManager by context.glideTypingManager()
|
||||
@@ -132,7 +132,7 @@ fun TextKeyboardLayout(
|
||||
controller.onTouchEventInternal(event)
|
||||
controller.popupUiController.hide()
|
||||
event.recycle()
|
||||
} catch (e: Throwable) {
|
||||
} catch (_: Throwable) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
@@ -167,9 +167,9 @@ fun TextKeyboardLayout(
|
||||
MotionEvent.ACTION_MOVE,
|
||||
MotionEvent.ACTION_POINTER_UP,
|
||||
MotionEvent.ACTION_UP,
|
||||
MotionEvent.ACTION_CANCEL
|
||||
-> {
|
||||
val clonedEvent = MotionEvent.obtainNoHistory(event)
|
||||
MotionEvent.ACTION_CANCEL,
|
||||
-> {
|
||||
val clonedEvent = MotionEvent.obtain(event)
|
||||
touchEventChannel
|
||||
.trySend(clonedEvent)
|
||||
.onFailure {
|
||||
@@ -337,7 +337,7 @@ private fun TextKeyButton(
|
||||
key.label?.let { label ->
|
||||
var customLabel = label
|
||||
if (key.computedData.code == KeyCode.SPACE) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val spaceBarMode by prefs.keyboard.spaceBarMode.observeAsState()
|
||||
when (spaceBarMode) {
|
||||
SpaceBarMode.NOTHING -> return@let
|
||||
@@ -385,7 +385,7 @@ private fun TextKeyButton(
|
||||
private class TextKeyboardLayoutController(
|
||||
context: Context,
|
||||
) : SwipeGesture.Listener, GlideTypingGesture.Listener {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val editorInstance by context.editorInstance()
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
|
||||
|
||||
@@ -27,13 +27,10 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.florisboard.themeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.snygg.SnyggAttributes
|
||||
import org.florisboard.lib.snygg.SnyggQueryAttributes
|
||||
import org.florisboard.lib.snygg.ui.ProvideSnyggTheme
|
||||
import org.florisboard.lib.snygg.ui.rememberSnyggTheme
|
||||
|
||||
@@ -52,10 +49,10 @@ fun FlorisImeTheme(content: @Composable () -> Unit) {
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val themeManager by context.themeManager()
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val accentColor by prefs.theme.accentColor.observeAsState()
|
||||
|
||||
val activeThemeInfo by themeManager.activeThemeInfo.observeAsNonNullState()
|
||||
val activeThemeInfo by themeManager.activeThemeInfo.collectAsState()
|
||||
val activeConfig = remember(activeThemeInfo) { activeThemeInfo.config }
|
||||
val activeStyle = remember(activeThemeInfo) { activeThemeInfo.stylesheet }
|
||||
|
||||
|
||||
@@ -39,10 +39,8 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.smartbar.CachedInlineSuggestionsChipStyleSet
|
||||
@@ -50,70 +48,69 @@ import dev.patrickgold.florisboard.lib.devtools.flogInfo
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionMeta
|
||||
import dev.patrickgold.florisboard.lib.io.ZipUtils
|
||||
import dev.patrickgold.florisboard.lib.util.TimeUtils.javaLocalTime
|
||||
import dev.patrickgold.florisboard.lib.util.ViewUtils
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.florisboard.lib.kotlin.collectIn
|
||||
import org.florisboard.lib.kotlin.io.FsDir
|
||||
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import org.florisboard.lib.snygg.SnyggStylesheet
|
||||
import org.florisboard.lib.snygg.value.SnyggStaticColorValue
|
||||
import java.util.UUID
|
||||
import kotlin.properties.Delegates
|
||||
import java.time.LocalTime
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Core class which manages the keyboard theme. Note, that this does not affect the UI theme of the
|
||||
* Settings Activities.
|
||||
*/
|
||||
class ThemeManager(context: Context) {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val appContext by context.appContext()
|
||||
private val extensionManager by context.extensionManager()
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
|
||||
|
||||
private val _indexedThemeConfigs = MutableLiveData(mapOf<ExtensionComponentName, ThemeExtensionComponent>())
|
||||
val indexedThemeConfigs: LiveData<Map<ExtensionComponentName, ThemeExtensionComponent>> get() = _indexedThemeConfigs
|
||||
var previewThemeId: ExtensionComponentName? by Delegates.observable(null) { _, _, _ ->
|
||||
updateActiveTheme()
|
||||
}
|
||||
var previewThemeInfo: ThemeInfo? by Delegates.observable(null) { _, _, _ ->
|
||||
updateActiveTheme()
|
||||
}
|
||||
private val _indexedThemeConfigs = MutableStateFlow(mapOf<ExtensionComponentName, ThemeExtensionComponent>())
|
||||
val indexedThemeConfigs get() = _indexedThemeConfigs.asStateFlow()
|
||||
val previewThemeId = MutableStateFlow<ExtensionComponentName?>(null)
|
||||
val previewThemeInfo = MutableStateFlow<ThemeInfo?>(null)
|
||||
val wallpaperChangedCounter = MutableStateFlow(0)
|
||||
|
||||
private val cachedThemeInfos = mutableListOf<ThemeInfo>()
|
||||
private val activeThemeGuard = Mutex(locked = false)
|
||||
private val _activeThemeInfo = MutableLiveData(ThemeInfo.DEFAULT)
|
||||
val activeThemeInfo: LiveData<ThemeInfo> get() = _activeThemeInfo
|
||||
private val _activeThemeInfo = MutableStateFlow(ThemeInfo.DEFAULT)
|
||||
val activeThemeInfo get() = _activeThemeInfo.asStateFlow()
|
||||
|
||||
init {
|
||||
extensionManager.themes.observeForever { themeExtensions ->
|
||||
val map = buildMap {
|
||||
_indexedThemeConfigs.value = buildMap {
|
||||
for (themeExtension in themeExtensions) {
|
||||
for (themeComponent in themeExtension.themes) {
|
||||
put(ExtensionComponentName(themeExtension.meta.id, themeComponent.id), themeComponent)
|
||||
}
|
||||
}
|
||||
}
|
||||
_indexedThemeConfigs.postValue(map)
|
||||
}
|
||||
indexedThemeConfigs.observeForever {
|
||||
updateActiveTheme {
|
||||
cachedThemeInfos.clear()
|
||||
}
|
||||
indexedThemeConfigs.collectIn(scope) {
|
||||
updateActiveTheme { cachedThemeInfos.clear() }
|
||||
}
|
||||
prefs.theme.mode.observeForever {
|
||||
updateActiveTheme()
|
||||
}
|
||||
prefs.theme.dayThemeId.observeForever {
|
||||
updateActiveTheme()
|
||||
}
|
||||
prefs.theme.nightThemeId.observeForever {
|
||||
combine(
|
||||
prefs.theme.mode.asFlow(),
|
||||
prefs.theme.dayThemeId.asFlow(),
|
||||
prefs.theme.nightThemeId.asFlow(),
|
||||
previewThemeId,
|
||||
previewThemeInfo,
|
||||
wallpaperChangedCounter,
|
||||
) {}.collectIn(scope) {
|
||||
updateActiveTheme()
|
||||
}
|
||||
}
|
||||
@@ -122,56 +119,54 @@ class ThemeManager(context: Context) {
|
||||
* Updates the current theme ref and loads the corresponding theme, as well as notifies all
|
||||
* callback receivers about the new theme.
|
||||
*/
|
||||
fun updateActiveTheme(action: () -> Unit = { }) = scope.launch {
|
||||
activeThemeGuard.withLock {
|
||||
action()
|
||||
previewThemeInfo?.let { previewThemeInfo ->
|
||||
_activeThemeInfo.postValue(previewThemeInfo)
|
||||
return@withLock
|
||||
}
|
||||
val activeName = evaluateActiveThemeName()
|
||||
val cachedInfo = cachedThemeInfos.find { it.name == activeName }
|
||||
if (cachedInfo != null) {
|
||||
_activeThemeInfo.postValue(cachedInfo)
|
||||
return@withLock
|
||||
}
|
||||
val themeExt = extensionManager.getExtensionById(activeName.extensionId) as? ThemeExtension
|
||||
val themeExtRef = themeExt?.sourceRef
|
||||
if (themeExtRef == null) {
|
||||
return@withLock
|
||||
}
|
||||
val themeConfig = themeExt.themes.find { it.id == activeName.componentId }
|
||||
if (themeConfig == null) {
|
||||
return@withLock
|
||||
}
|
||||
// TODO: loaded dir is implemented already...
|
||||
// TODO: this leaks the loaded dir, but at least the state is not kaputt from compose viewpoint
|
||||
val loadedDir = appContext.cacheDir.subDir("loaded").subDir(UUID.randomUUID().toString())
|
||||
runCatching {
|
||||
loadedDir.mkdirs()
|
||||
loadedDir.deleteContentsRecursively()
|
||||
ZipUtils.unzip(appContext, themeExtRef, loadedDir).getOrThrow()
|
||||
flogInfo { "Loaded extension ${themeExt.meta.id} into $loadedDir" }
|
||||
val stylesheetFile = loadedDir.subFile(themeConfig.stylesheetPath())
|
||||
val stylesheetJson = stylesheetFile.readText()
|
||||
SnyggStylesheet.fromJson(stylesheetJson).getOrThrow()
|
||||
}.fold(
|
||||
onSuccess = { newStylesheet ->
|
||||
val newInfo = ThemeInfo(activeName, themeConfig, newStylesheet, loadedDir, null)
|
||||
cachedThemeInfos.add(newInfo)
|
||||
_activeThemeInfo.postValue(newInfo)
|
||||
},
|
||||
onFailure = { cause ->
|
||||
_activeThemeInfo.postValue(ThemeInfo.DEFAULT.copy(
|
||||
loadFailure = LoadFailure(themeExt.meta, themeConfig, cause)
|
||||
))
|
||||
},
|
||||
)
|
||||
suspend fun updateActiveTheme(action: () -> Unit = { }) = activeThemeGuard.withLock {
|
||||
action()
|
||||
previewThemeInfo.value?.let { previewThemeInfo ->
|
||||
_activeThemeInfo.value = previewThemeInfo
|
||||
return@withLock
|
||||
}
|
||||
val activeName = evaluateActiveThemeName()
|
||||
val cachedInfo = cachedThemeInfos.find { it.name == activeName }
|
||||
if (cachedInfo != null) {
|
||||
_activeThemeInfo.value = cachedInfo
|
||||
return@withLock
|
||||
}
|
||||
val themeExt = extensionManager.getExtensionById(activeName.extensionId) as? ThemeExtension
|
||||
val themeExtRef = themeExt?.sourceRef
|
||||
if (themeExtRef == null) {
|
||||
return@withLock
|
||||
}
|
||||
val themeConfig = themeExt.themes.find { it.id == activeName.componentId }
|
||||
if (themeConfig == null) {
|
||||
return@withLock
|
||||
}
|
||||
// TODO: loaded dir is implemented already...
|
||||
// TODO: this leaks the loaded dir, but at least the state is not kaputt from compose viewpoint
|
||||
val loadedDir = appContext.cacheDir.subDir("loaded").subDir(UUID.randomUUID().toString())
|
||||
runCatching {
|
||||
loadedDir.mkdirs()
|
||||
loadedDir.deleteContentsRecursively()
|
||||
ZipUtils.unzip(appContext, themeExtRef, loadedDir).getOrThrow()
|
||||
flogInfo { "Loaded extension ${themeExt.meta.id} into $loadedDir" }
|
||||
val stylesheetFile = loadedDir.subFile(themeConfig.stylesheetPath())
|
||||
val stylesheetJson = stylesheetFile.readText()
|
||||
SnyggStylesheet.fromJson(stylesheetJson).getOrThrow()
|
||||
}.fold(
|
||||
onSuccess = { newStylesheet ->
|
||||
val newInfo = ThemeInfo(activeName, themeConfig, newStylesheet, loadedDir, null)
|
||||
cachedThemeInfos.add(newInfo)
|
||||
_activeThemeInfo.value = newInfo
|
||||
},
|
||||
onFailure = { cause ->
|
||||
_activeThemeInfo.value = ThemeInfo.DEFAULT.copy(
|
||||
loadFailure = LoadFailure(themeExt.meta, themeConfig, cause)
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun evaluateActiveThemeName(): ExtensionComponentName {
|
||||
previewThemeId?.let { return it }
|
||||
previewThemeId.value?.let { return it }
|
||||
return when (prefs.theme.mode.get()) {
|
||||
ThemeMode.ALWAYS_DAY -> {
|
||||
prefs.theme.dayThemeId.get()
|
||||
@@ -187,27 +182,22 @@ class ThemeManager(context: Context) {
|
||||
prefs.theme.dayThemeId.get()
|
||||
}
|
||||
ThemeMode.FOLLOW_TIME -> {
|
||||
//if (AndroidVersion.ATLEAST_API26_O) {
|
||||
// val current = LocalTime.now()
|
||||
// val sunrise = prefs.theme.sunriseTime.get()
|
||||
// val sunset = prefs.theme.sunsetTime.get()
|
||||
// if (current in sunrise..sunset) {
|
||||
// prefs.theme.dayThemeId.get()
|
||||
// } else {
|
||||
// prefs.theme.nightThemeId.get()
|
||||
// }
|
||||
//} else {
|
||||
val current = LocalTime.now()
|
||||
val sunrise = prefs.theme.sunriseTime.get().javaLocalTime
|
||||
val sunset = prefs.theme.sunsetTime.get().javaLocalTime
|
||||
if (current in sunrise..sunset) {
|
||||
prefs.theme.dayThemeId.get()
|
||||
} else {
|
||||
prefs.theme.nightThemeId.get()
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new inline suggestion UI bundle based on the attributes of the given [style].
|
||||
* Creates a new inline suggestion UI bundle.
|
||||
*
|
||||
* @param context The context of the parent view/controller.
|
||||
* @param style The style set which is responsible for styling the chips.
|
||||
*
|
||||
* @return A bundle containing all necessary attributes for the inline suggestion views to properly display.
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogDebug
|
||||
import dev.patrickgold.florisboard.themeManager
|
||||
import kotlinx.coroutines.flow.update
|
||||
|
||||
class WallpaperChangeReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
@@ -29,7 +30,8 @@ class WallpaperChangeReceiver : BroadcastReceiver() {
|
||||
@Suppress("DEPRECATION") // We do not retrieve the wallpaper but only listen to changes
|
||||
if (intent.action == Intent.ACTION_WALLPAPER_CHANGED) {
|
||||
flogDebug { "Wallpaper changed" }
|
||||
context.themeManager().value.updateActiveTheme()
|
||||
val themeManager by context.themeManager()
|
||||
themeManager.wallpaperChangedCounter.update { it + 1 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
|
||||
package dev.patrickgold.florisboard.lib
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisallowComposableCalls
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.SnapshotMutationPolicy
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.structuralEqualityPolicy
|
||||
@@ -28,21 +30,16 @@ import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceData
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceObserver
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
@SuppressLint("StateFlowValueCalledInComposition")
|
||||
@Composable
|
||||
inline fun <V : Any, R : Any> PreferenceData<V>.observeAsTransformingState(
|
||||
policy: SnapshotMutationPolicy<R> = structuralEqualityPolicy(),
|
||||
crossinline transform: @DisallowComposableCalls (V) -> R,
|
||||
): State<R> {
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
val state = remember(key) { mutableStateOf(transform(get()), policy) }
|
||||
DisposableEffect(this, lifecycleOwner) {
|
||||
val observer = PreferenceObserver<V> { newValue -> state.value = transform(newValue) }
|
||||
observe(lifecycleOwner, observer)
|
||||
onDispose { removeObserver(observer) }
|
||||
return asFlow().let { flow ->
|
||||
flow.map { transform(it) }.collectAsState(transform(flow.value))
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -37,9 +37,9 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.app.AppPrefs
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceLayout
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceUiContent
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
@@ -53,7 +53,7 @@ fun FlorisScreen(builder: @Composable FlorisScreenScope.() -> Unit) {
|
||||
|
||||
typealias FlorisScreenActions = @Composable RowScope.() -> Unit
|
||||
typealias FlorisScreenBottomBar = @Composable () -> Unit
|
||||
typealias FlorisScreenContent = PreferenceUiContent<AppPrefs>
|
||||
typealias FlorisScreenContent = PreferenceUiContent<FlorisPreferenceModel>
|
||||
typealias FlorisScreenFab = @Composable () -> Unit
|
||||
typealias FlorisScreenNavigationIcon = @Composable () -> Unit
|
||||
|
||||
@@ -152,7 +152,7 @@ private class FlorisScreenScopeImpl : FlorisScreenScope {
|
||||
Modifier
|
||||
}
|
||||
PreferenceLayout(
|
||||
florisPreferenceModel(),
|
||||
FlorisPreferenceStore,
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxWidth()
|
||||
|
||||
@@ -168,10 +168,11 @@ fun FlorisStepLayout(
|
||||
) {
|
||||
val scope = FlorisStepLayoutScope(this, primaryColor)
|
||||
header(scope)
|
||||
for (step in steps) {
|
||||
for ((index, step) in steps.withIndex()) {
|
||||
key(step.id) {
|
||||
Step(
|
||||
ownStepId = step.id,
|
||||
index = index + 1, // Start numbering with 1
|
||||
stepState = stepState,
|
||||
title = step.title,
|
||||
primaryColor = primaryColor,
|
||||
@@ -188,6 +189,7 @@ fun FlorisStepLayout(
|
||||
@Composable
|
||||
private fun ColumnScope.Step(
|
||||
ownStepId: Int,
|
||||
index: Int,
|
||||
stepState: FlorisStepState,
|
||||
title: String,
|
||||
primaryColor: Color,
|
||||
@@ -207,7 +209,7 @@ private fun ColumnScope.Step(
|
||||
else -> Modifier.alpha(0.38f)
|
||||
},
|
||||
backgroundColor = backgroundColor,
|
||||
step = ownStepId,
|
||||
step = index,
|
||||
title = title,
|
||||
)
|
||||
val animSpec = spring<Float>(stiffness = Spring.StiffnessMedium)
|
||||
|
||||
@@ -56,6 +56,7 @@ import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import dev.patrickgold.florisboard.lib.util.InputMethodUtils
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
|
||||
private const val AnimationDuration = 200
|
||||
|
||||
@@ -115,7 +116,7 @@ fun PreviewKeyboardField(
|
||||
Row {
|
||||
IconButton(onClick = {
|
||||
if (!InputMethodUtils.showImePicker(context)) {
|
||||
context.showShortToast("Error: InputMethodManager service not available!")
|
||||
context.showShortToastSync("Error: InputMethodManager service not available!")
|
||||
}
|
||||
}) {
|
||||
Icon(
|
||||
|
||||
@@ -23,22 +23,35 @@ import android.inputmethodservice.InputMethodService
|
||||
import android.view.Window
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.snygg.ui.rememberSnyggThemeQuery
|
||||
import org.florisboard.lib.snygg.ui.uriOrNull
|
||||
|
||||
@Composable
|
||||
fun SystemUiIme() {
|
||||
val useDarkIcons = !FlorisImeTheme.config.isNightTheme
|
||||
val backgroundQuery = rememberSnyggThemeQuery(FlorisImeUi.Window.elementName)
|
||||
val backgroundColor = backgroundQuery.background()
|
||||
val backgroundImage = backgroundQuery.backgroundImage.uriOrNull()
|
||||
|
||||
val hasBackgroundImage = backgroundImage != null
|
||||
val useDarkIcons = if (backgroundImage == null) {
|
||||
backgroundColor.luminance() >= 0.5
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
val view = LocalView.current
|
||||
val window = view.context.findWindow()!!
|
||||
val windowInsetsController = WindowInsetsControllerCompat(window, view)
|
||||
|
||||
LaunchedEffect(useDarkIcons) {
|
||||
LaunchedEffect(useDarkIcons, hasBackgroundImage) {
|
||||
windowInsetsController.isAppearanceLightNavigationBars = useDarkIcons
|
||||
if (AndroidVersion.ATLEAST_API29_Q) {
|
||||
window.isNavigationBarContrastEnforced = true
|
||||
window.isNavigationBarContrastEnforced = hasBackgroundImage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ import android.widget.Toolbar
|
||||
import androidx.activity.ComponentActivity
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.AppPrefs
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import org.florisboard.lib.android.stringRes
|
||||
import dev.patrickgold.florisboard.lib.devtools.Devtools
|
||||
import dev.patrickgold.florisboard.lib.devtools.LogTopic
|
||||
@@ -39,14 +39,14 @@ import dev.patrickgold.florisboard.lib.devtools.flogWarning
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
private class SafePreferenceInstanceWrapper : ReadOnlyProperty<Any?, AppPrefs?> {
|
||||
private class SafePreferenceInstanceWrapper : ReadOnlyProperty<Any?, FlorisPreferenceModel?> {
|
||||
val cachedPreferenceModel = try {
|
||||
florisPreferenceModel()
|
||||
FlorisPreferenceStore
|
||||
} catch (_: Throwable) {
|
||||
null
|
||||
}
|
||||
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): AppPrefs? {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): FlorisPreferenceModel? {
|
||||
return cachedPreferenceModel?.getValue(thisRef, property)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,12 +35,11 @@ import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.lib.devtools.LogTopic
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogError
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogInfo
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import java.lang.ref.WeakReference
|
||||
import org.florisboard.lib.kotlin.io.FsDir
|
||||
import org.florisboard.lib.kotlin.io.FsFile
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import java.lang.ref.WeakReference
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/**
|
||||
@@ -113,7 +112,7 @@ abstract class CrashUtility private constructor() {
|
||||
application.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
|
||||
override fun onActivityCreated(
|
||||
activity: Activity,
|
||||
savedInstanceState: Bundle?
|
||||
savedInstanceState: Bundle?,
|
||||
) {
|
||||
if (activity !is CrashDialogActivity) {
|
||||
lastActivityCreated = WeakReference(activity)
|
||||
@@ -125,28 +124,26 @@ abstract class CrashUtility private constructor() {
|
||||
override fun onActivityStopped(activity: Activity) {}
|
||||
override fun onActivitySaveInstanceState(
|
||||
activity: Activity,
|
||||
outState: Bundle
|
||||
outState: Bundle,
|
||||
) {}
|
||||
override fun onActivityDestroyed(activity: Activity) {}
|
||||
})
|
||||
if (AndroidVersion.ATLEAST_API26_O) {
|
||||
try {
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE)
|
||||
if (notificationManager != null && notificationManager is NotificationManager) {
|
||||
val notificationChannel = NotificationChannel(
|
||||
NOTIFICATION_CHANNEL_ID,
|
||||
context.resources.getString(R.string.crash_notification_channel__title),
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
)
|
||||
notificationManager.createNotificationChannel(notificationChannel)
|
||||
}
|
||||
flogInfo(LogTopic.CRASH_UTILITY) {
|
||||
"Successfully created crash handler notification channel!"
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
flogError(LogTopic.CRASH_UTILITY) {
|
||||
"Failed to create crash handler notification channel due to an unspecified error:\n$e"
|
||||
}
|
||||
try {
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE)
|
||||
if (notificationManager != null && notificationManager is NotificationManager) {
|
||||
val notificationChannel = NotificationChannel(
|
||||
NOTIFICATION_CHANNEL_ID,
|
||||
context.resources.getString(R.string.crash_notification_channel__title),
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
)
|
||||
notificationManager.createNotificationChannel(notificationChannel)
|
||||
}
|
||||
flogInfo(LogTopic.CRASH_UTILITY) {
|
||||
"Successfully created crash handler notification channel!"
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
flogError(LogTopic.CRASH_UTILITY) {
|
||||
"Failed to create crash handler notification channel due to an unspecified error:\n$e"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -274,14 +271,7 @@ abstract class CrashUtility private constructor() {
|
||||
context ?: return
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE)
|
||||
if (notificationManager != null && notificationManager is NotificationManager) {
|
||||
val notificationBuilder = if (AndroidVersion.ATLEAST_API26_O) {
|
||||
Notification.Builder(context.applicationContext, NOTIFICATION_CHANNEL_ID)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
Notification.Builder(context.applicationContext).apply {
|
||||
setPriority(Notification.PRIORITY_MAX)
|
||||
}
|
||||
}
|
||||
val notificationBuilder = Notification.Builder(context.applicationContext, NOTIFICATION_CHANNEL_ID)
|
||||
val crashDialogIntent = Intent(context, CrashDialogActivity::class.java)
|
||||
val notification = notificationBuilder.run {
|
||||
setContentTitle(title)
|
||||
@@ -369,7 +359,7 @@ abstract class CrashUtility private constructor() {
|
||||
*/
|
||||
data class Stacktrace(
|
||||
val name: String,
|
||||
val details: String
|
||||
val details: String,
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,7 @@ import android.os.Build
|
||||
import android.os.Debug
|
||||
import dev.patrickgold.florisboard.BuildConfig
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.AppPrefs
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.lib.titlecase
|
||||
import dev.patrickgold.florisboard.lib.util.TimeUtils
|
||||
@@ -35,7 +35,7 @@ import java.io.InputStreamReader
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
object Devtools {
|
||||
fun generateDebugLog(context: Context, prefs: AppPrefs? = null, includeLogcat: Boolean = false): String {
|
||||
fun generateDebugLog(context: Context, prefs: FlorisPreferenceModel? = null, includeLogcat: Boolean = false): String {
|
||||
return buildString {
|
||||
append(generateDebugLogHeader(context, prefs))
|
||||
if (includeLogcat) {
|
||||
@@ -45,7 +45,7 @@ object Devtools {
|
||||
}
|
||||
}
|
||||
|
||||
fun generateDebugLogHeader(context: Context, prefs: AppPrefs? = null): String {
|
||||
fun generateDebugLogHeader(context: Context, prefs: FlorisPreferenceModel? = null): String {
|
||||
return buildString {
|
||||
append(generateSystemInfoLog(context))
|
||||
appendLine()
|
||||
@@ -61,7 +61,7 @@ object Devtools {
|
||||
}
|
||||
}
|
||||
|
||||
fun generateDebugLogForGithub(context: Context, prefs: AppPrefs? = null, includeLogcat: Boolean = false): String {
|
||||
fun generateDebugLogForGithub(context: Context, prefs: FlorisPreferenceModel? = null, includeLogcat: Boolean = false): String {
|
||||
return buildString {
|
||||
appendLine("<details>")
|
||||
appendLine("<summary>Detailed info (Debug log header)</summary>")
|
||||
@@ -113,7 +113,7 @@ object Devtools {
|
||||
}
|
||||
}
|
||||
|
||||
fun generateFeatureConfigLog(prefs: AppPrefs, withTitle: Boolean = true): String {
|
||||
fun generateFeatureConfigLog(prefs: FlorisPreferenceModel, withTitle: Boolean = true): String {
|
||||
return buildString {
|
||||
if (withTitle) appendLine("======= FEATURE CONFIG =======")
|
||||
append("Smartbar enabled : ").appendLine(prefs.smartbar.enabled.get())
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user