Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc5ed3475c | ||
|
|
871ff0acb1 | ||
|
|
34f8fec2f6 | ||
|
|
7199fcdf12 | ||
|
|
8319f563b9 | ||
|
|
45efe52159 | ||
|
|
7a286b932b | ||
|
|
df3f84e96d | ||
|
|
4cc2216940 | ||
|
|
21fdd31c88 | ||
|
|
53c6cb52ca | ||
|
|
9b12213b3e | ||
|
|
659a5062ab | ||
|
|
086e5f7782 | ||
|
|
ae73e64cd2 | ||
|
|
9f5e8cddd5 | ||
|
|
3724495d4f | ||
|
|
a146c6f846 | ||
|
|
40ad937384 | ||
|
|
15b5dd9e3e | ||
|
|
8408cb5133 | ||
|
|
8b90cdf06e | ||
|
|
36f06e0b4b | ||
|
|
eacb5259ec | ||
|
|
e7a82a3123 | ||
|
|
123b3a6d39 | ||
|
|
9531ad4c36 | ||
|
|
54ed179ead | ||
|
|
2dc7efd811 | ||
|
|
01d0b02e6d | ||
|
|
67c083131a | ||
|
|
03cde7296e | ||
|
|
32d26bcf80 | ||
|
|
e61a2c6e82 | ||
|
|
ce7a97dce6 | ||
|
|
9f17a1d36c | ||
|
|
a248b4f717 | ||
|
|
c470b792c1 | ||
|
|
ff5cd1e7c2 | ||
|
|
32fee44364 | ||
|
|
97edc33d05 | ||
|
|
a89af25eab |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -40,6 +40,7 @@ captures/
|
||||
# Intellij
|
||||
*.iml
|
||||
.idea/
|
||||
!/.idea/copyright/
|
||||
|
||||
# Keystore files
|
||||
*.jks
|
||||
|
||||
6
.idea/copyright/copyright.xml
generated
Normal file
6
.idea/copyright/copyright.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="CopyrightManager">
|
||||
<copyright>
|
||||
<option name="notice" value="Copyright (C) &#36;originalComment.match("Copyright \(C\) (\d+)", 1, "-", "&#36;today.year")&#36;today.year 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." />
|
||||
<option name="myName" value="copyright" />
|
||||
</copyright>
|
||||
</component>
|
||||
15
.idea/copyright/profiles_settings.xml
generated
Normal file
15
.idea/copyright/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
<component name="CopyrightManager">
|
||||
<settings default="copyright">
|
||||
<module2copyright>
|
||||
<element module="Project Files" copyright="copyright" />
|
||||
</module2copyright>
|
||||
<LanguageOptions name="Shell Script">
|
||||
<option name="fileTypeOverride" value="1" />
|
||||
</LanguageOptions>
|
||||
<LanguageOptions name="XML">
|
||||
<option name="fileTypeOverride" value="1" />
|
||||
<option name="prefixLines" value="false" />
|
||||
</LanguageOptions>
|
||||
<LanguageOptions name="__TEMPLATE__" />
|
||||
</settings>
|
||||
</component>
|
||||
2
LICENSE
2
LICENSE
@@ -186,7 +186,7 @@
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2020 Patrick Goldinger
|
||||
Copyright 2020-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.
|
||||
|
||||
@@ -114,7 +114,7 @@ Many thanks to [Nikolay Anzarov](https://www.behance.net/nikolayanzarov) ([@Bloo
|
||||
|
||||
## License
|
||||
```
|
||||
Copyright 2020-2024 Patrick Goldinger
|
||||
Copyright 2020-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.
|
||||
@@ -129,6 +129,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
```
|
||||
|
||||
Thanks to [The FlorisBoard Contributors](https://github.com/florisboard/florisboard/graphs/contributors) for making this project possible!
|
||||
|
||||
<!-- BEGIN SECTION: obtainium_links -->
|
||||
<!-- auto-generated link templates, do NOT edit by hand -->
|
||||
<!-- see fastlane/update-readme.sh -->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
@@ -14,8 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag
|
||||
import java.io.ByteArrayOutputStream
|
||||
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.agp.application)
|
||||
@@ -202,6 +202,7 @@ dependencies {
|
||||
implementation(libs.patrickgold.jetpref.material.ui)
|
||||
|
||||
implementation(project(":lib:android"))
|
||||
implementation(project(":lib:color"))
|
||||
implementation(project(":lib:kotlin"))
|
||||
implementation(project(":lib:native"))
|
||||
implementation(project(":lib:snygg"))
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
@@ -1,18 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2020-2022 Patrick Goldinger
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
|
||||
@@ -49,6 +49,12 @@
|
||||
"authors": [ "iamrasel" ],
|
||||
"direction": "ltr"
|
||||
},
|
||||
{
|
||||
"id": "hindi_in",
|
||||
"label": "हिंदी",
|
||||
"authors": [ "npnpatidar" ],
|
||||
"direction": "ltr"
|
||||
},
|
||||
{
|
||||
"id": "bepo",
|
||||
"label": "BÉPO",
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
[
|
||||
[
|
||||
{ "$": "case_selector", "lower": { "code": 2335, "label": "ट" }, "upper": { "code": 2336, "label": "ठ" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2337, "label": "ड" }, "upper": { "code": 2338, "label": "ढ" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2375, "label": "े" }, "upper": { "code": 2376, "label": "ै" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2352, "label": "र" }, "upper": { "code": 2371, "label": "ृ" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2340, "label": "त" }, "upper": { "code": 2341, "label": "थ" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2351, "label": "य" }, "upper": { "code": 2399, "label": "य़" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2369, "label": "ु" }, "upper": { "code": 2370, "label": "ू" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2367, "label": "ि" }, "upper": { "code": 2368, "label": "ी" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2379, "label": "ो" }, "upper": { "code": 2380, "label": "ौ" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2346, "label": "प" }, "upper": { "code": 2398, "label": "फ़" } }
|
||||
],
|
||||
[
|
||||
{ "$": "case_selector", "lower": { "code": 2366, "label": "ा" }, "upper": { "code": 2309, "label": "अ" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2360, "label": "स" }, "upper": { "code": 2358, "label": "श" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2342, "label": "द" }, "upper": { "code": 2343, "label": "ध" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2347, "label": "फ" }, "upper": { "code": 2364, "label": " ़" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2327, "label": "ग" }, "upper": { "code": 2328, "label": "घ" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2361, "label": "ह" }, "upper": { "code": 2307, "label": "ः" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2332, "label": "ज" }, "upper": { "code": 2333, "label": "झ" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2325, "label": "क" }, "upper": { "code": 2326, "label": "ख" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2354, "label": "ल" }, "upper": { "code": 2355, "label": "ळ" } }
|
||||
],
|
||||
[
|
||||
{
|
||||
"$": "case_selector",
|
||||
"lower": { "$": "multi_text_key", "codePoints": [2332, 2381, 2334], "label": "ज्ञ" },
|
||||
"upper": { "code": 2395, "label": "ज़" }
|
||||
},
|
||||
{
|
||||
"$": "case_selector",
|
||||
"lower": { "$": "multi_text_key", "codePoints": [2325, 2381, 2359], "label": "क्ष" },
|
||||
"upper": { "code": 2359, "label": "ष" }
|
||||
},
|
||||
{ "$": "case_selector", "lower": { "code": 2330, "label": "च" }, "upper": { "code": 2331, "label": "छ" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2357, "label": "व" }, "upper": { "code": 2381, "label": "्" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2348, "label": "ब" }, "upper": { "code": 2349, "label": "भ" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2344, "label": "न" }, "upper": { "code": 2339, "label": "ण" } },
|
||||
{ "$": "case_selector", "lower": { "code": 2350, "label": "म" }, "upper": { "code": 2306, "label": "ं" } }
|
||||
]
|
||||
]
|
||||
@@ -48,6 +48,7 @@
|
||||
{ "id": "fi", "authors": [ "patrickgold" ] },
|
||||
{ "id": "fo", "authors": [ "BinFlush" ] },
|
||||
{ "id": "fr", "authors": [ "patrickgold" ] },
|
||||
{ "id": "hi-IN", "authors": [ "npnpatidar" ] },
|
||||
{ "id": "hr", "authors": [ "hedidnothingwrong" ] },
|
||||
{ "id": "hu", "authors": [ "zoli111, gabik65" ] },
|
||||
{ "id": "hy", "authors": [ "PJTSearch" ] },
|
||||
@@ -695,6 +696,16 @@
|
||||
"numericAdvanced": "org.florisboard.layouts:bengali"
|
||||
}
|
||||
},
|
||||
{
|
||||
"languageTag": "hi-IN",
|
||||
"composer": "org.florisboard.composers:appender",
|
||||
"currencySet": "org.florisboard.currencysets:indian_rupee",
|
||||
"popupMapping": "org.florisboard.localization:hi-IN",
|
||||
"preferred": {
|
||||
"characters": "org.florisboard.layouts:hindi_in",
|
||||
"numericRow": "org.florisboard.layouts:devanagari"
|
||||
}
|
||||
},
|
||||
{
|
||||
"languageTag": "ast",
|
||||
"composer": "org.florisboard.composers:appender",
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
"র": {
|
||||
"main": { "$": "auto_text_key", "code": 2482, "label": "ল" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": "র্য" }
|
||||
{ "code": -255, "label": "র্য" }
|
||||
]
|
||||
},
|
||||
"ন": {
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
{
|
||||
"all": {
|
||||
"क": {
|
||||
"main": { "$": "auto_text_key", "code": 2392, "label": "क़" },
|
||||
"relevant": [{ "$": "auto_text_key", "code": 2393, "label": "ख़" }]
|
||||
},
|
||||
"ग": {
|
||||
"main": { "$": "auto_text_key", "code": 2394, "label": "ग़" },
|
||||
"relevant": [{ "$": "auto_text_key", "code": 2427, "label": "ॻ" }]
|
||||
},
|
||||
"च": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 2385, "label": " ॑" },
|
||||
{ "$": "auto_text_key", "code": 2386, "label": " ॒" }
|
||||
]
|
||||
},
|
||||
"ज": {
|
||||
"main": { "$": "auto_text_key", "code": 2395, "label": "ज़" },
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 2428, "label": "ॼ" },
|
||||
{ "$": "auto_text_key", "code": 2425, "label": "ॹ" }
|
||||
]
|
||||
},
|
||||
"ड": {
|
||||
"main": { "$": "auto_text_key", "code": 2396, "label": "ड़" },
|
||||
"relevant": [{ "$": "auto_text_key", "code": 2397, "label": "ढ़" }]
|
||||
},
|
||||
"त": {
|
||||
"main": { "$": "multi_text_key", "codePoints": [2340, 2381, 2352], "label": "त्र" }
|
||||
},
|
||||
"द": {
|
||||
"main": { "$": "auto_text_key", "code": 2396, "label": "ड़" },
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 2430, "label": "ॾ" },
|
||||
{ "$": "auto_text_key", "code": 2397, "label": "ढ़" },
|
||||
{ "$": "auto_text_key", "code": 2424, "label": "ॸ" }
|
||||
]
|
||||
},
|
||||
"न": {
|
||||
"main": { "$": "auto_text_key", "code": 2329, "label": "ङ" },
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 2345, "label": "ऩ" },
|
||||
{ "$": "auto_text_key", "code": 2334, "label": "ञ" }
|
||||
]
|
||||
},
|
||||
"फ": {
|
||||
"main": { "$": "auto_text_key", "code": 2398, "label": "फ़" }
|
||||
},
|
||||
"ब": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 2431, "label": "ॿ" },
|
||||
{ "$": "auto_text_key", "code": 2365, "label": "ऽ" },
|
||||
{ "$": "auto_text_key", "code": 2416, "label": "॰" }
|
||||
]
|
||||
},
|
||||
"म": {
|
||||
"main": { "$": "auto_text_key", "code": 2305, "label": "ँ" },
|
||||
"relevant": [{ "$": "auto_text_key", "code": 2304, "label": "ऀ" }]
|
||||
},
|
||||
"य": {
|
||||
"main": { "$": "auto_text_key", "code": 2426, "label": "ॺ" }
|
||||
},
|
||||
"र": {
|
||||
"main": { "$": "auto_text_key", "code": 2315, "label": "ऋ" },
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 2400, "label": "ॠ" },
|
||||
{ "$": "auto_text_key", "code": 2372, "label": "ॄ" },
|
||||
{ "$": "auto_text_key", "code": 2353, "label": "ऱ" }
|
||||
]
|
||||
},
|
||||
"ल": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 2402, "label": "ॢ" },
|
||||
{ "$": "auto_text_key", "code": 2403, "label": "ॣ" },
|
||||
{ "$": "auto_text_key", "code": 2316, "label": "ऌ" },
|
||||
{ "$": "auto_text_key", "code": 2401, "label": "ॡ" },
|
||||
{ "$": "auto_text_key", "code": 2356, "label": "ऴ" }
|
||||
]
|
||||
},
|
||||
"व": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 2387, "label": " ॓" },
|
||||
{ "$": "auto_text_key", "code": 2388, "label": " ॔" }
|
||||
]
|
||||
},
|
||||
"स": {
|
||||
"main": { "$": "multi_text_key", "codePoints": [2358, 2381, 2352], "label": "श्र" },
|
||||
"relevant": [{ "$": "auto_text_key", "code": 2359, "label": "ष" }]
|
||||
},
|
||||
"ा": {
|
||||
"main": { "$": "auto_text_key", "code": 2310, "label": "आ" },
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 2373, "label": "ॅ" },
|
||||
{ "$": "auto_text_key", "code": 2418, "label": "ॲ" },
|
||||
{ "$": "auto_text_key", "code": 2308, "label": "ऄ" }
|
||||
]
|
||||
},
|
||||
"ि": {
|
||||
"main": { "$": "auto_text_key", "code": 2311, "label": "इ" },
|
||||
"relevant": [{ "$": "auto_text_key", "code": 2312, "label": "ई" }]
|
||||
},
|
||||
"ु": {
|
||||
"main": { "$": "auto_text_key", "code": 2313, "label": "उ" },
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 2422, "label": "ॶ" },
|
||||
{ "$": "auto_text_key", "code": 2423, "label": "ॷ" },
|
||||
{ "$": "auto_text_key", "code": 2390, "label": " ॖ" },
|
||||
{ "$": "auto_text_key", "code": 2314, "label": "ऊ" },
|
||||
{ "$": "auto_text_key", "code": 2391, "label": " ॗ" }
|
||||
]
|
||||
},
|
||||
"े": {
|
||||
"main": { "$": "auto_text_key", "code": 2319, "label": "ए" },
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 2317, "label": "ऍ" },
|
||||
{ "$": "auto_text_key", "code": 2318, "label": "ऎ" },
|
||||
{ "$": "auto_text_key", "code": 2374, "label": " ॆ" },
|
||||
{ "$": "auto_text_key", "code": 2320, "label": "ऐ" },
|
||||
{ "$": "auto_text_key", "code": 2382, "label": " ॎ" },
|
||||
{ "$": "auto_text_key", "code": 2389, "label": " ॕ" }
|
||||
]
|
||||
},
|
||||
"ो": {
|
||||
"main": { "$": "auto_text_key", "code": 2323, "label": "ओ" },
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 2322, "label": "ऒ" },
|
||||
{ "$": "auto_text_key", "code": 2378, "label": " ॊ" },
|
||||
{ "$": "auto_text_key", "code": 2383, "label": " ॏ" },
|
||||
{ "$": "auto_text_key", "code": 2421, "label": "ॵ" },
|
||||
{ "$": "auto_text_key", "code": 2384, "label": "ॐ" },
|
||||
{ "$": "auto_text_key", "code": 2377, "label": "ॉ" },
|
||||
{ "$": "auto_text_key", "code": 2419, "label": "ॳ" },
|
||||
{ "$": "auto_text_key", "code": 2420, "label": "ॴ" },
|
||||
{ "$": "auto_text_key", "code": 2362, "label": " ऺ" },
|
||||
{ "$": "auto_text_key", "code": 2363, "label": " ऻ" },
|
||||
{ "$": "auto_text_key", "code": 2324, "label": "औ" },
|
||||
{ "$": "auto_text_key", "code": 2321, "label": "ऑ" }
|
||||
]
|
||||
},
|
||||
|
||||
"~right": {
|
||||
"main": { "code": 2404, "label": "।" },
|
||||
"relevant": [
|
||||
{ "code": 37, "label": "%" },
|
||||
{ "code": 43, "label": "+" },
|
||||
{ "code": 45, "label": "-" },
|
||||
{ "code": 58, "label": ":" },
|
||||
{ "code": 59, "label": ";" },
|
||||
{ "code": 47, "label": "/" },
|
||||
{ "$": "layout_direction_selector", "ltr": { "code": 40, "label": "(" }, "rtl": { "code": 41, "label": "(" } },
|
||||
{ "$": "layout_direction_selector", "ltr": { "code": 41, "label": ")" }, "rtl": { "code": 40, "label": ")" } },
|
||||
{ "code": 8205, "label": ">⁞<" },
|
||||
{ "code": 8204, "label": "<⁞>" },
|
||||
{ "code": 2417, "label": "ॱ" },
|
||||
{ "code": 2429, "label": "ॽ" },
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 63, "label": "?" },
|
||||
{ "code": 2405, "label": "॥" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"uri": {
|
||||
"~right": {
|
||||
"main": { "code": -255, "label": ".com" },
|
||||
"relevant": [
|
||||
{ "code": -255, "label": ".org" },
|
||||
{ "code": -255, "label": ".net" },
|
||||
{ "code": -255, "label": ".in" },
|
||||
{ "code": -255, "label": ".co.in" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,7 +186,7 @@
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2020 Patrick Goldinger
|
||||
Copyright 2020-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
@@ -18,6 +18,7 @@ package dev.patrickgold.florisboard
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.res.Configuration
|
||||
import android.inputmethodservice.ExtractEditText
|
||||
import android.os.Build
|
||||
@@ -46,6 +47,7 @@ import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -78,6 +80,8 @@ 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
|
||||
import dev.patrickgold.florisboard.ime.core.isSubtypeSelectionShowing
|
||||
import dev.patrickgold.florisboard.ime.editor.EditorRange
|
||||
import dev.patrickgold.florisboard.ime.editor.FlorisEditorInfo
|
||||
import dev.patrickgold.florisboard.ime.input.InputFeedbackController
|
||||
@@ -98,6 +102,7 @@ import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionsEditorPa
|
||||
import dev.patrickgold.florisboard.ime.text.TextInputLayout
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.ime.theme.WallpaperChangeReceiver
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisButton
|
||||
import dev.patrickgold.florisboard.lib.compose.ProvideLocalizedResources
|
||||
import dev.patrickgold.florisboard.lib.compose.SystemUiIme
|
||||
@@ -106,13 +111,6 @@ import dev.patrickgold.florisboard.lib.devtools.flogError
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogInfo
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogWarning
|
||||
import dev.patrickgold.florisboard.lib.observeAsTransformingState
|
||||
import org.florisboard.lib.snygg.ui.SnyggSurface
|
||||
import org.florisboard.lib.snygg.ui.shape
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import org.florisboard.lib.snygg.ui.snyggBorder
|
||||
import org.florisboard.lib.snygg.ui.snyggShadow
|
||||
import org.florisboard.lib.snygg.ui.solidColor
|
||||
import org.florisboard.lib.snygg.ui.spSize
|
||||
import dev.patrickgold.florisboard.lib.util.ViewUtils
|
||||
import dev.patrickgold.florisboard.lib.util.debugSummarize
|
||||
import dev.patrickgold.florisboard.lib.util.launchActivity
|
||||
@@ -124,6 +122,13 @@ import org.florisboard.lib.android.isOrientationPortrait
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.systemServiceOrNull
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
import org.florisboard.lib.snygg.ui.SnyggSurface
|
||||
import org.florisboard.lib.snygg.ui.shape
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import org.florisboard.lib.snygg.ui.snyggBorder
|
||||
import org.florisboard.lib.snygg.ui.snyggShadow
|
||||
import org.florisboard.lib.snygg.ui.solidColor
|
||||
import org.florisboard.lib.snygg.ui.spSize
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
@@ -231,10 +236,10 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
val imm = ims.systemServiceOrNull(InputMethodManager::class) ?: return false
|
||||
val list: List<InputMethodInfo> = imm.enabledInputMethodList
|
||||
for (el in list) {
|
||||
for (i in 0 until el.subtypeCount){
|
||||
for (i in 0 until el.subtypeCount) {
|
||||
if (el.getSubtypeAt(i).mode != "voice") continue
|
||||
if (AndroidVersion.ATLEAST_API28_P) {
|
||||
ims.switchInputMethod(el.id)
|
||||
ims.switchInputMethod(el.id, el.getSubtypeAt(i))
|
||||
return true
|
||||
} else {
|
||||
ims.window.window?.let { window ->
|
||||
@@ -266,6 +271,8 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
private var isExtractUiShown by mutableStateOf(false)
|
||||
private var resourcesContext by mutableStateOf(this as Context)
|
||||
|
||||
private val wallpaperChangeReceiver = WallpaperChangeReceiver()
|
||||
|
||||
init {
|
||||
setTheme(R.style.FlorisImeTheme)
|
||||
}
|
||||
@@ -279,6 +286,8 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
config.setLocale(subtype.primaryLocale.base)
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
@Suppress("DEPRECATION") // We do not retrieve the wallpaper but only listen to changes
|
||||
registerReceiver(wallpaperChangeReceiver, IntentFilter(Intent.ACTION_WALLPAPER_CHANGED))
|
||||
}
|
||||
|
||||
override fun onCreateInputView(): View {
|
||||
@@ -317,6 +326,7 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
unregisterReceiver(wallpaperChangeReceiver)
|
||||
FlorisImeServiceReference = WeakReference(null)
|
||||
inputWindowView = null
|
||||
}
|
||||
@@ -494,7 +504,9 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
outInsets.visibleTopInsets = visibleTopY
|
||||
outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_REGION
|
||||
val left = 0
|
||||
val top = if (keyboardManager.activeState.isBottomSheetShowing()) { 0 } else {
|
||||
val top = if (keyboardManager.activeState.isBottomSheetShowing() || keyboardManager.activeState.isSubtypeSelectionShowing()) {
|
||||
0
|
||||
} else {
|
||||
visibleTopY - if (needAdditionalOverlay) FlorisImeSizing.Static.smartbarHeightPx else 0
|
||||
}
|
||||
val right = inputViewSize.width
|
||||
@@ -551,7 +563,7 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
) {
|
||||
DevtoolsUi()
|
||||
DevtoolsOverlay(modifier = Modifier.fillMaxSize())
|
||||
}
|
||||
}
|
||||
ImeUi()
|
||||
@@ -597,6 +609,7 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.safeDrawingPadding()
|
||||
// FIXME: removing this fixes the Smartbar sizing but breaks one-handed-mode
|
||||
//.height(IntrinsicSize.Min)
|
||||
.padding(bottom = bottomOffset),
|
||||
@@ -637,14 +650,6 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DevtoolsUi() {
|
||||
val devtoolsEnabled by prefs.devtools.enabled.observeAsState()
|
||||
if (devtoolsEnabled) {
|
||||
DevtoolsOverlay(modifier = Modifier.fillMaxSize())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean =
|
||||
if (keyboardManager.onHardwareKeyDown(keyCode, event)) true
|
||||
else super.onKeyDown(keyCode, event)
|
||||
@@ -686,12 +691,22 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
ProvideLocalizedResources(resourcesContext, forceLayoutDirection = LayoutDirection.Ltr) {
|
||||
FlorisImeTheme {
|
||||
BottomSheetHostUi(
|
||||
isShowing = state.isBottomSheetShowing(),
|
||||
isShowing = state.isBottomSheetShowing() || state.isSubtypeSelectionShowing(),
|
||||
onHide = {
|
||||
keyboardManager.activeState.isActionsEditorVisible = false
|
||||
if (state.isBottomSheetShowing()) {
|
||||
keyboardManager.activeState.isActionsEditorVisible = false
|
||||
}
|
||||
if (state.isSubtypeSelectionShowing()) {
|
||||
keyboardManager.activeState.isSubtypeSelectionVisible = false
|
||||
}
|
||||
},
|
||||
) {
|
||||
QuickActionsEditorPanel()
|
||||
if (state.isBottomSheetShowing()) {
|
||||
QuickActionsEditorPanel()
|
||||
}
|
||||
if (state.isSubtypeSelectionShowing()) {
|
||||
SelectSubtypePanel()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -740,7 +755,8 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
val fieldColor = fieldStyle.foreground.solidColor(context, FlorisImeTheme.fallbackContentColor())
|
||||
val fieldColor =
|
||||
fieldStyle.foreground.solidColor(context, FlorisImeTheme.fallbackContentColor())
|
||||
AndroidView(
|
||||
modifier = Modifier
|
||||
.padding(8.dp)
|
||||
@@ -776,8 +792,14 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
?: "ACTION",
|
||||
shape = actionStyle.shape.shape(),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = actionStyle.background.solidColor(context, FlorisImeTheme.fallbackContentColor()),
|
||||
contentColor = actionStyle.foreground.solidColor(context, FlorisImeTheme.fallbackSurfaceColor()),
|
||||
containerColor = actionStyle.background.solidColor(
|
||||
context,
|
||||
FlorisImeTheme.fallbackContentColor()
|
||||
),
|
||||
contentColor = actionStyle.foreground.solidColor(
|
||||
context,
|
||||
FlorisImeTheme.fallbackSurfaceColor()
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021-2024 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
@@ -18,6 +18,7 @@ package dev.patrickgold.florisboard.app
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import dev.patrickgold.florisboard.app.settings.theme.DisplayColorsAs
|
||||
import dev.patrickgold.florisboard.app.settings.theme.DisplayKbdAfterDialogs
|
||||
@@ -47,10 +48,9 @@ import dev.patrickgold.florisboard.ime.text.key.KeyHintMode
|
||||
import dev.patrickgold.florisboard.ime.text.key.UtilityKeyAction
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeMode
|
||||
import dev.patrickgold.florisboard.ime.theme.extCoreTheme
|
||||
import org.florisboard.lib.android.isOrientationPortrait
|
||||
import dev.patrickgold.florisboard.lib.compose.ColorPreferenceSerializer
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
|
||||
import dev.patrickgold.florisboard.lib.observeAsTransformingState
|
||||
import org.florisboard.lib.snygg.SnyggLevel
|
||||
import dev.patrickgold.florisboard.lib.util.VersionName
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceMigrationEntry
|
||||
@@ -59,6 +59,10 @@ import dev.patrickgold.jetpref.datastore.model.PreferenceType
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.isOrientationPortrait
|
||||
import org.florisboard.lib.color.DEFAULT_GREEN
|
||||
import org.florisboard.lib.snygg.SnyggLevel
|
||||
|
||||
fun florisPreferenceModel() = JetPref.getOrCreatePreferenceModel(AppPrefs::class, ::AppPrefs)
|
||||
|
||||
@@ -69,9 +73,13 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
key = "advanced__settings_theme",
|
||||
default = AppTheme.AUTO,
|
||||
)
|
||||
val useMaterialYou = boolean(
|
||||
key = "advanced__use_material_you",
|
||||
default = true,
|
||||
val accentColor = custom(
|
||||
key = "advanced__accent_color",
|
||||
default = when (AndroidVersion.ATLEAST_API31_S) {
|
||||
true -> Color.Unspecified
|
||||
false -> DEFAULT_GREEN
|
||||
},
|
||||
serializer = ColorPreferenceSerializer,
|
||||
)
|
||||
val settingsLanguage = string(
|
||||
key = "advanced__settings_language",
|
||||
@@ -118,6 +126,14 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
key = "clipboard__clean_up_after",
|
||||
default = 20,
|
||||
)
|
||||
val autoCleanSensitive = boolean(
|
||||
key = "clipboard__auto_clean_sensitive",
|
||||
default = false,
|
||||
)
|
||||
val autoCleanSensitiveAfter = int(
|
||||
key = "clipboard__auto_clean_sensitive_after",
|
||||
default = 20,
|
||||
)
|
||||
val limitHistorySize = boolean(
|
||||
key = "clipboard__limit_history_size",
|
||||
default = true,
|
||||
@@ -158,10 +174,6 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
key = "devtools__enabled",
|
||||
default = false,
|
||||
)
|
||||
val showHeapMemoryStats = boolean(
|
||||
key = "devtools__show_heap_memory_stats",
|
||||
default = false,
|
||||
)
|
||||
val showPrimaryClip = boolean(
|
||||
key = "devtools__show_primary_clip",
|
||||
default = false,
|
||||
@@ -700,6 +712,14 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
default = extCoreTheme("floris_night"),
|
||||
serializer = ExtensionComponentName.Serializer,
|
||||
)
|
||||
val accentColor = custom(
|
||||
key = "theme__accent_color",
|
||||
default = when (AndroidVersion.ATLEAST_API31_S) {
|
||||
true -> Color.Unspecified
|
||||
false -> DEFAULT_GREEN
|
||||
},
|
||||
serializer = ColorPreferenceSerializer,
|
||||
)
|
||||
//val sunriseTime = localTime(
|
||||
// key = "theme__sunrise_time",
|
||||
// default = LocalTime.of(6, 0),
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -526,6 +542,10 @@ private val ENUM_DISPLAY_ENTRIES = mapOf<Pair<KClass<*>, String>, @Composable ()
|
||||
key = SwipeAction.SHOW_INPUT_METHOD_PICKER,
|
||||
label = stringRes(R.string.enum__swipe_action__show_input_method_picker),
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.SHOW_SUBTYPE_PICKER,
|
||||
label = "Show subtype picker"
|
||||
)
|
||||
entry(
|
||||
key = SwipeAction.SWITCH_TO_PREV_SUBTYPE,
|
||||
label = stringRes(R.string.enum__swipe_action__switch_to_prev_subtype),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
@@ -36,6 +36,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.isUnspecified
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.WindowCompat
|
||||
@@ -117,8 +118,8 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
AppVersionUtils.updateVersionOnInstallAndLastUse(this, prefs)
|
||||
setContent {
|
||||
ProvideLocalizedResources(resourcesContext) {
|
||||
val useMaterialYou by prefs.advanced.useMaterialYou.observeAsState()
|
||||
FlorisAppTheme(theme = appTheme, isMaterialYouAware = useMaterialYou) {
|
||||
val accentColor by prefs.advanced.accentColor.observeAsState()
|
||||
FlorisAppTheme(theme = appTheme, isMaterialYouAware = accentColor.isUnspecified) {
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
AppContent()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
@@ -17,19 +17,24 @@
|
||||
package dev.patrickgold.florisboard.app.apptheme
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.graphics.Color
|
||||
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.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.color.ColorMappings
|
||||
|
||||
/*private val AmoledDarkColorPalette = darkColorScheme(
|
||||
primary = Green500,
|
||||
@@ -70,170 +75,93 @@ private val LightColorPalette = lightColorScheme(
|
||||
*/
|
||||
)*/
|
||||
|
||||
private val lightScheme = lightColorScheme(
|
||||
primary = primaryLight,
|
||||
onPrimary = onPrimaryLight,
|
||||
primaryContainer = primaryContainerLight,
|
||||
onPrimaryContainer = onPrimaryContainerLight,
|
||||
secondary = secondaryLight,
|
||||
onSecondary = onSecondaryLight,
|
||||
secondaryContainer = secondaryContainerLight,
|
||||
onSecondaryContainer = onSecondaryContainerLight,
|
||||
tertiary = tertiaryLight,
|
||||
onTertiary = onTertiaryLight,
|
||||
tertiaryContainer = tertiaryContainerLight,
|
||||
onTertiaryContainer = onTertiaryContainerLight,
|
||||
error = errorLight,
|
||||
onError = onErrorLight,
|
||||
errorContainer = errorContainerLight,
|
||||
onErrorContainer = onErrorContainerLight,
|
||||
background = backgroundLight,
|
||||
onBackground = onBackgroundLight,
|
||||
surface = surfaceLight,
|
||||
onSurface = onSurfaceLight,
|
||||
surfaceVariant = surfaceVariantLight,
|
||||
onSurfaceVariant = onSurfaceVariantLight,
|
||||
outline = outlineLight,
|
||||
outlineVariant = outlineVariantLight,
|
||||
scrim = scrimLight,
|
||||
inverseSurface = inverseSurfaceLight,
|
||||
inverseOnSurface = inverseOnSurfaceLight,
|
||||
inversePrimary = inversePrimaryLight,
|
||||
surfaceDim = surfaceDimLight,
|
||||
surfaceBright = surfaceBrightLight,
|
||||
surfaceContainerLowest = surfaceContainerLowestLight,
|
||||
surfaceContainerLow = surfaceContainerLowLight,
|
||||
surfaceContainer = surfaceContainerLight,
|
||||
surfaceContainerHigh = surfaceContainerHighLight,
|
||||
surfaceContainerHighest = surfaceContainerHighestLight,
|
||||
)
|
||||
|
||||
private val darkScheme = darkColorScheme(
|
||||
primary = primaryDark,
|
||||
onPrimary = onPrimaryDark,
|
||||
primaryContainer = primaryContainerDark,
|
||||
onPrimaryContainer = onPrimaryContainerDark,
|
||||
secondary = secondaryDark,
|
||||
onSecondary = onSecondaryDark,
|
||||
secondaryContainer = secondaryContainerDark,
|
||||
onSecondaryContainer = onSecondaryContainerDark,
|
||||
tertiary = tertiaryDark,
|
||||
onTertiary = onTertiaryDark,
|
||||
tertiaryContainer = tertiaryContainerDark,
|
||||
onTertiaryContainer = onTertiaryContainerDark,
|
||||
error = errorDark,
|
||||
onError = onErrorDark,
|
||||
errorContainer = errorContainerDark,
|
||||
onErrorContainer = onErrorContainerDark,
|
||||
background = backgroundDark,
|
||||
onBackground = onBackgroundDark,
|
||||
surface = surfaceDark,
|
||||
onSurface = onSurfaceDark,
|
||||
surfaceVariant = surfaceVariantDark,
|
||||
onSurfaceVariant = onSurfaceVariantDark,
|
||||
outline = outlineDark,
|
||||
outlineVariant = outlineVariantDark,
|
||||
scrim = scrimDark,
|
||||
inverseSurface = inverseSurfaceDark,
|
||||
inverseOnSurface = inverseOnSurfaceDark,
|
||||
inversePrimary = inversePrimaryDark,
|
||||
surfaceDim = surfaceDimDark,
|
||||
surfaceBright = surfaceBrightDark,
|
||||
surfaceContainerLowest = surfaceContainerLowestDark,
|
||||
surfaceContainerLow = surfaceContainerLowDark,
|
||||
surfaceContainer = surfaceContainerDark,
|
||||
surfaceContainerHigh = surfaceContainerHighDark,
|
||||
surfaceContainerHighest = surfaceContainerHighestDark,
|
||||
)
|
||||
@Composable
|
||||
fun getColorScheme(
|
||||
context: Context,
|
||||
isMaterialYouAware: Boolean,
|
||||
themeColor: Color,
|
||||
theme: AppTheme,
|
||||
): ColorScheme {
|
||||
val isDark = isSystemInDarkTheme()
|
||||
|
||||
private val amoledScheme = darkScheme.copy(
|
||||
background = amoledDark,
|
||||
surface = amoledDark
|
||||
)
|
||||
return when (theme) {
|
||||
|
||||
AppTheme.AUTO -> {
|
||||
if (isMaterialYouAware && AndroidVersion.ATLEAST_API31_S) {
|
||||
when {
|
||||
isDark -> dynamicDarkColorScheme(context)
|
||||
else -> dynamicLightColorScheme(context)
|
||||
}
|
||||
} else {
|
||||
ColorMappings.getColorSchemeOrDefault(themeColor, isDark, true)
|
||||
}
|
||||
}
|
||||
|
||||
AppTheme.DARK -> {
|
||||
if (isMaterialYouAware && AndroidVersion.ATLEAST_API31_S) {
|
||||
dynamicDarkColorScheme(context)
|
||||
} else {
|
||||
ColorMappings.getColorSchemeOrDefault(themeColor, isDark = true, settings = true)
|
||||
}
|
||||
}
|
||||
|
||||
AppTheme.LIGHT -> {
|
||||
if (isMaterialYouAware && AndroidVersion.ATLEAST_API31_S) {
|
||||
dynamicLightColorScheme(context)
|
||||
} else {
|
||||
ColorMappings.getColorSchemeOrDefault(themeColor, isDark = false, settings = true)
|
||||
}
|
||||
}
|
||||
|
||||
AppTheme.AMOLED_DARK -> {
|
||||
if (isMaterialYouAware && AndroidVersion.ATLEAST_API31_S) {
|
||||
dynamicDarkColorScheme(context).amoled()
|
||||
} else {
|
||||
ColorMappings.getColorSchemeOrDefault(themeColor, isDark = true, settings = true).amoled()
|
||||
}
|
||||
}
|
||||
|
||||
AppTheme.AUTO_AMOLED -> {
|
||||
if (isMaterialYouAware && AndroidVersion.ATLEAST_API31_S) {
|
||||
when {
|
||||
isDark -> dynamicDarkColorScheme(context).amoled()
|
||||
else -> dynamicLightColorScheme(context)
|
||||
}
|
||||
} else {
|
||||
with(ColorMappings.getColorSchemeOrDefault(themeColor, isDark, true)) {
|
||||
if (isDark) amoled() else this
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ColorScheme.amoled(): ColorScheme {
|
||||
return this.copy(background = Color.Black, surface = Color.Black)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FlorisAppTheme(
|
||||
theme: AppTheme,
|
||||
isMaterialYouAware: Boolean,
|
||||
content: @Composable () -> Unit
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val accent by prefs.advanced.accentColor.observeAsState()
|
||||
|
||||
val colors = if (AndroidVersion.ATLEAST_API31_S) {
|
||||
when (theme) {
|
||||
AppTheme.AUTO -> when {
|
||||
isMaterialYouAware -> when {
|
||||
isSystemInDarkTheme() -> dynamicDarkColorScheme(LocalContext.current)
|
||||
else -> dynamicLightColorScheme(LocalContext.current)
|
||||
}
|
||||
|
||||
else -> {
|
||||
when {
|
||||
isSystemInDarkTheme() -> darkScheme
|
||||
else -> lightScheme
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AppTheme.AUTO_AMOLED -> when {
|
||||
isMaterialYouAware -> when {
|
||||
isSystemInDarkTheme() -> dynamicDarkColorScheme(LocalContext.current).copy(
|
||||
background = amoledDark,
|
||||
surface = amoledDark,
|
||||
)
|
||||
|
||||
else -> dynamicLightColorScheme(LocalContext.current)
|
||||
}
|
||||
|
||||
else -> {
|
||||
when {
|
||||
isSystemInDarkTheme() -> amoledScheme
|
||||
else -> lightScheme
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AppTheme.LIGHT -> when {
|
||||
isMaterialYouAware -> dynamicLightColorScheme(LocalContext.current)
|
||||
else -> lightScheme
|
||||
}
|
||||
|
||||
AppTheme.DARK -> when {
|
||||
isMaterialYouAware -> dynamicDarkColorScheme(LocalContext.current)
|
||||
else -> darkScheme
|
||||
}
|
||||
|
||||
AppTheme.AMOLED_DARK -> when {
|
||||
isMaterialYouAware -> dynamicDarkColorScheme(LocalContext.current).copy(
|
||||
background = amoledDark,
|
||||
surface = amoledDark,
|
||||
)
|
||||
|
||||
else -> amoledScheme
|
||||
}
|
||||
}
|
||||
} else {
|
||||
when (theme) {
|
||||
AppTheme.AUTO -> when {
|
||||
isSystemInDarkTheme() -> darkScheme
|
||||
else -> lightScheme
|
||||
}
|
||||
|
||||
AppTheme.AUTO_AMOLED -> when {
|
||||
isSystemInDarkTheme() -> darkScheme
|
||||
else -> lightScheme
|
||||
}
|
||||
|
||||
AppTheme.LIGHT -> lightScheme
|
||||
AppTheme.DARK -> darkScheme
|
||||
AppTheme.AMOLED_DARK -> amoledScheme
|
||||
}
|
||||
}
|
||||
val colors = getColorScheme(
|
||||
context = LocalContext.current,
|
||||
theme = theme,
|
||||
isMaterialYouAware = isMaterialYouAware,
|
||||
themeColor = accent,
|
||||
)
|
||||
|
||||
val darkTheme =
|
||||
theme == AppTheme.DARK
|
||||
|| theme == AppTheme.AMOLED_DARK
|
||||
|| (theme == AppTheme.AUTO && isSystemInDarkTheme())
|
||||
|| (theme == AppTheme.AUTO_AMOLED && isSystemInDarkTheme())
|
||||
|| theme == AppTheme.AMOLED_DARK
|
||||
|| (theme == AppTheme.AUTO && isSystemInDarkTheme())
|
||||
|| (theme == AppTheme.AUTO_AMOLED && isSystemInDarkTheme())
|
||||
|
||||
val view = LocalView.current
|
||||
if (!view.isInEditMode) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
@@ -42,7 +42,10 @@ import androidx.compose.ui.unit.sp
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.ime.keyboard.CachedLayout
|
||||
import dev.patrickgold.florisboard.ime.keyboard.DebugLayoutComputationResult
|
||||
import dev.patrickgold.florisboard.ime.nlp.NlpInlineAutofill
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.florisboard.nlpManager
|
||||
@@ -56,28 +59,36 @@ 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 keyboardManager by context.keyboardManager()
|
||||
|
||||
val devtoolsEnabled by prefs.devtools.enabled.observeAsState()
|
||||
val showPrimaryClip by prefs.devtools.showPrimaryClip.observeAsState()
|
||||
val showInputStateOverlay by prefs.devtools.showInputStateOverlay.observeAsState()
|
||||
val showSpellingOverlay by prefs.devtools.showSpellingOverlay.observeAsState()
|
||||
val showInlineAutofillOverlay by prefs.devtools.showInlineAutofillOverlay.observeAsState()
|
||||
|
||||
val debugLayoutResult by keyboardManager.layoutManager.debugLayoutComputationResultFlow.collectAsState()
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides Color.White,
|
||||
LocalLayoutDirection provides LayoutDirection.Ltr,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
if (showPrimaryClip) {
|
||||
if (devtoolsEnabled && showPrimaryClip) {
|
||||
DevtoolsClipboardOverlay()
|
||||
}
|
||||
if (showInputStateOverlay) {
|
||||
if (devtoolsEnabled && showInputStateOverlay) {
|
||||
DevtoolsInputStateOverlay()
|
||||
}
|
||||
if (showSpellingOverlay) {
|
||||
if (debugLayoutResult?.allLayoutsSuccess() == false) {
|
||||
DevtoolsLastLayoutComputationOverlay(debugLayoutResult)
|
||||
}
|
||||
if (devtoolsEnabled && showSpellingOverlay) {
|
||||
DevtoolsSpellingOverlay()
|
||||
}
|
||||
if (showInlineAutofillOverlay && AndroidVersion.ATLEAST_API30_R) {
|
||||
if (devtoolsEnabled && showInlineAutofillOverlay && AndroidVersion.ATLEAST_API30_R) {
|
||||
DevtoolsInlineAutofillOverlay()
|
||||
}
|
||||
}
|
||||
@@ -125,6 +136,34 @@ private fun DevtoolsInputStateOverlay() {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DevtoolsLastLayoutComputationOverlay(debugLayoutResult: DebugLayoutComputationResult?) {
|
||||
@Composable
|
||||
fun PrintResult(result: Result<CachedLayout?>) {
|
||||
if (result.isSuccess) {
|
||||
DevtoolsText(text = "loaded: ${result.getOrNull()?.name}")
|
||||
} else {
|
||||
DevtoolsText(text = "error: ${result.exceptionOrNull()}")
|
||||
}
|
||||
}
|
||||
|
||||
DevtoolsOverlayBox(title = "Last layout computation") {
|
||||
if (debugLayoutResult == null) {
|
||||
DevtoolsText(text = "No layout computation result available.")
|
||||
return@DevtoolsOverlayBox
|
||||
}
|
||||
DevtoolsSubGroup(title = "main") {
|
||||
PrintResult(debugLayoutResult!!.main)
|
||||
}
|
||||
DevtoolsSubGroup(title = "mod") {
|
||||
PrintResult(debugLayoutResult!!.mod)
|
||||
}
|
||||
DevtoolsSubGroup(title = "ext") {
|
||||
PrintResult(debugLayoutResult!!.ext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DevtoolsSpellingOverlay() {
|
||||
val context = LocalContext.current
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
@@ -61,12 +61,6 @@ fun DevtoolsScreen() = FlorisScreen {
|
||||
)
|
||||
|
||||
PreferenceGroup(title = stringRes(R.string.devtools__title)) {
|
||||
SwitchPreference(
|
||||
prefs.devtools.showHeapMemoryStats,
|
||||
title = stringRes(R.string.devtools__show_heap_memory_stats__label),
|
||||
summary = stringRes(R.string.devtools__show_heap_memory_stats__summary),
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.devtools.showPrimaryClip,
|
||||
title = stringRes(R.string.devtools__show_primary_clip__label),
|
||||
@@ -128,6 +122,13 @@ fun DevtoolsScreen() = FlorisScreen {
|
||||
onClick = { navController.navigate(Routes.Devtools.ExportDebugLog) },
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.glide.enabled,
|
||||
title = "prefs.glide.enabled (debug)",
|
||||
summaryOn = "This impacts your performance and may trigger the all keys invisible bug!",
|
||||
summaryOff = "Recommended to keep this off!",
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
)
|
||||
}
|
||||
|
||||
PreferenceGroup(title = stringRes(R.string.devtools__group_android__title)) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.ext
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.ext
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.ext
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Patrick Goldinger
|
||||
* Copyright (C) 2024-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.
|
||||
@@ -17,10 +17,15 @@
|
||||
package dev.patrickgold.florisboard.app.ext
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
@@ -33,8 +38,13 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
import dev.patrickgold.florisboard.R
|
||||
@@ -46,6 +56,7 @@ import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisTextButton
|
||||
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
|
||||
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
@@ -80,49 +91,66 @@ enum class ExtensionListScreenType(
|
||||
fun ExtensionListScreen(type: ExtensionListScreenType, showUpdate: Boolean) = FlorisScreen {
|
||||
title = stringRes(type.titleResId)
|
||||
previewFieldVisible = false
|
||||
scrollable = false
|
||||
|
||||
val context = LocalContext.current
|
||||
val navController = LocalNavController.current
|
||||
val extensionManager by context.extensionManager()
|
||||
val extensionIndex by type.getExtensionIndex(extensionManager).observeAsNonNullState()
|
||||
|
||||
var fabHeight by remember {
|
||||
mutableStateOf(0)
|
||||
}
|
||||
val fabHeightDp = with(LocalDensity.current) { fabHeight.toDp()+16.dp }
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
content {
|
||||
if (showUpdate) {
|
||||
UpdateBox(extensionIndex = extensionIndex)
|
||||
}
|
||||
for (ext in extensionIndex) {
|
||||
FlorisOutlinedBox(
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
title = ext.meta.title,
|
||||
subtitle = ext.meta.id,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||
text = ext.meta.description ?: "",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 6.dp),
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.florisScrollbar(state = listState, isVertical = true),
|
||||
state = listState,
|
||||
contentPadding = PaddingValues(bottom = fabHeightDp),
|
||||
) {
|
||||
if (showUpdate) {
|
||||
item {
|
||||
UpdateBox(extensionIndex = extensionIndex)
|
||||
}
|
||||
}
|
||||
items(extensionIndex) { ext ->
|
||||
FlorisOutlinedBox(
|
||||
modifier = Modifier.defaultFlorisOutlinedBox(),
|
||||
title = ext.meta.title,
|
||||
subtitle = ext.meta.id,
|
||||
) {
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
navController.navigate(Routes.Ext.View(ext.meta.id))
|
||||
},
|
||||
icon = Icons.Outlined.Info,
|
||||
text = stringRes(id = R.string.ext__list__view_details),//stringRes(R.string.action__add),
|
||||
colors = ButtonDefaults.textButtonColors(),
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
navController.navigate(Routes.Ext.Edit(ext.meta.id))
|
||||
},
|
||||
icon = Icons.Default.Edit,
|
||||
text = stringRes(R.string.action__edit),
|
||||
enabled = extensionManager.canDelete(ext),
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||
text = ext.meta.description ?: "",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 6.dp),
|
||||
) {
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
navController.navigate(Routes.Ext.View(ext.meta.id))
|
||||
},
|
||||
icon = Icons.Outlined.Info,
|
||||
text = stringRes(id = R.string.ext__list__view_details),//stringRes(R.string.action__add),
|
||||
colors = ButtonDefaults.textButtonColors(),
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
FlorisTextButton(
|
||||
onClick = {
|
||||
navController.navigate(Routes.Ext.Edit(ext.meta.id))
|
||||
},
|
||||
icon = Icons.Default.Edit,
|
||||
text = stringRes(R.string.action__edit),
|
||||
enabled = extensionManager.canDelete(ext),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,6 +170,9 @@ fun ExtensionListScreen(type: ExtensionListScreenType, showUpdate: Boolean) = Fl
|
||||
text = stringRes(id = R.string.ext__editor__title_create_any),
|
||||
)
|
||||
},
|
||||
modifier = Modifier.onGloballyPositioned {
|
||||
fabHeight = it.size.height
|
||||
},
|
||||
shape = FloatingActionButtonDefaults.extendedFabShape,
|
||||
onClick = { type.launchExtensionCreate.invoke(navController) },
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
@@ -19,13 +19,15 @@ package dev.patrickgold.florisboard.app.settings.advanced
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Adb
|
||||
import androidx.compose.material.icons.filled.Archive
|
||||
import androidx.compose.material.icons.filled.FormatPaint
|
||||
import androidx.compose.material.icons.filled.FormatColorFill
|
||||
import androidx.compose.material.icons.filled.Language
|
||||
import androidx.compose.material.icons.filled.Palette
|
||||
import androidx.compose.material.icons.filled.Preview
|
||||
import androidx.compose.material.icons.filled.SettingsBackupRestore
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.AppTheme
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
@@ -34,16 +36,20 @@ import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
|
||||
import dev.patrickgold.florisboard.ime.keyboard.IncognitoMode
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.ColorPickerPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.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
|
||||
|
||||
|
||||
@Composable
|
||||
fun AdvancedScreen() = FlorisScreen {
|
||||
@@ -51,6 +57,7 @@ fun AdvancedScreen() = FlorisScreen {
|
||||
previewFieldVisible = false
|
||||
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
|
||||
content {
|
||||
ListPreference(
|
||||
@@ -59,13 +66,21 @@ fun AdvancedScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.pref__advanced__settings_theme__label),
|
||||
entries = enumDisplayEntriesOf(AppTheme::class),
|
||||
)
|
||||
SwitchPreference(
|
||||
pref = prefs.advanced.useMaterialYou,
|
||||
icon = Icons.Default.FormatPaint,
|
||||
title = stringRes(R.string.pref__advanced__settings_material_you__label),
|
||||
visibleIf = {
|
||||
AndroidVersion.ATLEAST_API31_S
|
||||
},
|
||||
ColorPickerPreference(
|
||||
pref = prefs.advanced.accentColor,
|
||||
title = stringRes(R.string.pref__advanced__settings_accent_color__label),
|
||||
defaultValueLabel = stringRes(R.string.action__default),
|
||||
icon = Icons.Default.FormatColorFill,
|
||||
defaultColors = ColorMappings.colors,
|
||||
showAlphaSlider = false,
|
||||
enableAdvancedLayout = false,
|
||||
colorOverride = {
|
||||
if (it.isMaterialYou(context)) {
|
||||
Color.Unspecified
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
)
|
||||
ListPreference(
|
||||
prefs.advanced.settingsLanguage,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
@@ -25,6 +25,7 @@ import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
|
||||
@OptIn(ExperimentalJetPrefDatastoreUi::class)
|
||||
@Composable
|
||||
@@ -71,6 +72,22 @@ fun ClipboardScreen() = FlorisScreen {
|
||||
stepIncrement = 5,
|
||||
enabledIf = { prefs.clipboard.historyEnabled isEqualTo true && prefs.clipboard.cleanUpOld isEqualTo true },
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.clipboard.autoCleanSensitive,
|
||||
title = stringRes(R.string.pref__clipboard__auto_clean_sensitive__label),
|
||||
enabledIf = { prefs.clipboard.historyEnabled isEqualTo true },
|
||||
visibleIf = { AndroidVersion.ATLEAST_API33_T },
|
||||
)
|
||||
DialogSliderPreference(
|
||||
prefs.clipboard.autoCleanSensitiveAfter,
|
||||
title = stringRes(R.string.pref__clipboard__auto_clean_sensitive_after__label),
|
||||
valueLabel = { pluralsRes(R.plurals.unit__seconds__written, it, "v" to it) },
|
||||
min = 0,
|
||||
max = 300,
|
||||
stepIncrement = 10,
|
||||
enabledIf = { prefs.clipboard.historyEnabled isEqualTo true && prefs.clipboard.autoCleanSensitive isEqualTo true },
|
||||
visibleIf = { AndroidVersion.ATLEAST_API33_T },
|
||||
)
|
||||
SwitchPreference(
|
||||
prefs.clipboard.limitHistorySize,
|
||||
title = stringRes(R.string.pref__clipboard__limit_history_size__label),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
@@ -25,12 +25,17 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material3.AlertDialogDefaults
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -73,6 +78,7 @@ import dev.patrickgold.florisboard.lib.compose.FlorisButtonBar
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisDropdownLikeButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisDropdownMenu
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
@@ -178,7 +184,7 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
})
|
||||
|
||||
val selectValue = stringRes(R.string.settings__localization__subtype_select_placeholder)
|
||||
val selectListValues = remember (selectValue) { listOf(selectValue) }
|
||||
val selectListValues = remember(selectValue) { listOf(selectValue) }
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val navController = LocalNavController.current
|
||||
@@ -229,7 +235,7 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
@Composable
|
||||
fun SubtypePropertyDropdown(
|
||||
title: String,
|
||||
layoutType: LayoutType
|
||||
layoutType: LayoutType,
|
||||
) {
|
||||
SubtypeProperty(title) {
|
||||
SubtypeLayoutDropdown(
|
||||
@@ -327,6 +333,7 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
DisplayLanguageNamesIn.NATIVE_LOCALE -> suggestedPreset.locale.displayName(suggestedPreset.locale)
|
||||
},
|
||||
secondaryText = suggestedPreset.preferred.characters.componentId,
|
||||
colors = ListItemDefaults.colors(containerColor = CardDefaults.cardColors().containerColor),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@@ -481,20 +488,30 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
showSubtypePresetsDialog = false
|
||||
},
|
||||
) {
|
||||
LazyColumn {
|
||||
items(subtypePresets) { subtypePreset ->
|
||||
JetPrefListItem(
|
||||
modifier = Modifier.clickable {
|
||||
subtypeEditor.applySubtype(subtypePreset.toSubtype())
|
||||
showSubtypePresetsDialog = false
|
||||
},
|
||||
text = when (displayLanguageNamesIn) {
|
||||
DisplayLanguageNamesIn.SYSTEM_LOCALE -> subtypePreset.locale.displayName()
|
||||
DisplayLanguageNamesIn.NATIVE_LOCALE -> subtypePreset.locale.displayName(subtypePreset.locale)
|
||||
},
|
||||
secondaryText = subtypePreset.preferred.characters.componentId,
|
||||
)
|
||||
Column {
|
||||
HorizontalDivider()
|
||||
val lazyListState = rememberLazyListState()
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.florisScrollbar(lazyListState, isVertical = true).weight(1f),
|
||||
state = lazyListState,
|
||||
) {
|
||||
items(subtypePresets) { subtypePreset ->
|
||||
JetPrefListItem(
|
||||
modifier = Modifier.clickable {
|
||||
subtypeEditor.applySubtype(subtypePreset.toSubtype())
|
||||
showSubtypePresetsDialog = false
|
||||
},
|
||||
text = when (displayLanguageNamesIn) {
|
||||
DisplayLanguageNamesIn.SYSTEM_LOCALE -> subtypePreset.locale.displayName()
|
||||
DisplayLanguageNamesIn.NATIVE_LOCALE -> subtypePreset.locale.displayName(subtypePreset.locale)
|
||||
},
|
||||
secondaryText = subtypePreset.preferred.characters.componentId,
|
||||
colors = ListItemDefaults.colors(containerColor = AlertDialogDefaults.containerColor),
|
||||
)
|
||||
}
|
||||
}
|
||||
HorizontalDivider()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Patrick Goldinger
|
||||
* Copyright (C) 2024-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.
|
||||
@@ -19,10 +19,18 @@ package dev.patrickgold.florisboard.app.settings.media
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.EmojiSymbols
|
||||
import androidx.compose.material.icons.outlined.Schedule
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistory
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistoryHelper
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSkinTone
|
||||
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSuggestionType
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
@@ -31,8 +39,11 @@ import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalJetPrefDatastoreUi::class)
|
||||
@Composable
|
||||
@@ -41,6 +52,11 @@ fun MediaScreen() = FlorisScreen {
|
||||
previewFieldVisible = true
|
||||
iconSpaceReserved = true
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
|
||||
var shouldDelete by remember { mutableStateOf<ShouldDelete?>(null) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
content {
|
||||
ListPreference(
|
||||
prefs.emoji.preferredSkinTone,
|
||||
@@ -85,6 +101,21 @@ fun MediaScreen() = FlorisScreen {
|
||||
stepIncrement = 1,
|
||||
enabledIf = { prefs.emoji.historyEnabled.isTrue() },
|
||||
)
|
||||
Preference(
|
||||
title = stringRes(R.string.prefs__media__emoji_history_pinned_reset),
|
||||
onClick = {
|
||||
shouldDelete = ShouldDelete(true)
|
||||
},
|
||||
enabledIf = { prefs.emoji.historyEnabled.isTrue() },
|
||||
)
|
||||
Preference(
|
||||
title = stringRes(R.string.prefs__media__emoji_history_reset),
|
||||
onClick = {
|
||||
shouldDelete = ShouldDelete(false)
|
||||
},
|
||||
enabledIf = { prefs.emoji.historyEnabled.isTrue() },
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
PreferenceGroup(title = stringRes(R.string.prefs__media__emoji_suggestion__title)) {
|
||||
@@ -138,4 +169,49 @@ fun MediaScreen() = FlorisScreen {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
DeleteEmojiHistoryConfirmDialog(
|
||||
shouldDelete = shouldDelete,
|
||||
onDismiss = {
|
||||
shouldDelete = null
|
||||
},
|
||||
onConfirm = {
|
||||
shouldDelete?.let {
|
||||
scope.launch {
|
||||
if (it.pinned) {
|
||||
EmojiHistoryHelper.deletePinned(prefs = prefs)
|
||||
} else {
|
||||
EmojiHistoryHelper.deleteHistory(prefs = prefs)
|
||||
}
|
||||
}
|
||||
shouldDelete = null
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DeleteEmojiHistoryConfirmDialog(
|
||||
shouldDelete: ShouldDelete?,
|
||||
onDismiss: () -> Unit,
|
||||
onConfirm: () -> Unit,
|
||||
) {
|
||||
shouldDelete?.let {
|
||||
JetPrefAlertDialog(
|
||||
title = stringRes(R.string.action__reset_confirm_title),
|
||||
confirmLabel = stringRes(R.string.action__yes),
|
||||
dismissLabel = stringRes(R.string.action__no),
|
||||
onDismiss = onDismiss,
|
||||
onConfirm = onConfirm,
|
||||
) {
|
||||
if (it.pinned) {
|
||||
Text(stringRes(R.string.action__reset_confirm_message, "name" to "pinned emojis"))
|
||||
} else {
|
||||
Text(stringRes(R.string.action__reset_confirm_message, "name" to "emoji history"))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ShouldDelete(val pinned: Boolean)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
@@ -19,12 +19,14 @@ 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.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.runtime.Composable
|
||||
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
|
||||
@@ -41,8 +43,11 @@ import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
|
||||
import dev.patrickgold.florisboard.themeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.ColorPickerPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.isMaterialYou
|
||||
import org.florisboard.lib.color.ColorMappings
|
||||
|
||||
@Composable
|
||||
fun ThemeScreen() = FlorisScreen {
|
||||
@@ -108,6 +113,22 @@ fun ThemeScreen() = FlorisScreen {
|
||||
navController.navigate(Routes.Settings.ThemeManager(ThemeManagerScreenAction.SELECT_NIGHT))
|
||||
},
|
||||
)
|
||||
ColorPickerPreference(
|
||||
pref = prefs.theme.accentColor,
|
||||
title = stringRes(R.string.pref__theme__theme_accent_color__label),
|
||||
defaultValueLabel = stringRes(R.string.action__default),
|
||||
icon = Icons.Default.ColorLens,
|
||||
defaultColors = ColorMappings.colors,
|
||||
showAlphaSlider = false,
|
||||
enableAdvancedLayout = false,
|
||||
colorOverride = {
|
||||
if (it.isMaterialYou(context)) {
|
||||
Color.Unspecified
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
AddonManagementReferenceBox(type = ExtensionListScreenType.EXT_THEME)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
@@ -36,7 +36,6 @@ import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
|
||||
import dev.patrickgold.florisboard.ime.nlp.SpellingLanguageMode
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisErrorCard
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisHyperlinkText
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
@@ -48,6 +47,7 @@ import dev.patrickgold.jetpref.datastore.ui.ListPreference
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
|
||||
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
|
||||
@OptIn(ExperimentalJetPrefDatastoreUi::class)
|
||||
@Composable
|
||||
@@ -58,11 +58,11 @@ fun TypingScreen() = FlorisScreen {
|
||||
val navController = LocalNavController.current
|
||||
|
||||
content {
|
||||
// This card is temporary and is therefore not using a string resource
|
||||
// This card is temporary and is therefore not using a string resource (not so temporary as we thought...)
|
||||
FlorisErrorCard(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
text = """
|
||||
Suggestions (except system autofill) and spell checking are not available in this alpha release. All
|
||||
Suggestions (except system autofill) and spell checking are not available in this release. All
|
||||
preferences in the "Corrections" group are properly implemented though.
|
||||
""".trimIndent().replace('\n', ' '),
|
||||
)
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.setup
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
@@ -45,9 +45,6 @@ import dev.patrickgold.florisboard.app.FlorisAppActivity
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
import dev.patrickgold.florisboard.app.Routes
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import dev.patrickgold.florisboard.lib.util.launchActivity
|
||||
import dev.patrickgold.florisboard.lib.util.launchUrl
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisBulletSpacer
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreenScope
|
||||
@@ -56,9 +53,12 @@ import dev.patrickgold.florisboard.lib.compose.FlorisStepLayout
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisStepState
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.lib.util.InputMethodUtils
|
||||
import dev.patrickgold.florisboard.lib.util.launchActivity
|
||||
import dev.patrickgold.florisboard.lib.util.launchUrl
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import dev.patrickgold.jetpref.datastore.ui.PreferenceUiScope
|
||||
import kotlinx.coroutines.delay
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
|
||||
|
||||
@Composable
|
||||
@@ -105,128 +105,65 @@ private fun FlorisScreenScope.content(
|
||||
hasNotificationPermission: NotificationPermissionState,
|
||||
) {
|
||||
|
||||
// Show screen without notification permission if the android version is below android 13.
|
||||
if (AndroidVersion.ATMOST_API32_S_V2) {
|
||||
val stepState = rememberSaveable(saver = FlorisStepState.Saver) {
|
||||
val initStep = when {
|
||||
!isFlorisBoardEnabled -> Steps.EnableIme.id
|
||||
!isFlorisBoardSelected -> Steps.SelectIme.id
|
||||
hasNotificationPermission == NotificationPermissionState.NOT_SET && AndroidVersion.ATLEAST_API33_T -> Steps.SelectNotification.id
|
||||
else -> Steps.FinishUp.id
|
||||
}
|
||||
FlorisStepState.new(init = initStep)
|
||||
}
|
||||
|
||||
val stepState = rememberSaveable(saver = FlorisStepState.Saver) {
|
||||
val initStep = when {
|
||||
!isFlorisBoardEnabled -> Steps.WithoutNotifications.EnableIme.id
|
||||
!isFlorisBoardSelected -> Steps.WithoutNotifications.SelectIme.id
|
||||
else -> Steps.WithoutNotifications.FinishUp.id
|
||||
}
|
||||
FlorisStepState.new(init = initStep)
|
||||
content {
|
||||
LaunchedEffect(isFlorisBoardEnabled, isFlorisBoardSelected, hasNotificationPermission) {
|
||||
stepState.setCurrentAuto(
|
||||
when {
|
||||
!isFlorisBoardEnabled -> Steps.EnableIme.id
|
||||
!isFlorisBoardSelected -> Steps.SelectIme.id
|
||||
hasNotificationPermission == NotificationPermissionState.NOT_SET && AndroidVersion.ATLEAST_API33_T -> Steps.SelectNotification.id
|
||||
else -> Steps.FinishUp.id
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
content {
|
||||
LaunchedEffect(isFlorisBoardEnabled, isFlorisBoardSelected) {
|
||||
stepState.setCurrentAuto(
|
||||
when {
|
||||
!isFlorisBoardEnabled -> Steps.WithoutNotifications.EnableIme.id
|
||||
!isFlorisBoardSelected -> Steps.WithoutNotifications.SelectIme.id
|
||||
else -> Steps.WithoutNotifications.FinishUp.id
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Below block allows to return from the system IME enabler activity
|
||||
// as soon as it gets selected.
|
||||
LaunchedEffect(Unit) {
|
||||
while (true) {
|
||||
delay(200L)
|
||||
val isEnabled = InputMethodUtils.isFlorisboardEnabled(context)
|
||||
if (stepState.getCurrentAuto().value == Steps.WithoutNotifications.EnableIme.id &&
|
||||
stepState.getCurrentManual().value == -1 &&
|
||||
!isFlorisBoardEnabled &&
|
||||
!isFlorisBoardSelected &&
|
||||
isEnabled
|
||||
) {
|
||||
context.launchActivity(FlorisAppActivity::class) {
|
||||
it.flags = (Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
|
||||
or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
or Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
}
|
||||
// Below block allows to return from the system IME enabler activity
|
||||
// as soon as it gets selected.
|
||||
LaunchedEffect(Unit) {
|
||||
while (true) {
|
||||
delay(200L)
|
||||
val isEnabled = InputMethodUtils.isFlorisboardEnabled(context)
|
||||
if (stepState.getCurrentAuto().value == Steps.EnableIme.id &&
|
||||
stepState.getCurrentManual().value == -1 &&
|
||||
!isFlorisBoardEnabled &&
|
||||
!isFlorisBoardSelected &&
|
||||
hasNotificationPermission == NotificationPermissionState.NOT_SET &&
|
||||
isEnabled
|
||||
) {
|
||||
context.launchActivity(FlorisAppActivity::class) {
|
||||
it.flags = (Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
|
||||
or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
or Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
}
|
||||
}
|
||||
}
|
||||
FlorisStepLayout(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 16.dp),
|
||||
stepState = stepState,
|
||||
header = {
|
||||
StepText(stringRes(R.string.setup__intro_message))
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
},
|
||||
steps = steps(
|
||||
context, navController, requestNotification
|
||||
),
|
||||
footer = {
|
||||
footer(context)
|
||||
},
|
||||
)
|
||||
}
|
||||
// Show the screen with notification permission on android 13+
|
||||
} else {
|
||||
val stepState = rememberSaveable(saver = FlorisStepState.Saver) {
|
||||
val initStep = when {
|
||||
!isFlorisBoardEnabled -> Steps.WithNotifications.EnableIme.id
|
||||
!isFlorisBoardSelected -> Steps.WithNotifications.SelectIme.id
|
||||
hasNotificationPermission == NotificationPermissionState.NOT_SET -> Steps.WithNotifications.SelectNotification.id
|
||||
else -> Steps.WithNotifications.FinishUp.id
|
||||
}
|
||||
FlorisStepState.new(init = initStep)
|
||||
}
|
||||
|
||||
content {
|
||||
LaunchedEffect(isFlorisBoardEnabled, isFlorisBoardSelected, hasNotificationPermission) {
|
||||
stepState.setCurrentAuto(
|
||||
when {
|
||||
!isFlorisBoardEnabled -> Steps.WithNotifications.EnableIme.id
|
||||
!isFlorisBoardSelected -> Steps.WithNotifications.SelectIme.id
|
||||
hasNotificationPermission == NotificationPermissionState.NOT_SET -> Steps.WithNotifications.SelectNotification.id
|
||||
else -> Steps.WithNotifications.FinishUp.id
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Below block allows to return from the system IME enabler activity
|
||||
// as soon as it gets selected.
|
||||
LaunchedEffect(Unit) {
|
||||
while (true) {
|
||||
delay(200L)
|
||||
val isEnabled = InputMethodUtils.isFlorisboardEnabled(context)
|
||||
if (stepState.getCurrentAuto().value == Steps.WithNotifications.EnableIme.id &&
|
||||
stepState.getCurrentManual().value == -1 &&
|
||||
!isFlorisBoardEnabled &&
|
||||
!isFlorisBoardSelected &&
|
||||
hasNotificationPermission == NotificationPermissionState.NOT_SET &&
|
||||
isEnabled
|
||||
) {
|
||||
context.launchActivity(FlorisAppActivity::class) {
|
||||
it.flags = (Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
|
||||
or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
or Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FlorisStepLayout(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 16.dp),
|
||||
stepState = stepState,
|
||||
header = {
|
||||
StepText(stringRes(R.string.setup__intro_message))
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
},
|
||||
steps = steps(
|
||||
context, navController, requestNotification
|
||||
),
|
||||
footer = {
|
||||
footer(context)
|
||||
},
|
||||
)
|
||||
}
|
||||
FlorisStepLayout(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 16.dp),
|
||||
stepState = stepState,
|
||||
header = {
|
||||
StepText(stringRes(R.string.setup__intro_message))
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
},
|
||||
steps = steps(
|
||||
context, navController, requestNotification
|
||||
),
|
||||
footer = {
|
||||
footer(context)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,105 +192,60 @@ private fun footer(context: Context) {
|
||||
private fun PreferenceUiScope<AppPrefs>.steps(
|
||||
context: Context,
|
||||
navController: NavController,
|
||||
requestNotification: ManagedActivityResultLauncher<String, Boolean>
|
||||
requestNotification: ManagedActivityResultLauncher<String, Boolean>,
|
||||
): List<FlorisStep> {
|
||||
return if (AndroidVersion.ATMOST_API32_S_V2) {
|
||||
listOf(
|
||||
|
||||
return listOfNotNull(
|
||||
FlorisStep(
|
||||
id = Steps.EnableIme.id,
|
||||
title = stringRes(R.string.setup__enable_ime__title),
|
||||
) {
|
||||
StepText(stringRes(R.string.setup__enable_ime__description))
|
||||
StepButton(label = stringRes(R.string.setup__enable_ime__open_settings_btn)) {
|
||||
InputMethodUtils.showImeEnablerActivity(context)
|
||||
}
|
||||
},
|
||||
FlorisStep(
|
||||
id = Steps.SelectIme.id,
|
||||
title = stringRes(R.string.setup__select_ime__title),
|
||||
) {
|
||||
StepText(stringRes(R.string.setup__select_ime__description))
|
||||
StepButton(label = stringRes(R.string.setup__select_ime__switch_keyboard_btn)) {
|
||||
InputMethodUtils.showImePicker(context)
|
||||
}
|
||||
},
|
||||
if (AndroidVersion.ATLEAST_API33_T) {
|
||||
FlorisStep(
|
||||
id = Steps.WithoutNotifications.EnableIme.id,
|
||||
title = stringRes(R.string.setup__enable_ime__title),
|
||||
) {
|
||||
StepText(stringRes(R.string.setup__enable_ime__description))
|
||||
StepButton(label = stringRes(R.string.setup__enable_ime__open_settings_btn)) {
|
||||
InputMethodUtils.showImeEnablerActivity(context)
|
||||
}
|
||||
},
|
||||
FlorisStep(
|
||||
id = Steps.WithoutNotifications.SelectIme.id,
|
||||
title = stringRes(R.string.setup__select_ime__title),
|
||||
) {
|
||||
StepText(stringRes(R.string.setup__select_ime__description))
|
||||
StepButton(label = stringRes(R.string.setup__select_ime__switch_keyboard_btn)) {
|
||||
InputMethodUtils.showImePicker(context)
|
||||
}
|
||||
},
|
||||
FlorisStep(
|
||||
id = Steps.WithoutNotifications.FinishUp.id,
|
||||
title = stringRes(R.string.setup__finish_up__title),
|
||||
) {
|
||||
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)
|
||||
navController.navigate(Routes.Settings.Home) {
|
||||
popUpTo(Routes.Setup.Screen) {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
listOf(
|
||||
FlorisStep(
|
||||
id = Steps.WithNotifications.EnableIme.id,
|
||||
title = stringRes(R.string.setup__enable_ime__title),
|
||||
) {
|
||||
StepText(stringRes(R.string.setup__enable_ime__description))
|
||||
StepButton(label = stringRes(R.string.setup__enable_ime__open_settings_btn)) {
|
||||
InputMethodUtils.showImeEnablerActivity(context)
|
||||
}
|
||||
},
|
||||
FlorisStep(
|
||||
id = Steps.WithNotifications.SelectIme.id,
|
||||
title = stringRes(R.string.setup__select_ime__title),
|
||||
) {
|
||||
StepText(stringRes(R.string.setup__select_ime__description))
|
||||
StepButton(label = stringRes(R.string.setup__select_ime__switch_keyboard_btn)) {
|
||||
InputMethodUtils.showImePicker(context)
|
||||
}
|
||||
},
|
||||
FlorisStep(
|
||||
id = Steps.WithNotifications.SelectNotification.id,
|
||||
title = stringRes(R.string.setup__grant_notification_permission__title)
|
||||
id = Steps.SelectNotification.id,
|
||||
title = stringRes(R.string.setup__grant_notification_permission__title),
|
||||
) {
|
||||
StepText(stringRes(R.string.setup__grant_notification_permission__description))
|
||||
StepButton(stringRes(R.string.setup__grant_notification_permission__btn)) {
|
||||
if (AndroidVersion.ATLEAST_API33_T) {
|
||||
requestNotification.launch(android.Manifest.permission.POST_NOTIFICATIONS)
|
||||
requestNotification.launch(android.Manifest.permission.POST_NOTIFICATIONS)
|
||||
}
|
||||
}
|
||||
} else null,
|
||||
FlorisStep(
|
||||
id = Steps.FinishUp.id,
|
||||
title = stringRes(R.string.setup__finish_up__title),
|
||||
) {
|
||||
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)
|
||||
navController.navigate(Routes.Settings.Home) {
|
||||
popUpTo(Routes.Setup.Screen) {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
},
|
||||
FlorisStep(
|
||||
id = Steps.WithNotifications.FinishUp.id,
|
||||
title = stringRes(R.string.setup__finish_up__title),
|
||||
) {
|
||||
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)
|
||||
navController.navigate(Routes.Settings.Home) {
|
||||
popUpTo(Routes.Setup.Screen) {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private sealed class Steps(val id: Int) {
|
||||
sealed class WithoutNotifications(id: Int) : Steps(id) {
|
||||
data object EnableIme : WithoutNotifications(id = 1)
|
||||
data object SelectIme : WithoutNotifications(id = 2)
|
||||
data object FinishUp : WithoutNotifications(id = 3)
|
||||
}
|
||||
|
||||
sealed class WithNotifications(id: Int) : Steps(id) {
|
||||
data object EnableIme : WithNotifications(id = 1)
|
||||
data object SelectIme : WithNotifications(id = 2)
|
||||
data object SelectNotification : WithNotifications(id = 3)
|
||||
data object FinishUp : WithNotifications(id = 4)
|
||||
}
|
||||
data object EnableIme : Steps(id = 1)
|
||||
data object SelectIme : Steps(id = 2)
|
||||
data object SelectNotification : Steps(id = 3)
|
||||
data object FinishUp : Steps(id = 4)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
@@ -214,6 +214,7 @@ fun ClipboardInputLayout(
|
||||
contentScrollInsteadOfClip: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val fontSize = style.fontSize.spSize() safeTimes 1f
|
||||
SnyggSurface(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
@@ -258,7 +259,7 @@ fun ClipboardInputLayout(
|
||||
text = bitmap.exceptionOrNull()?.message ?: "Unknown error",
|
||||
style = TextStyle(textDirection = TextDirection.Ltr),
|
||||
color = Color.Red,
|
||||
fontSize = style.fontSize.spSize(),
|
||||
fontSize = fontSize,
|
||||
)
|
||||
}
|
||||
} else if (item.type == ItemType.VIDEO) {
|
||||
@@ -305,7 +306,7 @@ fun ClipboardInputLayout(
|
||||
text = bitmap.exceptionOrNull()?.message ?: "Unknown error",
|
||||
style = TextStyle(textDirection = TextDirection.Ltr),
|
||||
color = Color.Red,
|
||||
fontSize = style.fontSize.spSize(),
|
||||
fontSize = fontSize,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@@ -510,7 +511,7 @@ fun ClipboardInputLayout(
|
||||
Text(
|
||||
text = stringRes(R.string.clipboard__empty__message),
|
||||
color = itemStyle.foreground.solidColor(context),
|
||||
fontSize = itemStyle.fontSize.spSize(),
|
||||
fontSize = itemStyle.fontSize.spSize() safeTimes 1f,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
@@ -544,7 +545,7 @@ fun ClipboardInputLayout(
|
||||
Text(
|
||||
text = stringRes(R.string.clipboard__disabled__message),
|
||||
color = itemStyle.foreground.solidColor(context),
|
||||
fontSize = itemStyle.fontSize.spSize(),
|
||||
fontSize = itemStyle.fontSize.spSize() safeTimes 1f,
|
||||
)
|
||||
SnyggButton(
|
||||
modifier = Modifier
|
||||
@@ -578,7 +579,7 @@ fun ClipboardInputLayout(
|
||||
Text(
|
||||
text = stringRes(R.string.clipboard__locked__message),
|
||||
color = itemStyle.foreground.solidColor(context),
|
||||
fontSize = itemStyle.fontSize.spSize(),
|
||||
fontSize = itemStyle.fontSize.spSize() safeTimes 1f,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
@@ -254,14 +254,20 @@ class ClipboardManager(
|
||||
}
|
||||
|
||||
private fun enforceExpiryDate(clipHistory: ClipboardHistory) {
|
||||
val itemsToRemove = mutableSetOf<ClipboardItem>()
|
||||
if (prefs.clipboard.cleanUpOld.get()) {
|
||||
val nonPinnedItems = clipHistory.recent + clipHistory.other
|
||||
val expiryTime = System.currentTimeMillis() - (prefs.clipboard.cleanUpAfter.get() * 60 * 1000)
|
||||
val itemsToRemove = nonPinnedItems.filter { it.creationTimestampMs < expiryTime }
|
||||
if (itemsToRemove.isNotEmpty()) {
|
||||
ioScope.launch {
|
||||
clipHistoryDao?.delete(itemsToRemove)
|
||||
}
|
||||
itemsToRemove.addAll(nonPinnedItems.filter { it.creationTimestampMs < expiryTime })
|
||||
}
|
||||
if (prefs.clipboard.autoCleanSensitive.get()) {
|
||||
val sensitiveData = clipHistory.all.filter { it.isSensitive }
|
||||
val expiryTime = System.currentTimeMillis() - (prefs.clipboard.autoCleanSensitiveAfter.get() * 1000)
|
||||
itemsToRemove.addAll(sensitiveData.filter { it.creationTimestampMs < expiryTime })
|
||||
}
|
||||
if (itemsToRemove.isNotEmpty()) {
|
||||
ioScope.launch {
|
||||
clipHistoryDao?.delete(itemsToRemove.toList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.ime.clipboard
|
||||
|
||||
import android.content.ClipData
|
||||
@@ -27,6 +43,7 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.isUnspecified
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -105,8 +122,8 @@ class FlorisCopyToClipboardActivity : ComponentActivity() {
|
||||
setContent {
|
||||
ProvideLocalizedResources(this, forceLayoutDirection = LayoutDirection.Ltr) {
|
||||
val theme by prefs.advanced.settingsTheme.observeAsState()
|
||||
val isMaterialYouAware by prefs.advanced.useMaterialYou.observeAsState()
|
||||
FlorisAppTheme(theme, isMaterialYouAware) {
|
||||
val accentColor by prefs.advanced.accentColor.observeAsState()
|
||||
FlorisAppTheme(theme, accentColor.isUnspecified) {
|
||||
BottomSheet {
|
||||
Row {
|
||||
Text(
|
||||
@@ -146,7 +163,9 @@ class FlorisCopyToClipboardActivity : ComponentActivity() {
|
||||
Column {
|
||||
content()
|
||||
Button(
|
||||
modifier = Modifier.align(Alignment.End).padding(16.dp),
|
||||
modifier = Modifier
|
||||
.align(Alignment.End)
|
||||
.padding(16.dp),
|
||||
onClick = { finish() },
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
//containerColor = buttonContainer.background.solidColor(context = context),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
@@ -242,6 +242,7 @@ data class ClipboardItem @OptIn(ExperimentalSerializationApi::class) constructor
|
||||
if (uri != other.uri) return false
|
||||
if (creationTimestampMs != other.creationTimestampMs) return false
|
||||
if (!mimeTypes.contentEquals(other.mimeTypes)) return false
|
||||
if (isSensitive != other.isSensitive) return false
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -253,6 +254,7 @@ data class ClipboardItem @OptIn(ExperimentalSerializationApi::class) constructor
|
||||
result = 31 * result + (uri?.hashCode() ?: 0)
|
||||
result = 31 * result + creationTimestampMs.hashCode()
|
||||
result = 31 * result + mimeTypes.contentHashCode()
|
||||
result = 31 * result + isSensitive.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* 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.ime.core
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.RadioButtonChecked
|
||||
import androidx.compose.material.icons.filled.RadioButtonUnchecked
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.ime.keyboard.KeyboardState
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.rippleClickable
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import dev.patrickgold.florisboard.subtypeManager
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import org.florisboard.lib.snygg.ui.snyggBackground
|
||||
import org.florisboard.lib.snygg.ui.snyggClip
|
||||
import org.florisboard.lib.snygg.ui.solidColor
|
||||
import org.florisboard.lib.snygg.ui.spSize
|
||||
|
||||
@Composable
|
||||
fun SelectSubtypePanel(modifier: Modifier = Modifier) {
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val subtypeManager by context.subtypeManager()
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
val subtypes by subtypeManager.subtypesFlow.collectAsState()
|
||||
|
||||
val currentlySelected = subtypeManager.activeSubtype.id
|
||||
|
||||
val panelStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarActionsEditor)
|
||||
val headerStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarActionsEditorHeader)
|
||||
val subheaderStyle = FlorisImeTheme.style.get(FlorisImeUi.SmartbarActionsEditorSubheader)
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.snyggBackground(context, panelStyle, fallbackColor = FlorisImeTheme.fallbackSurfaceColor())
|
||||
.snyggClip(panelStyle),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(48.dp)
|
||||
.snyggBackground(context, headerStyle),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.clickable(false) {},
|
||||
text = stringRes(R.string.select_subtype_panel__header),
|
||||
color = headerStyle.foreground.solidColor(
|
||||
context,
|
||||
default = FlorisImeTheme.fallbackContentColor()
|
||||
),
|
||||
fontSize = headerStyle.fontSize.spSize(),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
|
||||
Box {
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
) {
|
||||
items(
|
||||
subtypes,
|
||||
key = {
|
||||
it.id
|
||||
}
|
||||
) {
|
||||
JetPrefListItem(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.rippleClickable {
|
||||
subtypeManager.switchToSubtypeById(it.id)
|
||||
keyboardManager.activeState.isSubtypeSelectionVisible = false
|
||||
},
|
||||
icon = {
|
||||
if (currentlySelected == it.id) {
|
||||
Icon(Icons.Default.RadioButtonChecked, null)
|
||||
} else {
|
||||
Icon(Icons.Default.RadioButtonUnchecked, null)
|
||||
}
|
||||
},
|
||||
text = it.primaryLocale.displayName(),
|
||||
colors = ListItemDefaults.colors(
|
||||
leadingIconColor = subheaderStyle.foreground.solidColor(context),
|
||||
headlineColor = subheaderStyle.foreground.solidColor(context),
|
||||
containerColor = subheaderStyle.background.solidColor(context),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(Modifier
|
||||
.systemBarsPadding()
|
||||
.snyggBackground(context, panelStyle))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun KeyboardState.isSubtypeSelectionShowing(): Boolean {
|
||||
return isSubtypeSelectionVisible
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
@@ -24,7 +24,6 @@ import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogDebug
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@@ -227,4 +226,11 @@ class SubtypeManager(context: Context) {
|
||||
prefs.localization.activeSubtypeId.set(newActiveSubtype.id)
|
||||
activeSubtype = newActiveSubtype
|
||||
}
|
||||
|
||||
fun switchToSubtypeById(id: Long) {
|
||||
if (subtypes.any { it.id == id }) {
|
||||
activeSubtype = getSubtypeById(id)!!
|
||||
prefs.localization.activeSubtypeId.set(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
@@ -634,7 +634,7 @@ abstract class AbstractEditorInstance(context: Context) {
|
||||
suspend fun popUntilOrNull(predicate: (EditorContent) -> Boolean): EditorContent? {
|
||||
return list.withLock { list ->
|
||||
while (list.isNotEmpty()) {
|
||||
val item = list.removeFirst()
|
||||
val item = list.removeAt(0)
|
||||
if (predicate(item)) return@withLock item
|
||||
}
|
||||
return@withLock null
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Patrick Goldinger
|
||||
* Copyright (C) 2021-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.ime.input
|
||||
|
||||
enum class CapitalizationBehavior {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Patrick Goldinger
|
||||
* Copyright (C) 2022-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.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user