Compare commits

..

1 Commits

Author SHA1 Message Date
lm41
66491c27fc Make emoji history scrollable 2025-07-26 13:46:31 +02:00
185 changed files with 1757 additions and 2750 deletions

View File

@@ -44,8 +44,7 @@ body:
label: Install Source
options:
- Google PlayStore
- F-Droid (F-Droid Main)
- F-Droid (IzzyOnDroid)
- F-Droid
- GitHub
validations:
required: true

View File

@@ -10,7 +10,7 @@ The FlorisBoard community is international, as such we require all contributions
### Translations
To make FlorisBoard accessible in as many languages as possible, the platform [Crowdin](https://crowdin.florisboard.org) is used to crowdsource and manage translations. The list of languages in Crowdin covers a good range of languages, but feel free to email [florisboard@patrickgold.dev](mailto:florisboard@patrickgold.dev) to request a new language.
To make FlorisBoard accessible in as many languages as possible, the platform [Crowdin](https://crowdin.florisboard.patrickgold.dev) is used to crowdsource and manage translations. The list of languages in Crowdin covers a good range of languages, but feel free to email [florisboard@patrickgold.dev](mailto:florisboard@patrickgold.dev) to request a new language.
> [!IMPORTANT]
> This is the only source of translations - **PRs that add/update translations are not accepted.**

View File

@@ -1,11 +1,11 @@
<img align="left" width="80" height="80"
src=".github/repo_icon.png" alt="App icon">
# FlorisBoard [![Crowdin](https://badges.crowdin.net/florisboard/localized.svg)](https://crowdin.florisboard.org) [![Matrix badge](https://img.shields.io/badge/chat-%23florisboard%3amatrix.org-blue)](https://matrix.to/#/#florisboard:matrix.org) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md) [![FlorisBoard CI](https://github.com/florisboard/florisboard/actions/workflows/android.yml/badge.svg?event=push)](https://github.com/florisboard/florisboard/actions/workflows/android.yml)
# FlorisBoard [![Crowdin](https://badges.crowdin.net/florisboard/localized.svg)](https://crowdin.florisboard.patrickgold.dev) [![Matrix badge](https://img.shields.io/badge/chat-%23florisboard%3amatrix.org-blue)](https://matrix.to/#/#florisboard:matrix.org) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md) [![FlorisBoard CI](https://github.com/florisboard/florisboard/actions/workflows/android.yml/badge.svg?event=push)](https://github.com/florisboard/florisboard/actions/workflows/android.yml)
**FlorisBoard** is a free and open-source keyboard for Android 8.0+
**FlorisBoard** is a free and open-source keyboard for Android 7.0+
devices. It aims at being modern, user-friendly and customizable while
fully respecting your privacy. Currently in beta state.
fully respecting your privacy. Currently in early-beta state.
<table>
<tr>
@@ -26,13 +26,10 @@ fully respecting your privacy. Currently in beta state.
</tr>
<tr>
<td valign="top">
<p>
<a href="https://apt.izzysoft.de/fdroid/index/apk/dev.patrickgold.florisboard"><img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" height="64" alt="IzzySoft repo badge"></a>
<a href="https://f-droid.org/packages/dev.patrickgold.florisboard"><img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="64" alt="F-Droid badge"></a>
</p>
<p><a href="https://f-droid.org/packages/dev.patrickgold.florisboard"><img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="64" alt="F-Droid badge"></a></p>
<p>
**Google Play**: Join the [FlorisBoard Test Group](https://groups.google.com/g/florisboard-closed-beta-test), then visit the [testing page](https://play.google.com/apps/testing/dev.patrickgold.florisboard). Once joined and installed, updates will be delivered like for any other app. ([Store entry](https://play.google.com/store/apps/details?id=dev.patrickgold.florisboard))
**Google Play**: Join the [FlorisBoard Test Group](https://groups.google.com/g/florisboard-public-alpha-test), then visit the [testing page](https://play.google.com/apps/testing/dev.patrickgold.florisboard). Once joined and installed, updates will be delivered like for any other app. ([Store entry](https://play.google.com/store/apps/details?id=dev.patrickgold.florisboard))
</p>
<p>
@@ -50,7 +47,7 @@ fully respecting your privacy. Currently in beta state.
<p><a href="https://apt.izzysoft.de/fdroid/index/apk/dev.patrickgold.florisboard.beta"><img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" height="64" alt="IzzySoft repo badge"></a></p>
<p>
**Google Play**: Join the [FlorisBoard Test Group](https://groups.google.com/g/florisboard-closed-beta-test), then visit the [preview testing page](https://play.google.com/apps/testing/dev.patrickgold.florisboard.beta). Once joined and installed, updates will be delivered like for any other app. ([Store entry](https://play.google.com/store/apps/details?id=dev.patrickgold.florisboard.beta))
**Google Play**: Join the [FlorisBoard Test Group](https://groups.google.com/g/florisboard-public-alpha-test), then visit the [preview testing page](https://play.google.com/apps/testing/dev.patrickgold.florisboard.beta). Once joined and installed, updates will be delivered like for any other app. ([Store entry](https://play.google.com/store/apps/details?id=dev.patrickgold.florisboard.beta))
</p>
<p>
@@ -99,22 +96,6 @@ Many thanks to Ali ([@4H1R](https://github.com/4H1R)) for implementing the store
Please refer to this [page](https://github.com/florisboard/florisboard/wiki/List-of-permissions-FlorisBoard-requests)
to get more information on this topic.
## APK signing certificate hashes
The package names and SHA-256 hashes of the signature certificate are listed below, so you can verify both FlorisBoard variants with apksigner by using `apksigner verify --print-certs florisboard-<version>-<track>.apk` when you download the APK.
If you have [AppVerifier](https://github.com/soupslurpr/AppVerifier) installed, you can alternatively copy both the package name and the hash of the corresponding track and share them to AppVerifier.
##### Stable track:
dev.patrickgold.florisboard<br>
0B:80:71:64:50:8E:AF:EB:1F:BB:81:5B:E7:A2:3C:77:FE:68:9D:94:B1:43:75:C9:9B:DA:A9:B6:57:7F:D6:D6
##### Preview track:
dev.patrickgold.florisboard.beta<br>
0B:80:71:64:50:8E:AF:EB:1F:BB:81:5B:E7:A2:3C:77:FE:68:9D:94:B1:43:75:C9:9B:DA:A9:B6:57:7F:D6:D6
## Used libraries, components and icons
* [AndroidX libraries](https://github.com/androidx/androidx) by
[Android Jetpack](https://github.com/androidx)

View File

@@ -9,11 +9,13 @@ Each major milestone has associated alpha/beta releases, so if you are intereste
> [!NOTE]
> The milestone 0.5 was split, thus the word suggestions now come with version 0.6. The old version 0.6 has been moved down and is now 0.7. The time it takes to implement word suggestions will not change, but we can now release the new theme editor earlier, which would otherwise lie dormant.
- [x] Theme rework part II / Snygg v2
- [ ] Theme rework part II / Snygg v2
- [x] See https://github.com/florisboard/florisboard/pull/2855
- [x] Spaces in URI bug (See https://github.com/florisboard/florisboard/issues/2898)
- [ ] Rework cache manager (See https://github.com/florisboard/florisboard/issues/2870)
- [x] Re-add time based theme switching (See https://github.com/florisboard/florisboard/pull/2977)
- [x] Add support for any remaining new features introduced with Android 13 / 14
- [ ] Add support for any remaining new features introduced with Android 13 / 14
- [ ] Proper physical keyboard support (See https://github.com/florisboard/florisboard/issues/2815)
- [x] Raise minimum required Android version from Android 7 (SDK level 24) to Android 8 (SDK level 26)
## 0.6
@@ -22,8 +24,6 @@ Each major milestone has associated alpha/beta releases, so if you are intereste
- [ ] Add new extension type: Language Pack
- Basically groups all locale-relevant data (predictive base model, emoji suggestion data, ...)
in a dynamically importable extension file
- [ ] Proper physical keyboard support (See https://github.com/florisboard/florisboard/issues/1972)
- [ ] Rework cache manager (See https://github.com/florisboard/florisboard/issues/2870)
## k3lp
@@ -62,6 +62,7 @@ Each major milestone has associated alpha/beta releases, so if you are intereste
- Text translation
- Stickers/GIFs
- Kaomoji panel implementation
- FlorisBoard landing web page for presentation
- Implementing additional layouts
- Support for Tasker/Automate/MacroDroid plugins
- Support for WearOS/Smartwatches

View File

@@ -200,20 +200,17 @@ dependencies {
ksp(libs.androidx.room.compiler)
implementation(libs.androidx.room.runtime)
implementation(libs.cache4k)
implementation(libs.kotlin.reflect)
implementation(libs.kotlinx.coroutines)
implementation(libs.kotlinx.serialization.json)
implementation(libs.mikepenz.aboutlibraries.core)
implementation(libs.mikepenz.aboutlibraries.compose)
implementation(libs.patrickgold.compose.tooltip)
implementation(libs.patrickgold.jetpref.datastore.model)
ksp(libs.patrickgold.jetpref.datastore.model.processor)
implementation(libs.patrickgold.jetpref.datastore.ui)
implementation(libs.patrickgold.jetpref.material.ui)
implementation(project(":lib:android"))
implementation(project(":lib:color"))
implementation(project(":lib:compose"))
implementation(project(":lib:kotlin"))
implementation(project(":lib:native"))
implementation(project(":lib:snygg"))

View File

@@ -157,13 +157,6 @@
"direction": "ltr",
"modifier": "org.florisboard.layouts:dvorak"
},
{
"id": "dvorak_se",
"label": "Dvorak (SE)",
"authors": [ "iceaway" ],
"direction": "ltr",
"modifier": "org.florisboard.layouts:dvorak_se"
},
{
"id": "esperanto",
"label": "Esperanto",
@@ -517,12 +510,6 @@
"authors": [ "msrd0" ],
"direction": "ltr"
},
{
"id": "dvorak_se",
"label": "Dvorak (SE)",
"authors": [ "iceaway" ],
"direction": "ltr"
},
{
"id": "hebrew",
"label": "עברית",
@@ -621,12 +608,6 @@
"authors": [ "waelwindows" ],
"direction": "ltr"
},
{
"id": "czech",
"label": "Czech",
"authors": [ "bmondream" ],
"direction": "ltr"
},
{
"id": "devanagari",
"label": "Devanagari",

View File

@@ -1,37 +0,0 @@
[
[
{ "$": "auto_text_key", "code": 229, "label": "å" },
{ "$": "auto_text_key", "code": 228, "label": "ä" },
{ "$": "auto_text_key", "code": 246, "label": "ö" },
{ "$": "auto_text_key", "code": 112, "label": "p" },
{ "$": "auto_text_key", "code": 121, "label": "y" },
{ "$": "auto_text_key", "code": 102, "label": "f" },
{ "$": "auto_text_key", "code": 103, "label": "g" },
{ "$": "auto_text_key", "code": 99, "label": "c" },
{ "$": "auto_text_key", "code": 114, "label": "r" },
{ "$": "auto_text_key", "code": 108, "label": "l" }
],
[
{ "$": "auto_text_key", "code": 97, "label": "a" },
{ "$": "auto_text_key", "code": 111, "label": "o" },
{ "$": "auto_text_key", "code": 101, "label": "e" },
{ "$": "auto_text_key", "code": 117, "label": "u" },
{ "$": "auto_text_key", "code": 105, "label": "i" },
{ "$": "auto_text_key", "code": 100, "label": "d" },
{ "$": "auto_text_key", "code": 104, "label": "h" },
{ "$": "auto_text_key", "code": 116, "label": "t" },
{ "$": "auto_text_key", "code": 110, "label": "n" },
{ "$": "auto_text_key", "code": 115, "label": "s" }
],
[
{ "$": "auto_text_key", "code": 113, "label": "q" },
{ "$": "auto_text_key", "code": 106, "label": "j" },
{ "$": "auto_text_key", "code": 107, "label": "k" },
{ "$": "auto_text_key", "code": 120, "label": "x" },
{ "$": "auto_text_key", "code": 98, "label": "b" },
{ "$": "auto_text_key", "code": 109, "label": "m" },
{ "$": "auto_text_key", "code": 119, "label": "w" },
{ "$": "auto_text_key", "code": 118, "label": "v" },
{ "$": "auto_text_key", "code": 122, "label": "z" }
]
]

View File

@@ -1,16 +0,0 @@
[
[
{ "code": -11, "label": "shift", "type": "modifier" },
{ "code": 0, "type": "placeholder" },
{ "code": -7, "label": "delete", "type": "enter_editing" }
],
[
{ "code": -202, "label": "view_symbols", "type": "system_gui" },
{ "$": "auto_text_key", "code": 44, "label": "," },
{ "code": -227, "label": "language_switch", "type": "system_gui" },
{ "code": -212, "label": "ime_ui_mode_media", "type": "system_gui" },
{ "code": 32, "label": "space" },
{ "$": "auto_text_key", "code": 46, "label": "." },
{ "code": 10, "label": "enter", "groupId": 3, "type": "enter_editing" }
]
]

View File

@@ -1,119 +0,0 @@
[
[
{ "$": "shift_state_selector",
"default": {
"code": 43, "label": "+", "type": "numeric", "popup": {
"main": { "code": 49, "label": "1" }
}
}
},
{ "$": "shift_state_selector",
"shiftedManual": {
"code": 282, "label": "Ě", "type": "numeric", "popup": {
"main": { "code": 50, "label": "2" }
}
},
"default": {
"code": 283, "label": "ě", "type": "numeric", "popup": {
"main": { "code": 50, "label": "2" }
}
}
},
{ "$": "shift_state_selector",
"shiftedManual": {
"code": 352, "label": "Š", "type": "numeric", "popup": {
"main": { "code": 51, "label": "3" }
}
},
"default": {
"code": 353, "label": "š", "type": "numeric", "popup": {
"main": { "code": 51, "label": "3" }
}
}
},
{ "$": "shift_state_selector",
"shiftedManual": {
"code": 268, "label": "Č", "type": "numeric", "popup": {
"main": { "code": 52, "label": "4" }
}
},
"default": {
"code": 269, "label": "č", "type": "numeric", "popup": {
"main": { "code": 52, "label": "4" }
}
}
},
{ "$": "shift_state_selector",
"shiftedManual": {
"code": 344, "label": "Ř", "type": "numeric", "popup": {
"main": { "code": 53, "label": "5" }
}
},
"default": {
"code": 345, "label": "ř", "type": "numeric", "popup": {
"main": { "code": 53, "label": "5" }
}
}
},
{ "$": "shift_state_selector",
"shiftedManual": {
"code": 381, "label": "Ž", "type": "numeric", "popup": {
"main": { "code": 54, "label": "6" }
}
},
"default": {
"code": 382, "label": "ž", "type": "numeric", "popup": {
"main": { "code": 54, "label": "6" }
}
}
},
{ "$": "shift_state_selector",
"shiftedManual": {
"code": 221, "label": "Ý", "type": "numeric", "popup": {
"main": { "code": 55, "label": "7" }
}
},
"default": {
"code": 253, "label": "ý", "type": "numeric", "popup": {
"main": { "code": 55, "label": "7" }
}
}
},
{ "$": "shift_state_selector",
"shiftedManual": {
"code": 193, "label": "Á", "type": "numeric", "popup": {
"main": { "code": 56, "label": "8" }
}
},
"default": {
"code": 225, "label": "á", "type": "numeric", "popup": {
"main": { "code": 56, "label": "8" }
}
}
},
{ "$": "shift_state_selector",
"shiftedManual": {
"code": 205, "label": "Í", "type": "numeric", "popup": {
"main": { "code": 57, "label": "9" }
}
},
"default": {
"code": 237, "label": "í", "type": "numeric", "popup": {
"main": { "code": 57, "label": "9" }
}
}
},
{ "$": "shift_state_selector",
"shiftedManual": {
"code": 201, "label": "É", "type": "numeric", "popup": {
"main": { "code": 48, "label": "0" }
}
},
"default": {
"code": 233, "label": "é", "type": "numeric", "popup": {
"main": { "code": 48, "label": "0" }
}
}
}
]
]

View File

@@ -23,17 +23,13 @@
{ "code": 45, "label": "-", "popup": {
"main": { "code": 95, "label": "_" },
"relevant": [
{ "code": 8315, "label": "⁻" },
{ "code": 8212, "label": "—" },
{ "code": 8211, "label": "" },
{ "code": 183, "label": "·" }
]
} },
{ "code": 43, "label": "+", "popup": {
"main": { "code": 177, "label": "±" },
"relevant": [
{ "code": 8314, "label": "⁺" }
]
"main": { "code": 177, "label": "±" }
} },
{ "$": "layout_direction_selector",
"ltr": { "code": 40, "label": "(", "popup": {

View File

@@ -70,9 +70,8 @@
]
},
"~right": {
"main": { "code": 1567, "label": "؟" },
"main": { "code": 1611, "label": "ً" },
"relevant": [
{ "code": 1611, "label": "ً" },
{ "code": 1622, "label": "ٖ" },
{ "code": 1648, "label": "ٰ" },
{ "code": 1619, "label": "ٓ" },

View File

@@ -34,7 +34,9 @@
]
},
"h": {
"main": { "$": "auto_text_key", "code": 7717, "label": "ḥ" }
"relevant": [
{ "$": "auto_text_key", "code": 7717, "label": "ḥ" }
]
},
"i": {
"main": { "$": "auto_text_key", "code": 237, "label": "í" },
@@ -47,11 +49,13 @@
]
},
"l": {
"main": { "$": "auto_text_key", "code": 7735, "label": "ḷ" }
"relevant": [
{ "$": "auto_text_key", "code": 7735, "label": "ḷ" }
]
},
"n": {
"main": { "$": "auto_text_key", "code": 241, "label": "ñ" },
"relevant": [
{ "$": "auto_text_key", "code": 241, "label": "ñ" },
{ "$": "auto_text_key", "code": 324, "label": "ń" }
]
},
@@ -69,7 +73,9 @@
]
},
"r": {
"main": { "$": "auto_text_key", "code": 691, "label": "ʳ" }
"relevant": [
{ "$": "auto_text_key", "code": 691, "label": "ʳ" }
]
},
"s": {
"relevant": [
@@ -106,4 +112,4 @@
}
}
}

View File

@@ -23,10 +23,8 @@ import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import android.util.Log
import androidx.core.os.UserManagerCompat
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.clipboard.ClipboardManager
import dev.patrickgold.florisboard.ime.core.SubtypeManager
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
@@ -42,11 +40,7 @@ import dev.patrickgold.florisboard.lib.devtools.Flog
import dev.patrickgold.florisboard.lib.devtools.LogTopic
import dev.patrickgold.florisboard.lib.devtools.flogError
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
import dev.patrickgold.jetpref.datastore.runtime.initAndroid
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import dev.patrickgold.jetpref.datastore.JetPref
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
import org.florisboard.lib.kotlin.tryOrNull
import org.florisboard.libnative.dummyAdd
@@ -69,9 +63,8 @@ class FlorisApplication : Application() {
}
}
private val prefs by florisPreferenceModel()
private val mainHandler by lazy { Handler(mainLooper) }
private val scope = CoroutineScope(Dispatchers.Default)
val preferenceStoreLoaded = MutableStateFlow(false)
val cacheManager = lazy { CacheManager(this) }
val clipboardManager = lazy { ClipboardManager(this) }
@@ -87,6 +80,7 @@ class FlorisApplication : Application() {
super.onCreate()
FlorisApplicationReference = WeakReference(this)
try {
JetPref.configure(saveIntervalMs = 500)
Flog.install(
context = this,
isFloggingEnabled = BuildConfig.DEBUG,
@@ -114,14 +108,7 @@ class FlorisApplication : Application() {
fun init() {
cacheDir?.deleteContentsRecursively()
scope.launch {
val result = FlorisPreferenceStore.initAndroid(
context = this@FlorisApplication,
datastoreName = FlorisPreferenceModel.NAME,
)
Log.i("PREFS", result.toString())
preferenceStoreLoaded.value = true
}
prefs.initializeBlocking(this)
extensionManager.value.init()
clipboardManager.value.initializeForContext(this)
DictionaryManager.init(this)

View File

@@ -75,8 +75,8 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.WindowCompat
import androidx.lifecycle.lifecycleScope
import dev.patrickgold.florisboard.app.FlorisAppActivity
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.devtools.DevtoolsOverlay
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.ImeUiMode
import dev.patrickgold.florisboard.ime.clipboard.ClipboardInputLayout
import dev.patrickgold.florisboard.ime.core.SelectSubtypePanel
@@ -102,6 +102,7 @@ import dev.patrickgold.florisboard.ime.text.TextInputLayout
import dev.patrickgold.florisboard.ime.theme.FlorisImeTheme
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.ime.theme.WallpaperChangeReceiver
import dev.patrickgold.florisboard.lib.compose.ProvideLocalizedResources
import dev.patrickgold.florisboard.lib.compose.SystemUiIme
import dev.patrickgold.florisboard.lib.devtools.LogTopic
import dev.patrickgold.florisboard.lib.devtools.flogError
@@ -113,19 +114,16 @@ import dev.patrickgold.florisboard.lib.util.debugSummarize
import dev.patrickgold.florisboard.lib.util.launchActivity
import dev.patrickgold.jetpref.datastore.model.observeAsState
import java.lang.ref.WeakReference
import kotlinx.coroutines.flow.update
import org.florisboard.lib.android.AndroidInternalR
import org.florisboard.lib.android.AndroidVersion
import org.florisboard.lib.android.isOrientationLandscape
import org.florisboard.lib.android.isOrientationPortrait
import org.florisboard.lib.android.showShortToastSync
import org.florisboard.lib.android.showShortToast
import org.florisboard.lib.android.systemServiceOrNull
import org.florisboard.lib.compose.ProvideLocalizedResources
import org.florisboard.lib.kotlin.collectIn
import org.florisboard.lib.kotlin.collectLatestIn
import org.florisboard.lib.snygg.ui.SnyggBox
import org.florisboard.lib.snygg.ui.SnyggButton
import org.florisboard.lib.snygg.ui.SnyggRow
import org.florisboard.lib.snygg.ui.SnyggSurfaceView
import org.florisboard.lib.snygg.ui.SnyggText
import org.florisboard.lib.snygg.ui.rememberSnyggThemeQuery
@@ -248,12 +246,12 @@ class FlorisImeService : LifecycleInputMethodService() {
}
}
}
ims.showShortToastSync("Failed to find voice IME, do you have one installed?")
ims.showShortToast("Failed to find voice IME, do you have one installed?")
return false
}
}
private val prefs by FlorisPreferenceStore
private val prefs by florisPreferenceModel()
private val editorInstance by editorInstance()
private val keyboardManager by keyboardManager()
private val nlpManager by nlpManager()
@@ -279,21 +277,21 @@ class FlorisImeService : LifecycleInputMethodService() {
super.onCreate()
FlorisImeServiceReference = WeakReference(this)
WindowCompat.setDecorFitsSystemWindows(window.window!!, false)
subtypeManager.activeSubtypeFlow.collectIn(lifecycleScope) { subtype ->
subtypeManager.activeSubtypeFlow.collectLatestIn(lifecycleScope) { subtype ->
val config = Configuration(resources.configuration)
if (prefs.localization.displayKeyboardLabelsInSubtypeLanguage.get()) {
config.setLocale(subtype.primaryLocale.base)
}
resourcesContext = createConfigurationContext(config)
}
prefs.localization.displayKeyboardLabelsInSubtypeLanguage.asFlow().collectIn(lifecycleScope) { shouldSync ->
prefs.localization.displayKeyboardLabelsInSubtypeLanguage.observeForever { shouldSync ->
val config = Configuration(resources.configuration)
if (shouldSync) {
config.setLocale(subtypeManager.activeSubtype.primaryLocale.base)
}
resourcesContext = createConfigurationContext(config)
}
prefs.physicalKeyboard.showOnScreenKeyboard.asFlow().collectIn(lifecycleScope) {
prefs.physicalKeyboard.showOnScreenKeyboard.observeForever {
updateInputViewShown()
}
@Suppress("DEPRECATION") // We do not retrieve the wallpaper but only listen to changes
@@ -334,11 +332,6 @@ class FlorisImeService : LifecycleInputMethodService() {
return defaultExtractView
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
themeManager.configurationChangeCounter.update { it + 1 }
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(wallpaperChangeReceiver)
@@ -415,6 +408,7 @@ class FlorisImeService : LifecycleInputMethodService() {
flogInfo(LogTopic.IMS_EVENTS)
}
isWindowShown = true
themeManager.updateActiveTheme()
inputFeedbackController.updateSystemPrefsState()
}
@@ -578,10 +572,7 @@ class FlorisImeService : LifecycleInputMethodService() {
@Composable
private fun ImeUiWrapper() {
ProvideLocalizedResources(
resourcesContext,
appName = R.string.app_name,
) {
ProvideLocalizedResources(resourcesContext) {
ProvideKeyboardRowBaseHeight {
CompositionLocalProvider(LocalInputFeedbackController provides inputFeedbackController) {
FlorisImeTheme {
@@ -626,19 +617,9 @@ class FlorisImeService : LifecycleInputMethodService() {
clickAndSemanticsModifier = Modifier
// Do not remove below line or touch input may get stuck
.pointerInteropFilter { false },
supportsBackgroundImage = !AndroidVersion.ATLEAST_API30_R,
supportsBackgroundImage = true,
allowClip = false,
) {
// The SurfaceView is used to render the background image under inline-autofill chips. These are only
// available on Android >=11, and SurfaceView causes trouble on Android 8/9, thus we render the image
// in the SurfaceView for Android >=11, and in the Compose View Tree for Android <=10.
if (AndroidVersion.ATLEAST_API30_R) {
SnyggSurfaceView(
elementName = FlorisImeUi.Window.elementName,
attributes = attributes,
modifier = Modifier.matchParentSize(),
)
}
val configuration = LocalConfiguration.current
val bottomOffset by if (configuration.isOrientationPortrait()) {
prefs.keyboard.bottomOffsetPortrait
@@ -690,13 +671,10 @@ class FlorisImeService : LifecycleInputMethodService() {
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
return keyboardManager.onHardwareKeyDown(keyCode, event) || super.onKeyDown(keyCode, event)
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean =
if (keyboardManager.onHardwareKeyDown(keyCode, event)) true
else super.onKeyDown(keyCode, event)
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
return keyboardManager.onHardwareKeyUp(keyCode, event) || super.onKeyUp(keyCode, event)
}
private inner class ComposeInputView : AbstractComposeView(this) {
init {
@@ -731,11 +709,7 @@ class FlorisImeService : LifecycleInputMethodService() {
val keyboardManager by context.keyboardManager()
val state by keyboardManager.activeState.collectAsState()
ProvideLocalizedResources(
resourcesContext,
appName = R.string.app_name,
forceLayoutDirection = LayoutDirection.Ltr,
) {
ProvideLocalizedResources(resourcesContext, forceLayoutDirection = LayoutDirection.Ltr) {
FlorisImeTheme {
BottomSheetHostUi(
isShowing = state.isBottomSheetShowing() || state.isSubtypeSelectionShowing(),
@@ -787,11 +761,7 @@ class FlorisImeService : LifecycleInputMethodService() {
@Composable
fun Content() {
ProvideLocalizedResources(
resourcesContext,
appName = R.string.app_name,
forceLayoutDirection = LayoutDirection.Ltr,
) {
ProvideLocalizedResources(resourcesContext, forceLayoutDirection = LayoutDirection.Ltr) {
FlorisImeTheme {
val activeEditorInfo by editorInstance.activeInfoFlow.collectAsState()
SnyggBox(FlorisImeUi.ExtractedLandscapeInputLayout.elementName) {

View File

@@ -20,7 +20,7 @@ import android.service.textservice.SpellCheckerService
import android.view.textservice.SentenceSuggestionsInfo
import android.view.textservice.SuggestionsInfo
import android.view.textservice.TextInfo
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.core.Subtype
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
import dev.patrickgold.florisboard.ime.nlp.SpellingLanguageMode
@@ -33,7 +33,7 @@ import kotlinx.coroutines.runBlocking
import org.florisboard.lib.kotlin.map
class FlorisSpellCheckerService : SpellCheckerService() {
private val prefs by FlorisPreferenceStore
private val prefs by florisPreferenceModel()
private val dictionaryManager get() = DictionaryManager.default()
private val nlpManager by nlpManager()
private val subtypeManager by subtypeManager()

View File

@@ -20,7 +20,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import dev.patrickgold.florisboard.app.settings.theme.ColorPreferenceSerializer
import dev.patrickgold.florisboard.app.settings.theme.DisplayKbdAfterDialogs
import dev.patrickgold.florisboard.app.settings.theme.SnyggLevel
import dev.patrickgold.florisboard.app.setup.NotificationPermissionState
@@ -54,11 +53,11 @@ import dev.patrickgold.florisboard.ime.text.key.UtilityKeyAction
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import dev.patrickgold.florisboard.ime.theme.ThemeMode
import dev.patrickgold.florisboard.ime.theme.extCoreTheme
import dev.patrickgold.florisboard.lib.compose.ColorPreferenceSerializer
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
import dev.patrickgold.florisboard.lib.observeAsTransformingState
import dev.patrickgold.florisboard.lib.util.VersionName
import dev.patrickgold.jetpref.datastore.annotations.Preferences
import dev.patrickgold.jetpref.datastore.jetprefDataStoreOf
import dev.patrickgold.jetpref.datastore.JetPref
import dev.patrickgold.jetpref.datastore.model.LocalTime
import dev.patrickgold.jetpref.datastore.model.PreferenceData
import dev.patrickgold.jetpref.datastore.model.PreferenceMigrationEntry
@@ -70,14 +69,9 @@ import org.florisboard.lib.android.AndroidVersion
import org.florisboard.lib.android.isOrientationPortrait
import org.florisboard.lib.color.DEFAULT_GREEN
val FlorisPreferenceStore = jetprefDataStoreOf(FlorisPreferenceModel::class)
@Preferences
abstract class FlorisPreferenceModel : PreferenceModel() {
companion object {
const val NAME = "florisboard-app-prefs"
}
fun florisPreferenceModel() = JetPref.getOrCreatePreferenceModel(AppPrefs::class, ::AppPrefs)
class AppPrefs : PreferenceModel("florisboard-app-prefs") {
val clipboard = Clipboard()
inner class Clipboard {
val useInternalClipboard = boolean(
@@ -764,11 +758,11 @@ abstract class FlorisPreferenceModel : PreferenceModel() {
},
serializer = ColorPreferenceSerializer,
)
val sunriseTime = localTime(
val sunriseTime = time(
key = "theme__sunrise_time",
default = LocalTime(6, 0),
)
val sunsetTime = localTime(
val sunsetTime = time(
key = "theme__sunset_time",
default = LocalTime(18, 0),
)
@@ -860,11 +854,6 @@ abstract class FlorisPreferenceModel : PreferenceModel() {
dynamicActions = newArrangement.dynamicActions.plus(QuickAction.InsertKey(TextKeyData.LANGUAGE_SWITCH))
)
}
if (QuickAction.InsertKey(TextKeyData.FORWARD_DELETE) !in newArrangement) {
newArrangement = newArrangement.copy(
dynamicActions = newArrangement.dynamicActions.plus(QuickAction.InsertKey(TextKeyData.FORWARD_DELETE))
)
}
val json = QuickActionJsonConfig.encodeToString(newArrangement.distinct())
entry.transform(rawValue = json)
}

View File

@@ -42,10 +42,10 @@ import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
import dev.patrickgold.florisboard.ime.text.key.KeyHintMode
import dev.patrickgold.florisboard.ime.text.key.UtilityKeyAction
import dev.patrickgold.florisboard.ime.theme.ThemeMode
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.ListPreferenceEntry
import dev.patrickgold.jetpref.datastore.ui.listPrefEntries
import dev.patrickgold.jetpref.material.ui.ColorRepresentation
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.kotlin.curlyFormat
import kotlin.reflect.KClass

View File

@@ -39,30 +39,26 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.apptheme.FlorisAppTheme
import dev.patrickgold.florisboard.app.ext.ExtensionImportScreenType
import dev.patrickgold.florisboard.app.setup.NotificationPermissionState
import dev.patrickgold.florisboard.appContext
import dev.patrickgold.florisboard.cacheManager
import dev.patrickgold.florisboard.lib.FlorisLocale
import dev.patrickgold.florisboard.lib.compose.LocalPreviewFieldController
import dev.patrickgold.florisboard.lib.compose.PreviewKeyboardField
import dev.patrickgold.florisboard.lib.compose.ProvideLocalizedResources
import dev.patrickgold.florisboard.lib.compose.conditional
import dev.patrickgold.florisboard.lib.compose.rememberPreviewFieldController
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.util.AppVersionUtils
import dev.patrickgold.jetpref.datastore.model.observeAsState
import dev.patrickgold.jetpref.datastore.ui.ProvideDefaultDialogPrefStrings
import org.florisboard.lib.android.AndroidVersion
import org.florisboard.lib.android.hideAppIcon
import org.florisboard.lib.android.showAppIcon
import org.florisboard.lib.compose.ProvideLocalizedResources
import org.florisboard.lib.compose.conditional
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.kotlin.collectIn
import java.util.concurrent.atomic.AtomicBoolean
enum class AppTheme(val id: String) {
AUTO("auto"),
@@ -77,8 +73,7 @@ val LocalNavController = staticCompositionLocalOf<NavController> {
}
class FlorisAppActivity : ComponentActivity() {
private val prefs by FlorisPreferenceStore
private val appContext by appContext()
private val prefs by florisPreferenceModel()
private val cacheManager by cacheManager()
private var appTheme by mutableStateOf(AppTheme.AUTO)
private var showAppIcon = true
@@ -88,43 +83,40 @@ class FlorisAppActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Splash screen should be installed before calling super.onCreate()
installSplashScreen().apply {
setKeepOnScreenCondition { !appContext.preferenceStoreLoaded.value }
setKeepOnScreenCondition { !prefs.datastoreReadyStatus.get() }
}
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
prefs.other.settingsTheme.asFlow().collectIn(lifecycleScope) {
prefs.other.settingsTheme.observe(this) {
appTheme = it
}
prefs.other.settingsLanguage.asFlow().collectIn(lifecycleScope) {
prefs.other.settingsLanguage.observe(this) {
val config = Configuration(resources.configuration)
val locale = if (it == "auto") FlorisLocale.default() else FlorisLocale.fromTag(it)
config.setLocale(locale.base)
resourcesContext = createConfigurationContext(config)
}
if (AndroidVersion.ATMOST_API28_P) {
prefs.other.showAppIcon.asFlow().collectIn(lifecycleScope) {
prefs.other.showAppIcon.observe(this) {
showAppIcon = it
}
}
//Check if android 13+ is running and the NotificationPermission is not set
if (AndroidVersion.ATLEAST_API33_T &&
prefs.internal.notificationPermissionState.get() == NotificationPermissionState.NOT_SET
) {
// update pref value to show the setup screen again again
prefs.internal.isImeSetUp.set(false)
}
// We defer the setContent call until the datastore model is loaded, until then the splash screen stays drawn
val isModelLoaded = AtomicBoolean(false)
appContext.preferenceStoreLoaded.collectIn(lifecycleScope) { loaded ->
if (!loaded || isModelLoaded.getAndSet(true)) return@collectIn
// Check if android 13+ is running and the NotificationPermission is not set
if (AndroidVersion.ATLEAST_API33_T &&
prefs.internal.notificationPermissionState.get() == NotificationPermissionState.NOT_SET
) {
// update pref value to show the setup screen again
prefs.internal.isImeSetUp.set(false)
}
prefs.datastoreReadyStatus.observe(this) { isModelLoaded ->
if (!isModelLoaded) return@observe
AppVersionUtils.updateVersionOnInstallAndLastUse(this, prefs)
setContent {
ProvideLocalizedResources(
resourcesContext,
appName = R.string.app_name,
) {
ProvideLocalizedResources(resourcesContext) {
FlorisAppTheme(theme = appTheme) {
Surface(color = MaterialTheme.colorScheme.background) {
AppContent()
@@ -197,7 +189,7 @@ class FlorisAppActivity : ComponentActivity() {
Routes.AppNavHost(
modifier = Modifier.weight(1.0f),
navController = navController,
startDestination = if (isImeSetUp) Routes.Settings.Home::class else Routes.Setup.Screen::class,
startDestination = if (isImeSetUp) Routes.Settings.Home else Routes.Setup.Screen,
)
PreviewKeyboardField(previewFieldController)
}

View File

@@ -17,17 +17,12 @@
package dev.patrickgold.florisboard.app
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleOut
import androidx.compose.animation.slideIn
import androidx.compose.animation.slideOut
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.unit.IntOffset
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavGraphBuilder
@@ -35,7 +30,6 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.navDeepLink
import androidx.navigation.toRoute
import dev.patrickgold.florisboard.app.devtools.AndroidLocalesScreen
import dev.patrickgold.florisboard.app.devtools.AndroidSettingsScreen
import dev.patrickgold.florisboard.app.devtools.DevtoolsScreen
@@ -76,185 +70,113 @@ import dev.patrickgold.florisboard.app.settings.theme.ThemeManagerScreenAction
import dev.patrickgold.florisboard.app.settings.theme.ThemeScreen
import dev.patrickgold.florisboard.app.settings.typing.TypingScreen
import dev.patrickgold.florisboard.app.setup.SetupScreen
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlin.reflect.KClass
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Deeplink(val path: String)
inline fun <reified T : Any> NavGraphBuilder.composableWithDeepLink(
kClass: KClass<T>,
noinline content: @Composable (AnimatedContentScope.(NavBackStackEntry) -> Unit),
) {
val deeplink = requireNotNull(kClass.annotations.firstOrNull { it is Deeplink } as? Deeplink) {
"faulty class: $kClass with annotations ${kClass.annotations}"
}
composable<T>(
deepLinks = listOf(navDeepLink<T>(basePath = "ui://florisboard/${deeplink.path}")),
content = content,
)
}
import org.florisboard.lib.kotlin.curlyFormat
@Suppress("FunctionName", "ConstPropertyName")
object Routes {
object Setup {
@Serializable
object Screen
const val Screen = "setup"
}
object Settings {
@Serializable
@Deeplink("settings/home")
object Home
const val Home = "settings"
@Serializable
@Deeplink("settings/localization")
object Localization
const val Localization = "settings/localization"
const val SelectLocale = "settings/localization/select-locale"
const val LanguagePackManager = "settings/localization/language-pack-manage/{action}"
fun LanguagePackManager(action: LanguagePackManagerScreenAction) =
LanguagePackManager.curlyFormat("action" to action.id)
const val SubtypeAdd = "settings/localization/subtype/add"
const val SubtypeEdit = "settings/localization/subtype/edit/{id}"
fun SubtypeEdit(id: Long) = SubtypeEdit.curlyFormat("id" to id)
@Serializable
@Deeplink("settings/localization/select-locale")
object SelectLocale
const val Theme = "settings/theme"
const val ThemeManager = "settings/theme/manage/{action}"
fun ThemeManager(action: ThemeManagerScreenAction) = ThemeManager.curlyFormat("action" to action.id)
@Serializable
@Deeplink("settings/localization/language-pack-manage")
data class LanguagePackManager(val action: LanguagePackManagerScreenAction)
const val Keyboard = "settings/keyboard"
const val InputFeedback = "settings/keyboard/input-feedback"
@Serializable
@Deeplink("settings/localization/subtype/add")
object SubtypeAdd
const val Smartbar = "settings/smartbar"
@Serializable
@Deeplink("settings/localization/subtype/edit")
data class SubtypeEdit(val id: Long)
const val Typing = "settings/typing"
@Serializable
@Deeplink("settings/theme")
object Theme
const val Dictionary = "settings/dictionary"
const val UserDictionary = "settings/dictionary/user-dictionary/{type}"
fun UserDictionary(type: UserDictionaryType) = UserDictionary.curlyFormat("type" to type.id)
@Serializable
@Deeplink("settings/theme/manage")
data class ThemeManager(val action: ThemeManagerScreenAction)
const val Gestures = "settings/gestures"
@Serializable
@Deeplink("settings/keyboard")
object Keyboard
const val Clipboard = "settings/clipboard"
@Serializable
@Deeplink("settings/keyboard/input-feedback")
object InputFeedback
const val Media = "settings/media"
@Serializable
@Deeplink("settings/smartbar")
object Smartbar
const val Other = "settings/other"
const val PhysicalKeyboard = "settings/other/physical-keyboard"
const val Backup = "settings/other/backup"
const val Restore = "settings/other/restore"
@Serializable
@Deeplink("settings/typing")
object Typing
@Serializable
@Deeplink("settings/dictionary")
object Dictionary
@Serializable
@Deeplink("settings/dictionary/user-dictionary")
data class UserDictionary(val type: UserDictionaryType)
@Serializable
@Deeplink("settings/gestures")
object Gestures
@Serializable
@Deeplink("settings/clipboard")
object Clipboard
@Serializable
@Deeplink("settings/media")
object Media
@Serializable
@Deeplink("settings/other")
object Other
@Serializable
@Deeplink("settings/other/physical-keyboard")
object PhysicalKeyboard
@Serializable
@Deeplink("settings/other/backup")
object Backup
@Serializable
@Deeplink("settings/other/restore")
object Restore
@Serializable
@Deeplink("settings/about")
object About
@Serializable
@Deeplink("settings/about/project-license")
object ProjectLicense
@Serializable
@Deeplink("settings/about/third-party-licenses")
object ThirdPartyLicenses
const val About = "settings/about"
const val ProjectLicense = "settings/about/project-license"
const val ThirdPartyLicenses = "settings/about/third-party-licenses"
}
object Devtools {
@Serializable
@Deeplink("devtools")
object Home
const val Home = "devtools"
@Serializable
@Deeplink("devtools/android/locales")
object AndroidLocales
const val AndroidLocales = "devtools/android/locales"
const val AndroidSettings = "devtools/android/settings/{name}"
fun AndroidSettings(name: String) = AndroidSettings.curlyFormat("name" to name)
@Serializable
@Deeplink("devtools/android/settings")
data class AndroidSettings(val name: String)
@Serializable
@Deeplink("export-debug-log")
object ExportDebugLog
const val ExportDebugLog = "export-debug-log"
}
object Ext {
@Serializable
@Deeplink("ext")
object Home
const val Home = "ext"
@Serializable
@Deeplink("ext/list")
data class List(val type: ExtensionListScreenType, val showUpdate: Boolean? = null)
const val List = "ext/list/{type}?showUpdate={showUpdate}"
fun List(
type: ExtensionListScreenType,
showUpdate: Boolean
) = List.curlyFormat("type" to type.id, "showUpdate" to showUpdate)
@Serializable
@Deeplink("ext/edit")
data class Edit(val id: String, @SerialName("create") val serialType: String? = null)
const val Edit = "ext/edit/{id}?create={serial_type}"
fun Edit(id: String, serialType: String? = null): String {
return Edit.curlyFormat("id" to id, "serial_type" to (serialType ?: ""))
}
@Serializable
@Deeplink("ext/export")
data class Export(val id: String)
const val Export = "ext/export/{id}"
fun Export(id: String) = Export.curlyFormat("id" to id)
@Serializable
@Deeplink("ext/import")
data class Import(val type: ExtensionImportScreenType, val uuid: String? = null)
const val Import = "ext/import/{type}?uuid={uuid}"
fun Import(
type: ExtensionImportScreenType,
uuid: String?,
) = Import.curlyFormat("type" to type.id, "uuid" to uuid.toString())
@Serializable
@Deeplink("ext/view")
data class View(val id: String)
const val View = "ext/view/{id}"
fun View(id: String) = View.curlyFormat("id" to id)
@Serializable
@Deeplink("ext/check-updates")
object CheckUpdates
const val CheckUpdates = "ext/check-updates"
}
@Composable
fun AppNavHost(
modifier: Modifier,
navController: NavHostController,
startDestination: KClass<*>,
startDestination: String,
) {
fun NavGraphBuilder.composableWithDeepLink(
route: String,
content: @Composable (AnimatedContentScope.(NavBackStackEntry) -> Unit),
) {
composable(
route = route,
deepLinks = listOf(navDeepLink { uriPattern = "ui://florisboard/$route" }),
content = content,
)
}
NavHost(
modifier = modifier,
navController = navController,
@@ -265,103 +187,109 @@ object Routes {
exitTransition = {
slideOut { IntOffset(-it.width, 0) } + fadeOut()
},
popEnterTransition = { EnterTransition.None },
popExitTransition = {
scaleOut(
targetScale = 0.85F,
transformOrigin = TransformOrigin(pivotFractionX = 0.8f, pivotFractionY = 0.5f)
) + fadeOut(spring(stiffness = Spring.StiffnessMedium))
popEnterTransition = {
slideIn { IntOffset(-it.width, 0) } + fadeIn()
},
popExitTransition = {
slideOut { IntOffset(it.width, 0) } + fadeOut()
}
) {
composable<Setup.Screen> { SetupScreen() }
composable(Setup.Screen) { SetupScreen() }
composableWithDeepLink(Settings.Home::class) { HomeScreen() }
composableWithDeepLink(Settings.Home) { HomeScreen() }
composableWithDeepLink(Settings.Localization::class) { LocalizationScreen() }
composableWithDeepLink(Settings.SelectLocale::class) { SelectLocaleScreen() }
composableWithDeepLink(Settings.LanguagePackManager::class) { navBackStack ->
val payload = navBackStack.toRoute<Settings.LanguagePackManager>()
LanguagePackManagerScreen(payload.action)
composableWithDeepLink(Settings.Localization) { LocalizationScreen() }
composableWithDeepLink(Settings.SelectLocale) { SelectLocaleScreen() }
composableWithDeepLink(Settings.LanguagePackManager) { navBackStack ->
val action = navBackStack.arguments?.getString("action")?.let { actionId ->
LanguagePackManagerScreenAction.entries.firstOrNull { it.id == actionId }
}
LanguagePackManagerScreen(action)
}
composableWithDeepLink(Settings.SubtypeAdd::class) { SubtypeEditorScreen(null) }
composableWithDeepLink(Settings.SubtypeEdit::class) { navBackStack ->
val payload = navBackStack.toRoute<Settings.SubtypeEdit>()
SubtypeEditorScreen(payload.id)
composableWithDeepLink(Settings.SubtypeAdd) { SubtypeEditorScreen(null) }
composableWithDeepLink(Settings.SubtypeEdit) { navBackStack ->
val id = navBackStack.arguments?.getString("id")?.toLongOrNull()
SubtypeEditorScreen(id)
}
composableWithDeepLink(Settings.Theme::class) { ThemeScreen() }
composableWithDeepLink(Settings.ThemeManager::class) { navBackStack ->
val payload = navBackStack.toRoute<Settings.ThemeManager>()
ThemeManagerScreen(payload.action)
composableWithDeepLink(Settings.Theme) { ThemeScreen() }
composableWithDeepLink(Settings.ThemeManager) { navBackStack ->
val action = navBackStack.arguments?.getString("action")?.let { actionId ->
ThemeManagerScreenAction.entries.firstOrNull { it.id == actionId }
}
ThemeManagerScreen(action)
}
composableWithDeepLink(Settings.Keyboard::class) { KeyboardScreen() }
composableWithDeepLink(Settings.InputFeedback::class) { InputFeedbackScreen() }
composableWithDeepLink(Settings.Keyboard) { KeyboardScreen() }
composableWithDeepLink(Settings.InputFeedback) { InputFeedbackScreen() }
composableWithDeepLink(Settings.Smartbar::class) { SmartbarScreen() }
composableWithDeepLink(Settings.Smartbar) { SmartbarScreen() }
composableWithDeepLink(Settings.Typing::class) { TypingScreen() }
composableWithDeepLink(Settings.Typing) { TypingScreen() }
composableWithDeepLink(Settings.Dictionary::class) { DictionaryScreen() }
composableWithDeepLink(Settings.UserDictionary::class) { navBackStack ->
val payload = navBackStack.toRoute<Settings.UserDictionary>()
UserDictionaryScreen(payload.type)
composableWithDeepLink(Settings.Dictionary) { DictionaryScreen() }
composableWithDeepLink(Settings.UserDictionary) { navBackStack ->
val type = navBackStack.arguments?.getString("type")?.let { typeId ->
UserDictionaryType.entries.firstOrNull { it.id == typeId }
}
UserDictionaryScreen(type!!)
}
composableWithDeepLink(Settings.Gestures::class) { GesturesScreen() }
composableWithDeepLink(Settings.Gestures) { GesturesScreen() }
composableWithDeepLink(Settings.Clipboard::class) { ClipboardScreen() }
composableWithDeepLink(Settings.Clipboard) { ClipboardScreen() }
composableWithDeepLink(Settings.Media::class) { MediaScreen() }
composableWithDeepLink(Settings.Media) { MediaScreen() }
composableWithDeepLink(Settings.Other::class) { OtherScreen() }
composableWithDeepLink(Settings.PhysicalKeyboard::class) { PhysicalKeyboardScreen() }
composableWithDeepLink(Settings.Backup::class) { BackupScreen() }
composableWithDeepLink(Settings.Restore::class) { RestoreScreen() }
composableWithDeepLink(Settings.Other) { OtherScreen() }
composableWithDeepLink(Settings.PhysicalKeyboard) { PhysicalKeyboardScreen() }
composableWithDeepLink(Settings.Backup) { BackupScreen() }
composableWithDeepLink(Settings.Restore) { RestoreScreen() }
composableWithDeepLink(Settings.About::class) { AboutScreen() }
composableWithDeepLink(Settings.ProjectLicense::class) { ProjectLicenseScreen() }
composableWithDeepLink(Settings.ThirdPartyLicenses::class) { ThirdPartyLicensesScreen() }
composableWithDeepLink(Settings.About) { AboutScreen() }
composableWithDeepLink(Settings.ProjectLicense) { ProjectLicenseScreen() }
composableWithDeepLink(Settings.ThirdPartyLicenses) { ThirdPartyLicensesScreen() }
composableWithDeepLink(Devtools.Home::class) { DevtoolsScreen() }
composableWithDeepLink(Devtools.AndroidLocales::class) { AndroidLocalesScreen() }
composableWithDeepLink(Devtools.AndroidSettings::class) { navBackStack ->
val payload = navBackStack.toRoute<Devtools.AndroidSettings>()
AndroidSettingsScreen(payload.name)
composableWithDeepLink(Devtools.Home) { DevtoolsScreen() }
composableWithDeepLink(Devtools.AndroidLocales) { AndroidLocalesScreen() }
composableWithDeepLink(Devtools.AndroidSettings) { navBackStack ->
val name = navBackStack.arguments?.getString("name")
AndroidSettingsScreen(name)
}
composableWithDeepLink(Devtools.ExportDebugLog::class) { ExportDebugLogScreen() }
composableWithDeepLink(Devtools.ExportDebugLog) { ExportDebugLogScreen() }
composableWithDeepLink(Ext.Home::class) { ExtensionHomeScreen() }
composableWithDeepLink(Ext.List::class) { navBackStack ->
val payload = navBackStack.toRoute<Ext.List>()
val showUpdate = payload.showUpdate != null && payload.showUpdate
ExtensionListScreen(payload.type, showUpdate)
composableWithDeepLink(Ext.Home) { ExtensionHomeScreen() }
composableWithDeepLink(Ext.List) { navBackStack ->
val type = navBackStack.arguments?.getString("type")?.let { typeId ->
ExtensionListScreenType.entries.firstOrNull { it.id == typeId }
} ?: error("unknown type")
val showUpdate = navBackStack.arguments?.getString("showUpdate")
ExtensionListScreen(type, showUpdate == "true")
}
composableWithDeepLink(Ext.Edit::class) { navBackStack ->
val payload = navBackStack.toRoute<Ext.Edit>()
val extensionId = payload.id
val serialType = payload.serialType
composableWithDeepLink(Ext.Edit) { navBackStack ->
val extensionId = navBackStack.arguments?.getString("id")
val serialType = navBackStack.arguments?.getString("serial_type")
ExtensionEditScreen(
id = extensionId,
id = extensionId.toString(),
createSerialType = serialType.takeIf { !it.isNullOrBlank() },
)
}
composableWithDeepLink(Ext.Export::class) { navBackStack ->
val payload = navBackStack.toRoute<Ext.Export>()
val extensionId = payload.id
ExtensionExportScreen(id = extensionId)
composableWithDeepLink(Ext.Export) { navBackStack ->
val extensionId = navBackStack.arguments?.getString("id")
ExtensionExportScreen(id = extensionId.toString())
}
composableWithDeepLink(Ext.Import::class) { navBackStack ->
val payload = navBackStack.toRoute<Ext.Import>()
val uuid = payload.uuid
ExtensionImportScreen(payload.type, uuid)
composableWithDeepLink(Ext.Import) { navBackStack ->
val type = navBackStack.arguments?.getString("type")?.let { typeId ->
ExtensionImportScreenType.entries.firstOrNull { it.id == typeId }
} ?: ExtensionImportScreenType.EXT_ANY
val uuid = navBackStack.arguments?.getString("uuid")?.takeIf { it != "null" }
ExtensionImportScreen(type, uuid)
}
composableWithDeepLink(Ext.View::class) { navBackStack ->
val payload = navBackStack.toRoute<Ext.View>()
val extensionId = payload.id
ExtensionViewScreen(id = extensionId)
composableWithDeepLink(Ext.View) { navBackStack ->
val extensionId = navBackStack.arguments?.getString("id")
ExtensionViewScreen(id = extensionId.toString())
}
composableWithDeepLink(Ext.CheckUpdates::class) {
composableWithDeepLink(Ext.CheckUpdates) {
CheckUpdatesScreen()
}
}

View File

@@ -29,8 +29,9 @@ 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.FlorisPreferenceStore
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(
@@ -78,7 +79,7 @@ fun getColorScheme(
context: Context,
theme: AppTheme,
): ColorScheme {
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val accentColor by prefs.other.accentColor.observeAsState()
val isDark = isSystemInDarkTheme()

View File

@@ -32,11 +32,11 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontFamily
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.model.observeAsState
import org.florisboard.lib.android.showLongToastSync
import org.florisboard.lib.compose.FlorisIconButton
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.android.showLongToast
import org.florisboard.lib.kotlin.io.subDir
import org.florisboard.lib.kotlin.io.subFile
import java.util.Locale
@@ -66,9 +66,9 @@ fun AndroidLocalesScreen() = FlorisScreen {
out.appendLine()
}
}
context.showLongToastSync("Exported available system locales to \"${txtFile.path}\"")
context.showLongToast("Exported available system locales to \"${txtFile.path}\"")
} catch (e: Exception) {
context.showLongToastSync(
context.showLongToast(
R.string.error__snackbar_message_template,
"error_message" to e.message.toString(),
)

View File

@@ -27,11 +27,11 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import dev.patrickgold.florisboard.R
import org.florisboard.lib.android.AndroidSettings
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.Preference
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
import org.florisboard.lib.android.AndroidSettings
import org.florisboard.lib.compose.stringRes
@Composable
fun AndroidSettingsScreen(name: String?) = FlorisScreen {

View File

@@ -29,6 +29,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -39,8 +40,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.appContext
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.clipboardManager
import dev.patrickgold.florisboard.editorInstance
import dev.patrickgold.florisboard.ime.keyboard.CachedLayout
@@ -53,10 +53,10 @@ import dev.patrickgold.florisboard.lib.observeAsNonNullState
import dev.patrickgold.florisboard.nlpManager
import dev.patrickgold.florisboard.themeManager
import dev.patrickgold.jetpref.datastore.model.observeAsState
import java.text.SimpleDateFormat
import java.util.*
import org.florisboard.lib.android.AndroidVersion
import org.florisboard.lib.snygg.SnyggMissingSchemaException
import java.text.SimpleDateFormat
import java.util.*
private val CardBackground = Color.Black.copy(0.6f)
private val DateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss", FlorisLocale.default().base)
@@ -64,8 +64,7 @@ private val DateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss", FlorisLocale.
@Composable
fun DevtoolsOverlay(modifier: Modifier = Modifier) {
val context = LocalContext.current
val prefs by FlorisPreferenceStore
val appContext by context.appContext()
val prefs by florisPreferenceModel()
val keyboardManager by context.keyboardManager()
val themeManager by context.themeManager()
@@ -74,10 +73,9 @@ fun DevtoolsOverlay(modifier: Modifier = Modifier) {
val showInputStateOverlay by prefs.devtools.showInputStateOverlay.observeAsState()
val showSpellingOverlay by prefs.devtools.showSpellingOverlay.observeAsState()
val showInlineAutofillOverlay by prefs.devtools.showInlineAutofillOverlay.observeAsState()
val prefsLoaded by appContext.preferenceStoreLoaded.collectAsState()
val debugLayoutResult by keyboardManager.layoutManager.debugLayoutComputationResultFlow.collectAsState()
val themeInfo by themeManager.activeThemeInfo.collectAsState()
val themeInfo by themeManager.activeThemeInfo.observeAsState()
CompositionLocalProvider(
LocalContentColor provides Color.White,
@@ -99,8 +97,8 @@ fun DevtoolsOverlay(modifier: Modifier = Modifier) {
if (devtoolsEnabled && showInlineAutofillOverlay && AndroidVersion.ATLEAST_API30_R) {
DevtoolsInlineAutofillOverlay()
}
val loadFailure = themeInfo.loadFailure
if (loadFailure != null && prefsLoaded) {
val loadFailure = themeInfo?.loadFailure
if (loadFailure != null) {
DevtoolsStylesheetFailedToLoadOverlay(loadFailure)
}
}
@@ -165,13 +163,13 @@ private fun DevtoolsLastLayoutComputationOverlay(debugLayoutResult: DebugLayoutC
return@DevtoolsOverlayBox
}
DevtoolsSubGroup(title = "main") {
PrintResult(debugLayoutResult.main)
PrintResult(debugLayoutResult!!.main)
}
DevtoolsSubGroup(title = "mod") {
PrintResult(debugLayoutResult.mod)
PrintResult(debugLayoutResult!!.mod)
}
DevtoolsSubGroup(title = "ext") {
PrintResult(debugLayoutResult.ext)
PrintResult(debugLayoutResult!!.ext)
}
}
}

View File

@@ -20,7 +20,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
@@ -29,18 +28,17 @@ import dev.patrickgold.florisboard.extensionManager
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
import dev.patrickgold.florisboard.ime.dictionary.FlorisUserDictionaryDatabase
import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionArrangement
import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionJsonConfig
import org.florisboard.lib.android.AndroidSettings
import org.florisboard.lib.android.showLongToast
import dev.patrickgold.florisboard.lib.compose.FlorisConfirmDeleteDialog
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.model.observeAsState
import dev.patrickgold.jetpref.datastore.ui.Preference
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
import org.florisboard.lib.android.AndroidSettings
import kotlinx.coroutines.launch
import org.florisboard.lib.android.AndroidVersion
import org.florisboard.lib.android.showLongToast
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.android.showLongToastSync
class DebugOnPurposeCrashException : Exception(
"Success! The app crashed purposely to display this beautiful screen we all love :)"
@@ -54,7 +52,6 @@ fun DevtoolsScreen() = FlorisScreen {
val context = LocalContext.current
val navController = LocalNavController.current
val extensionManager by context.extensionManager()
val scope = rememberCoroutineScope()
val (showDialog, setShowDialog) = remember { mutableStateOf(false) }
@@ -113,17 +110,15 @@ fun DevtoolsScreen() = FlorisScreen {
title = stringRes(R.string.devtools__reset_quick_actions_to_default__label),
summary = stringRes(R.string.devtools__reset_quick_actions_to_default__summary),
onClick = {
scope.launch {
prefs.smartbar.actionArrangement.set(QuickActionArrangement.Default)
}
context.showLongToastSync(R.string.devtools__reset_quick_actions_to_default__toast_success)
prefs.smartbar.actionArrangement.set(QuickActionArrangement.Default)
context.showLongToast(R.string.devtools__reset_quick_actions_to_default__toast_success)
},
enabledIf = { prefs.devtools.enabled isEqualTo true },
)
Preference(
title = stringRes(R.string.devtools__reset_flag__label, "flag_name" to "isImeSetUp"),
summary = stringRes(R.string.devtools__reset_flag_is_ime_set_up__summary),
onClick = { scope.launch { prefs.internal.isImeSetUp.set(false) } },
onClick = { prefs.internal.isImeSetUp.set(false) },
enabledIf = { prefs.devtools.enabled isEqualTo true },
)
Preference(
@@ -205,14 +200,14 @@ fun DevtoolsScreen() = FlorisScreen {
title = "keyboardExtensions",
summary = extensionManager.keyboardExtensions.internalModuleDir.absolutePath,
onClick = {
context.showLongToastSync(extensionManager.keyboardExtensions.internalModuleDir.absolutePath)
context.showLongToast(extensionManager.keyboardExtensions.internalModuleDir.absolutePath)
},
)
Preference(
title = "themes",
summary = extensionManager.themes.internalModuleDir.absolutePath,
onClick = {
context.showLongToastSync(extensionManager.themes.internalModuleDir.absolutePath)
context.showLongToast(extensionManager.themes.internalModuleDir.absolutePath)
},
)
}

View File

@@ -38,16 +38,15 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.sp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.clipboardManager
import dev.patrickgold.florisboard.lib.compose.FlorisButton
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.devtools.Devtools
import org.florisboard.lib.android.showShortToast
import org.florisboard.lib.compose.FlorisButton
import org.florisboard.lib.compose.florisHorizontalScroll
import org.florisboard.lib.compose.florisScrollbar
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.android.showShortToastSync
// TODO: This screen is just a quick thrown-together thing and needs further enhancing in the UI
@Composable
@@ -55,7 +54,7 @@ fun ExportDebugLogScreen() = FlorisScreen {
title = stringRes(R.string.devtools__debuglog__title)
scrollable = false
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val context = LocalContext.current
val clipboardManager by context.clipboardManager()
@@ -75,7 +74,7 @@ fun ExportDebugLogScreen() = FlorisScreen {
FlorisButton(
onClick = {
clipboardManager.addNewPlaintext(debugLog!!.joinToString("\n"))
context.showShortToastSync(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
context.showShortToast(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
},
modifier = Modifier,
text = stringRes(R.string.devtools__debuglog__copy_log),
@@ -84,7 +83,7 @@ fun ExportDebugLogScreen() = FlorisScreen {
FlorisButton(
onClick = {
clipboardManager.addNewPlaintext(formattedDebugLog!!.joinToString("\n"))
context.showShortToastSync(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
context.showShortToast(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
},
text = stringRes(R.string.devtools__debuglog__copy_for_github),
enabled = debugLog != null,

View File

@@ -35,13 +35,13 @@ import dev.patrickgold.florisboard.BuildConfig
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.FlorisTextButton
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.Extension
import dev.patrickgold.florisboard.lib.ext.generateUpdateUrl
import dev.patrickgold.florisboard.lib.util.launchUrl
import org.florisboard.lib.compose.FlorisOutlinedBox
import org.florisboard.lib.compose.FlorisTextButton
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.kotlin.curlyFormat
@Composable

View File

@@ -21,7 +21,7 @@ import androidx.compose.ui.platform.LocalContext
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.extensionManager
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import org.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.compose.stringRes
@Composable
fun CheckUpdatesScreen() = FlorisScreen {

View File

@@ -40,13 +40,13 @@ import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.ime.nlp.LanguagePackComponent
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.FlorisTextButton
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.ExtensionComponent
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
import dev.patrickgold.florisboard.lib.ext.ExtensionMeta
import org.florisboard.lib.compose.FlorisIconButton
import org.florisboard.lib.compose.FlorisOutlinedBox
import org.florisboard.lib.compose.FlorisTextButton
import org.florisboard.lib.compose.stringRes
@Composable
fun ExtensionComponentNoneFoundView() {

View File

@@ -48,7 +48,9 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.lib.cache.CacheManager
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.Preference
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
import dev.patrickgold.jetpref.material.ui.JetPrefTextField
@@ -57,11 +59,7 @@ import java.util.*
import org.florisboard.lib.android.query
import org.florisboard.lib.android.readToFile
import org.florisboard.lib.android.showLongToast
import org.florisboard.lib.android.showLongToastSync
import org.florisboard.lib.android.showShortToast
import org.florisboard.lib.android.showShortToastSync
import org.florisboard.lib.compose.FlorisIconButton
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.kotlin.io.parentDir
import org.florisboard.lib.kotlin.io.subDir
import org.florisboard.lib.kotlin.io.subFile
@@ -188,9 +186,9 @@ fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = Fl
allowOutsideDismissal = true,
onNeutral = {
if (file.delete()) {
context.showShortToastSync("Successfully deleted")
context.showShortToast("Successfully deleted")
} else {
context.showShortToastSync("Failed to delete")
context.showShortToast("Failed to delete")
}
dialogFile = null
version++
@@ -198,18 +196,18 @@ fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = Fl
onConfirm = {
val newFile = file.parentFile!!.subFile(fileNameInput).canonicalFile
if (newFile.parentFile != file.canonicalFile.parentFile) {
context.showLongToastSync("Invalid file name!")
context.showLongToast("Invalid file name!")
return@JetPrefAlertDialog
}
if (newFile.exists()) {
context.showShortToastSync("Filename already exists.")
context.showShortToast("Filename already exists.")
return@JetPrefAlertDialog
}
val success = file.renameTo(newFile)
if (success) {
context.showShortToastSync("Successfully renamed")
context.showShortToast("Successfully renamed")
} else {
context.showShortToastSync("Failed to rename the file.")
context.showShortToast("Failed to rename the file.")
}
dialogFile = null
version++
@@ -259,13 +257,13 @@ fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = Fl
dir.mkdirs()
val file = dir.subFile(fileName)
if (file.parentDir != workspace.extDir.subDir(dest)) {
context.showShortToastSync("Invalid file name")
context.showShortToast("Invalid file name")
} else if (file.exists()) {
context.showShortToastSync("File already exists")
context.showShortToast("File already exists")
} else {
val tempFile = result.first
if (!tempFile.renameTo(file)) {
context.showShortToastSync("Failed to rename file")
context.showShortToast("Failed to rename file")
tempFile.delete()
}
currentImportDest = null

View File

@@ -42,9 +42,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
@@ -62,9 +60,15 @@ import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponentImpl
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionEditor
import dev.patrickgold.florisboard.lib.ValidationResult
import dev.patrickgold.florisboard.lib.cache.CacheManager
import dev.patrickgold.florisboard.lib.compose.FlorisButtonBar
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
import dev.patrickgold.florisboard.lib.compose.FlorisInfoCard
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.FlorisUnsavedChangesDialog
import dev.patrickgold.florisboard.lib.compose.Validation
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.Extension
import dev.patrickgold.florisboard.lib.ext.ExtensionComponent
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
@@ -81,16 +85,11 @@ import dev.patrickgold.florisboard.lib.io.ZipUtils
import dev.patrickgold.florisboard.lib.rememberValidationResult
import dev.patrickgold.florisboard.themeManager
import dev.patrickgold.jetpref.datastore.ui.Preference
import dev.patrickgold.jetpref.datastore.ui.vectorResource
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
import dev.patrickgold.jetpref.material.ui.JetPrefTextField
import java.util.*
import org.florisboard.lib.compose.FlorisButtonBar
import org.florisboard.lib.compose.FlorisIconButton
import org.florisboard.lib.compose.FlorisInfoCard
import org.florisboard.lib.compose.FlorisOutlinedBox
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.android.showLongToastSync
import org.florisboard.lib.android.showLongToast
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
import org.florisboard.lib.kotlin.io.subDir
import org.florisboard.lib.kotlin.io.subFile
@@ -288,7 +287,7 @@ private fun EditScreen(
stylesheetFile.writeText(stylesheet)
}.onFailure {
// TODO: better error handling
context.showLongToastSync(it.message.toString())
context.showLongToast(it.message.toString())
return
}
} else {
@@ -353,7 +352,7 @@ private fun EditScreen(
)
Preference(
onClick = { workspace.currentAction = EditorAction.ManageFiles },
icon = ImageVector.vectorResource(R.drawable.ic_file_blank),
icon = vectorResource(R.drawable.ic_file_blank),
title = stringRes(R.string.ext__editor__files__title),
)
}
@@ -628,7 +627,7 @@ private fun <T : ExtensionComponent> CreateComponentScreen(
for (theme in editor.themes) {
put(ExtensionComponentName(extId, theme.id), theme)
}
for ((componentName, theme) in themeManager.indexedThemeConfigs.value.first) {
for ((componentName, theme) in themeManager.indexedThemeConfigs.value ?: emptyMap()) {
if (componentName.extensionId != extId) {
put(componentName, theme)
}
@@ -666,7 +665,7 @@ private fun <T : ExtensionComponent> CreateComponentScreen(
when (createFrom) {
CreateFrom.EMPTY -> {
if (editor.themes.any { it.id == newId.trim() }) {
context.showLongToastSync("A theme with this ID already exists!")
context.showLongToast("A theme with this ID already exists!")
} else {
val componentEditor = ThemeExtensionComponentEditor(
id = newId.trim(),
@@ -710,7 +709,7 @@ private fun <T : ExtensionComponent> CreateComponentScreen(
}
editor.themes.add(componentEditor)
} else {
val component = themeManager.indexedThemeConfigs.value.first.get(componentName) ?: return
val component = themeManager.indexedThemeConfigs.value?.get(componentName) ?: return
val componentEditor = (component as? ThemeExtensionComponentImpl)?.edit() ?: return
componentEditor.id = componentId
componentEditor.stylesheetPath = ""

View File

@@ -27,7 +27,6 @@ import org.florisboard.lib.android.showLongToast
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.ext.Extension
import dev.patrickgold.florisboard.lib.ext.ExtensionDefaults
import org.florisboard.lib.android.showLongToastSync
@Composable
fun ExtensionExportScreen(id: String) {
@@ -62,9 +61,9 @@ private fun ExportScreen(ext: Extension) = FlorisScreen {
return@rememberLauncherForActivityResult
}
runCatching { extensionManager.export(ext, uri) }.onSuccess {
context.showLongToastSync(R.string.ext__export__success)
context.showLongToast(R.string.ext__export__success)
}.onFailure { error ->
context.showLongToastSync(R.string.ext__export__failure, "error_message" to error.localizedMessage)
context.showLongToast(R.string.ext__export__failure, "error_message" to error.localizedMessage)
}
navController.popBackStack()
},

View File

@@ -27,8 +27,8 @@ import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.extensionManager
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.Preference
import org.florisboard.lib.compose.stringRes
@Composable
fun ExtensionHomeScreen() = FlorisScreen {

View File

@@ -50,17 +50,17 @@ import dev.patrickgold.florisboard.ime.keyboard.KeyboardExtension
import dev.patrickgold.florisboard.ime.nlp.LanguagePackExtension
import dev.patrickgold.florisboard.ime.theme.ThemeExtension
import dev.patrickgold.florisboard.lib.NATIVE_NULLPTR
import org.florisboard.lib.android.showLongToast
import dev.patrickgold.florisboard.lib.cache.CacheManager
import dev.patrickgold.florisboard.lib.compose.FlorisBulletSpacer
import dev.patrickgold.florisboard.lib.compose.FlorisButtonBar
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedButton
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.io.FileRegistry
import org.florisboard.lib.compose.FlorisBulletSpacer
import org.florisboard.lib.compose.FlorisButtonBar
import org.florisboard.lib.compose.FlorisOutlinedBox
import org.florisboard.lib.compose.FlorisOutlinedButton
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
import org.florisboard.lib.compose.florisHorizontalScroll
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.android.showLongToastSync
import org.florisboard.lib.kotlin.resultOk
enum class ExtensionImportScreenType(
@@ -188,10 +188,10 @@ fun ExtensionImportScreen(type: ExtensionImportScreenType, initUuid: String?) =
}
}.onSuccess {
workspace.close()
context.showLongToastSync(R.string.ext__import__success)
context.showLongToast(R.string.ext__import__success)
navController.popBackStack()
}.onFailure { error ->
context.showLongToastSync(R.string.ext__import__failure, "error_message" to error.localizedMessage)
context.showLongToast(R.string.ext__import__failure, "error_message" to error.localizedMessage)
}
}
}

View File

@@ -20,7 +20,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import org.florisboard.lib.compose.FlorisChip
import dev.patrickgold.florisboard.lib.compose.FlorisChip
@Composable
fun ExtensionKeywordChip(

View File

@@ -52,14 +52,14 @@ import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.extensionManager
import dev.patrickgold.florisboard.ime.theme.ThemeExtension
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.FlorisTextButton
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
import dev.patrickgold.florisboard.lib.observeAsNonNullState
import org.florisboard.lib.compose.FlorisOutlinedBox
import org.florisboard.lib.compose.FlorisTextButton
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
import org.florisboard.lib.compose.florisScrollbar
import org.florisboard.lib.compose.stringRes
enum class ExtensionListScreenType(
val id: String,

View File

@@ -30,10 +30,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.lib.compose.FlorisChip
import dev.patrickgold.florisboard.lib.ext.ExtensionMaintainer
import dev.patrickgold.florisboard.lib.util.launchUrl
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
import org.florisboard.lib.compose.FlorisChip
@Composable
fun ExtensionMaintainerChip(

View File

@@ -24,7 +24,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import org.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.compose.stringRes
@Composable
internal fun ExtensionNotFoundScreen(id: String) = FlorisScreen {

View File

@@ -51,17 +51,17 @@ import dev.patrickgold.florisboard.extensionManager
import dev.patrickgold.florisboard.ime.nlp.LanguagePackExtension
import dev.patrickgold.florisboard.ime.theme.ThemeExtension
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponentImpl
import org.florisboard.lib.android.showLongToast
import dev.patrickgold.florisboard.lib.compose.FlorisConfirmDeleteDialog
import dev.patrickgold.florisboard.lib.compose.FlorisHyperlinkText
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedButton
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.Extension
import dev.patrickgold.florisboard.lib.ext.ExtensionMaintainer
import dev.patrickgold.florisboard.lib.ext.ExtensionMeta
import dev.patrickgold.florisboard.lib.io.FlorisRef
import org.florisboard.lib.android.showLongToastSync
import org.florisboard.lib.compose.FlorisOutlinedButton
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
import org.florisboard.lib.compose.stringRes
@Composable
fun ExtensionViewScreen(id: String) {
@@ -202,7 +202,7 @@ private fun ViewScreen(ext: Extension) = FlorisScreen {
}.onSuccess {
navController.popBackStack()
}.onFailure { error ->
context.showLongToastSync(
context.showLongToast(
R.string.error__snackbar_message,
"error_message" to error.localizedMessage,
)

View File

@@ -37,13 +37,13 @@ import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.lib.compose.FlorisErrorCard
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.FlorisWarningCard
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.util.InputMethodUtils
import dev.patrickgold.jetpref.datastore.model.observeAsState
import dev.patrickgold.jetpref.datastore.ui.Preference
import org.florisboard.lib.compose.FlorisErrorCard
import org.florisboard.lib.compose.FlorisWarningCard
import org.florisboard.lib.compose.stringRes
@Composable
fun HomeScreen() = FlorisScreen {

View File

@@ -41,12 +41,12 @@ import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.clipboardManager
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.util.launchUrl
import dev.patrickgold.jetpref.datastore.ui.Preference
import org.florisboard.lib.android.stringRes
import org.florisboard.lib.compose.FlorisCanvasIcon
import org.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.compose.FlorisCanvasIcon
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.Preference
@Composable
fun AboutScreen() = FlorisScreen {

View File

@@ -29,11 +29,11 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.sp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
import dev.patrickgold.florisboard.lib.compose.florisVerticalScroll
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.io.FlorisRef
import dev.patrickgold.florisboard.lib.io.loadTextAsset
import org.florisboard.lib.compose.florisHorizontalScroll
import org.florisboard.lib.compose.florisVerticalScroll
import org.florisboard.lib.compose.stringRes
@Composable
fun ProjectLicenseScreen() = FlorisScreen {

View File

@@ -26,8 +26,8 @@ import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
import com.mikepenz.aboutlibraries.ui.compose.m3.libraryColors
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import org.florisboard.lib.compose.florisScrollbar
import org.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
import dev.patrickgold.florisboard.lib.compose.stringRes
@Composable
fun ThirdPartyLicensesScreen() = FlorisScreen {

View File

@@ -32,7 +32,6 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@@ -42,33 +41,28 @@ import androidx.core.app.ShareCompat
import androidx.core.content.FileProvider
import dev.patrickgold.florisboard.BuildConfig
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.cacheManager
import dev.patrickgold.florisboard.clipboardManager
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
import dev.patrickgold.florisboard.ime.clipboard.provider.ItemType
import dev.patrickgold.florisboard.lib.cache.CacheManager
import dev.patrickgold.florisboard.lib.compose.FlorisButtonBar
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.rippleClickable
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.devtools.flogError
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
import dev.patrickgold.florisboard.lib.io.FileRegistry
import dev.patrickgold.florisboard.lib.io.ZipUtils
import dev.patrickgold.jetpref.datastore.runtime.AndroidAppDataStorage
import dev.patrickgold.jetpref.datastore.runtime.FileBasedStorage
import dev.patrickgold.jetpref.datastore.jetprefDatastoreDir
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
import kotlinx.coroutines.launch
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import org.florisboard.lib.android.showLongToast
import org.florisboard.lib.android.showLongToastSync
import org.florisboard.lib.android.writeFromFile
import org.florisboard.lib.compose.FlorisButtonBar
import org.florisboard.lib.compose.FlorisOutlinedBox
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
import org.florisboard.lib.compose.rippleClickable
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.kotlin.io.subDir
import org.florisboard.lib.kotlin.io.subFile
import org.florisboard.lib.kotlin.io.writeJson
@@ -142,7 +136,6 @@ fun BackupScreen() = FlorisScreen {
val navController = LocalNavController.current
val context = LocalContext.current
val cacheManager by context.cacheManager()
val scope = rememberCoroutineScope()
var backupDestination by remember { mutableStateOf(Backup.Destination.FILE_SYS) }
val backupFilesSelector = remember { Backup.FilesSelector() }
@@ -162,24 +155,22 @@ fun BackupScreen() = FlorisScreen {
context.contentResolver.writeFromFile(uri, backupWorkspace!!.zipFile)
backupWorkspace!!.close()
}.onSuccess {
context.showLongToastSync(R.string.backup_and_restore__back_up__success)
context.showLongToast(R.string.backup_and_restore__back_up__success)
navController.popBackStack()
}.onFailure { error ->
flogError { error.stackTraceToString() }
context.showLongToastSync(R.string.backup_and_restore__back_up__failure, "error_message" to error.message)
context.showLongToast(R.string.backup_and_restore__back_up__failure, "error_message" to error.message)
backupWorkspace = null
}
},
)
suspend fun prepareBackupWorkspace() {
fun prepareBackupWorkspace() {
val workspace = cacheManager.backupAndRestore.new()
if (backupFilesSelector.jetprefDatastore) {
val fileBasedStorage = workspace.inputDir
.subDir(AndroidAppDataStorage.JETPREF_DIR_NAME)
.subFile("${FlorisPreferenceModel.NAME}.${AndroidAppDataStorage.JETPREF_FILE_EXT}")
.let { FileBasedStorage(it.path) }
FlorisPreferenceStore.export(fileBasedStorage).getOrThrow()
context.jetprefDatastoreDir.let { dir ->
dir.copyRecursively(workspace.inputDir.subDir(dir.name))
}
}
val workspaceFilesDir = workspace.inputDir.subDir("files")
if (backupFilesSelector.imeKeyboard) {
@@ -234,7 +225,7 @@ fun BackupScreen() = FlorisScreen {
backupWorkspace = workspace
}
suspend fun prepareAndPerformBackup() {
fun prepareAndPerformBackup() {
runCatching {
if (backupWorkspace == null || backupWorkspace!!.isClosed()) {
prepareBackupWorkspace()
@@ -274,7 +265,7 @@ fun BackupScreen() = FlorisScreen {
)
ButtonBarButton(
onClick = {
scope.launch { prepareAndPerformBackup() }
prepareAndPerformBackup()
},
text = stringRes(R.string.action__back_up),
enabled = backupFilesSelector.atLeastOneSelected(),

View File

@@ -27,9 +27,7 @@ import androidx.compose.material.icons.filled.SettingsBackupRestore
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.vectorResource
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.AppTheme
import dev.patrickgold.florisboard.app.LocalNavController
@@ -38,6 +36,7 @@ import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
import dev.patrickgold.florisboard.lib.FlorisLocale
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.model.observeAsState
import dev.patrickgold.jetpref.datastore.ui.ColorPickerPreference
import dev.patrickgold.jetpref.datastore.ui.ListPreference
@@ -46,9 +45,9 @@ 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
import org.florisboard.lib.compose.stringRes
@Composable
@@ -157,7 +156,7 @@ fun OtherScreen() = FlorisScreen {
enabledIf = { AndroidVersion.ATMOST_API28_P },
)
Preference(
icon = ImageVector.vectorResource(R.drawable.ic_keyboard_keys),
icon = vectorResource(R.drawable.ic_keyboard_keys),
title = stringRes(R.string.physical_keyboard__title),
onClick = { navController.navigate(Routes.Settings.PhysicalKeyboard) },
)

View File

@@ -28,9 +28,9 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.Preference
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
import org.florisboard.lib.compose.stringRes
@Composable
fun PhysicalKeyboardScreen() = FlorisScreen {

View File

@@ -43,46 +43,47 @@ import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.BuildConfig
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.cacheManager
import dev.patrickgold.florisboard.clipboardManager
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardItem
import dev.patrickgold.florisboard.ime.clipboard.provider.ItemType
import dev.patrickgold.florisboard.lib.cache.CacheManager
import dev.patrickgold.florisboard.lib.compose.FlorisButtonBar
import dev.patrickgold.florisboard.lib.compose.FlorisCardDefaults
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedButton
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
import dev.patrickgold.florisboard.lib.io.ZipUtils
import dev.patrickgold.jetpref.datastore.runtime.AndroidAppDataStorage
import dev.patrickgold.jetpref.datastore.runtime.FileBasedStorage
import dev.patrickgold.jetpref.datastore.runtime.ImportStrategy
import dev.patrickgold.jetpref.datastore.JetPref
import dev.patrickgold.jetpref.datastore.ui.Preference
import java.io.FileNotFoundException
import java.text.DateFormat
import java.util.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.florisboard.lib.android.readToFile
import org.florisboard.lib.android.showLongToast
import org.florisboard.lib.android.showLongToastSync
import org.florisboard.lib.compose.FlorisButtonBar
import org.florisboard.lib.compose.FlorisCardDefaults
import org.florisboard.lib.compose.FlorisOutlinedBox
import org.florisboard.lib.compose.FlorisOutlinedButton
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
import org.florisboard.lib.kotlin.io.readJson
import org.florisboard.lib.kotlin.io.subDir
import org.florisboard.lib.kotlin.io.subFile
import java.io.FileNotFoundException
import java.text.DateFormat
import java.util.*
object Restore {
const val MIN_VERSION_CODE = 64
const val PACKAGE_NAME = "dev.patrickgold.florisboard"
const val BACKUP_ARCHIVE_FILE_NAME = "backup.zip"
enum class Mode {
MERGE,
ERASE_AND_OVERWRITE;
}
}
@Composable
@@ -90,12 +91,13 @@ fun RestoreScreen() = FlorisScreen {
title = stringRes(R.string.backup_and_restore__restore__title)
previewFieldVisible = false
val prefs by florisPreferenceModel()
val navController = LocalNavController.current
val context = LocalContext.current
val cacheManager by context.cacheManager()
val restoreFilesSelector = remember { Backup.FilesSelector() }
var importStrategy by remember { mutableStateOf(ImportStrategy.Merge) }
var restoreMode by remember { mutableStateOf(Restore.Mode.MERGE) }
// TODO: rememberCoroutineScope() is unusable because it provides the scope in a cancelled state, which does
// not make sense at all. I suspect that this is a bug and once it is resolved we can use it here again.
val restoreScope = remember { CoroutineScope(Dispatchers.Main) }
@@ -136,7 +138,7 @@ fun RestoreScreen() = FlorisScreen {
}
restoreWorkspace = workspace
}.onFailure { error ->
context.showLongToastSync(
context.showLongToast(
R.string.backup_and_restore__restore__failure,
"error_message" to error.localizedMessage,
)
@@ -146,14 +148,14 @@ fun RestoreScreen() = FlorisScreen {
suspend fun performRestore() {
val workspace = restoreWorkspace!!
val shouldReset = importStrategy == ImportStrategy.Erase
val shouldReset = restoreMode == Restore.Mode.ERASE_AND_OVERWRITE
if (restoreFilesSelector.jetprefDatastore) {
val file = workspace.outputDir
.subDir(AndroidAppDataStorage.JETPREF_DIR_NAME)
.subFile("${FlorisPreferenceModel.NAME}.${AndroidAppDataStorage.JETPREF_FILE_EXT}")
if (file.exists()) {
val fileBasedStorage = FileBasedStorage(file.path)
FlorisPreferenceStore.import(importStrategy, fileBasedStorage).getOrThrow()
val datastoreFile = workspace.outputDir
.subDir(JetPref.JETPREF_DIR_NAME)
.subFile("${prefs.name}.${JetPref.JETPREF_FILE_EXT}")
if (datastoreFile.exists()) {
prefs.datastorePersistenceHandler?.loadPrefs(datastoreFile, shouldReset)
prefs.datastorePersistenceHandler?.persistPrefs()
}
}
val workspaceFilesDir = workspace.outputDir.subDir("files")
@@ -273,16 +275,16 @@ fun RestoreScreen() = FlorisScreen {
) {
RadioListItem(
onClick = {
importStrategy = ImportStrategy.Merge
restoreMode = Restore.Mode.MERGE
},
selected = importStrategy == ImportStrategy.Merge,
selected = restoreMode == Restore.Mode.MERGE,
text = stringRes(R.string.backup_and_restore__restore__mode_merge),
)
RadioListItem(
onClick = {
importStrategy = ImportStrategy.Erase
restoreMode = Restore.Mode.ERASE_AND_OVERWRITE
},
selected = importStrategy == ImportStrategy.Erase,
selected = restoreMode == Restore.Mode.ERASE_AND_OVERWRITE,
text = stringRes(R.string.backup_and_restore__restore__mode_erase_and_overwrite),
)
}
@@ -291,7 +293,7 @@ fun RestoreScreen() = FlorisScreen {
runCatching {
restoreDataFromFileSystemLauncher.launch("*/*")
}.onFailure { error ->
context.showLongToastSync(
context.showLongToast(
R.string.backup_and_restore__restore__failure,
"error_message" to error.localizedMessage,
)

View File

@@ -20,13 +20,13 @@ import androidx.compose.runtime.Composable
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.ime.clipboard.CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.pluralsRes
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
import org.florisboard.lib.android.AndroidVersion
import org.florisboard.lib.compose.pluralsRes
import org.florisboard.lib.compose.stringRes
@OptIn(ExperimentalJetPrefDatastoreUi::class)
@Composable

View File

@@ -21,9 +21,9 @@ import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.Preference
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
import org.florisboard.lib.compose.stringRes
@Composable
fun DictionaryScreen() = FlorisScreen {

View File

@@ -55,8 +55,11 @@ import dev.patrickgold.florisboard.ime.dictionary.UserDictionaryDao
import dev.patrickgold.florisboard.ime.dictionary.UserDictionaryEntry
import dev.patrickgold.florisboard.ime.dictionary.UserDictionaryValidation
import dev.patrickgold.florisboard.lib.FlorisLocale
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.Validation
import dev.patrickgold.florisboard.lib.compose.rippleClickable
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.rememberValidationResult
import dev.patrickgold.florisboard.lib.util.launchActivity
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
@@ -65,11 +68,7 @@ import dev.patrickgold.jetpref.material.ui.JetPrefTextField
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.florisboard.lib.android.showLongToast
import org.florisboard.lib.android.showLongToastSync
import org.florisboard.lib.android.stringRes
import org.florisboard.lib.compose.FlorisIconButton
import org.florisboard.lib.compose.rippleClickable
import org.florisboard.lib.compose.stringRes
private val AllLanguagesLocale = FlorisLocale.from(language = "zz")
private val UserDictionaryEntryToAdd = UserDictionaryEntry(id = 0, "", 255, null, null)
@@ -144,16 +143,16 @@ fun UserDictionaryScreen(type: UserDictionaryType) = FlorisScreen {
UserDictionaryType.SYSTEM -> dictionaryManager.systemUserDictionaryDatabase()
}
if (db == null) {
context.showLongToastSync("Database handle is null, failed to import")
context.showLongToast("Database handle is null, failed to import")
return@rememberLauncherForActivityResult
}
runCatching {
db.importCombinedList(context, uri)
}.onSuccess {
buildUi()
context.showLongToastSync(R.string.settings__udm__dictionary_import_success)
context.showLongToast(R.string.settings__udm__dictionary_import_success)
}.onFailure { error ->
context.showLongToastSync("Error: ${error.localizedMessage}")
context.showLongToast("Error: ${error.localizedMessage}")
}
},
)
@@ -169,15 +168,15 @@ fun UserDictionaryScreen(type: UserDictionaryType) = FlorisScreen {
UserDictionaryType.SYSTEM -> dictionaryManager.systemUserDictionaryDatabase()
}
if (db == null) {
context.showLongToastSync("Database handle is null, failed to export")
context.showLongToast("Database handle is null, failed to export")
return@rememberLauncherForActivityResult
}
runCatching {
db.exportCombinedList(context, uri)
}.onSuccess {
context.showLongToastSync(R.string.settings__udm__dictionary_export_success)
context.showLongToast(R.string.settings__udm__dictionary_export_success)
}.onFailure { error ->
context.showLongToastSync("Error: ${error.localizedMessage}")
context.showLongToast("Error: ${error.localizedMessage}")
}
},
)

View File

@@ -23,13 +23,14 @@ import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
import dev.patrickgold.florisboard.lib.compose.FlorisInfoCard
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
import dev.patrickgold.jetpref.datastore.ui.ListPreference
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
import org.florisboard.lib.compose.FlorisInfoCard
import org.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
@OptIn(ExperimentalJetPrefDatastoreUi::class)
@Composable

View File

@@ -23,6 +23,7 @@ import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
import dev.patrickgold.florisboard.ime.input.HapticVibrationMode
import dev.patrickgold.florisboard.ime.input.InputFeedbackActivationMode
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
import dev.patrickgold.jetpref.datastore.ui.ListPreference
@@ -30,7 +31,6 @@ import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
import org.florisboard.lib.android.systemVibratorOrNull
import org.florisboard.lib.android.vibrate
import org.florisboard.lib.compose.stringRes
@OptIn(ExperimentalJetPrefDatastoreUi::class)
@Composable

View File

@@ -29,13 +29,13 @@ import dev.patrickgold.florisboard.ime.smartbar.IncognitoDisplayMode
import dev.patrickgold.florisboard.ime.text.key.KeyHintMode
import dev.patrickgold.florisboard.ime.text.key.UtilityKeyAction
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
import dev.patrickgold.jetpref.datastore.ui.ListPreference
import dev.patrickgold.jetpref.datastore.ui.Preference
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
import org.florisboard.lib.compose.stringRes
@OptIn(ExperimentalJetPrefDatastoreUi::class)
@Composable

View File

@@ -41,27 +41,26 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.app.ext.ExtensionImportScreenType
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.extensionManager
import dev.patrickgold.florisboard.ime.nlp.LanguagePackComponent
import org.florisboard.lib.android.showLongToast
import dev.patrickgold.florisboard.lib.compose.FlorisConfirmDeleteDialog
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.FlorisTextButton
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.rippleClickable
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.Extension
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
import dev.patrickgold.florisboard.lib.observeAsNonNullState
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
import dev.patrickgold.jetpref.datastore.ui.Preference
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
import org.florisboard.lib.android.showLongToast
import org.florisboard.lib.compose.FlorisOutlinedBox
import org.florisboard.lib.compose.FlorisTextButton
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
import org.florisboard.lib.compose.rippleClickable
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.android.showLongToastSync
enum class LanguagePackManagerScreenAction(val id: String) {
MANAGE("manage-installed-language-packs");
@@ -76,7 +75,7 @@ fun LanguagePackManagerScreen(action: LanguagePackManagerScreenAction?) = Floris
else -> error("LanguagePack manager screen action must not be null")
})
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val navController = LocalNavController.current
val context = LocalContext.current
val extensionManager by context.extensionManager()
@@ -200,7 +199,7 @@ fun LanguagePackManagerScreen(action: LanguagePackManagerScreenAction?) = Floris
runCatching {
extensionManager.delete(languagePackExtToDelete!!)
}.onFailure { error ->
context.showLongToastSync(
context.showLongToast(
R.string.error__snackbar_message,
"error_message" to error.localizedMessage,
)

View File

@@ -45,6 +45,8 @@ import dev.patrickgold.florisboard.ime.core.Subtype
import dev.patrickgold.florisboard.ime.keyboard.LayoutType
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.FlorisWarningCard
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.observeAsNonNullState
import dev.patrickgold.florisboard.subtypeManager
import dev.patrickgold.jetpref.datastore.model.observeAsState
@@ -53,9 +55,8 @@ import dev.patrickgold.jetpref.datastore.ui.Preference
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.florisboard.lib.compose.FlorisWarningCard
import org.florisboard.lib.compose.stringRes
internal val SubtypeSaver = Saver<MutableState<Subtype?>, String>(
save = {

View File

@@ -44,15 +44,15 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
import dev.patrickgold.florisboard.lib.FlorisLocale
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.model.observeAsState
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
import org.florisboard.lib.compose.florisScrollbar
import org.florisboard.lib.compose.stringRes
const val SelectLocaleScreenResultLanguageTag = "SelectLocaleScreen.languageTag"
@@ -61,7 +61,7 @@ fun SelectLocaleScreen() = FlorisScreen {
title = stringRes(R.string.settings__localization__subtype_select_locale)
scrollable = false
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val navController = LocalNavController.current
val displayLanguageNamesIn by prefs.localization.displayLanguageNamesIn.observeAsState()

View File

@@ -37,7 +37,6 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ShapeDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
@@ -59,9 +58,9 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.Observer
import androidx.lifecycle.compose.LocalLifecycleOwner
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.core.DisplayLanguageNamesIn
import dev.patrickgold.florisboard.ime.core.Subtype
import dev.patrickgold.florisboard.ime.core.SubtypeJsonConfig
@@ -75,20 +74,19 @@ import dev.patrickgold.florisboard.ime.nlp.han.HanShapeBasedLanguageProvider
import dev.patrickgold.florisboard.ime.nlp.latin.LatinLanguageProvider
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.lib.FlorisLocale
import dev.patrickgold.florisboard.lib.compose.FlorisButtonBar
import dev.patrickgold.florisboard.lib.compose.FlorisDropdownLikeButton
import dev.patrickgold.florisboard.lib.compose.FlorisDropdownMenu
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
import dev.patrickgold.florisboard.lib.observeAsNonNullState
import dev.patrickgold.florisboard.subtypeManager
import dev.patrickgold.jetpref.datastore.model.observeAsState
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
import dev.patrickgold.jetpref.material.ui.JetPrefDropdown
import dev.patrickgold.jetpref.material.ui.JetPrefDropdownMenuDefaults
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
import org.florisboard.lib.compose.FlorisButtonBar
import org.florisboard.lib.compose.FlorisDropdownLikeButton
import org.florisboard.lib.compose.florisScrollbar
import org.florisboard.lib.compose.stringRes
import kotlinx.serialization.encodeToString
private val SelectComponentName = ExtensionComponentName("00", "00")
private val SelectNlpProviderId = SelectComponentName.toString()
@@ -188,7 +186,7 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
val selectValue = stringRes(R.string.settings__localization__subtype_select_placeholder)
val selectListValues = remember(selectValue) { listOf(selectValue) }
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val navController = LocalNavController.current
val context = LocalContext.current
val configuration = LocalConfiguration.current
@@ -368,7 +366,6 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
onClick = {
navController.navigate(Routes.Settings.SelectLocale)
},
appearance = JetPrefDropdownMenuDefaults.outlined(shape = ShapeDefaults.Small),
)
}
SubtypeProperty(stringRes(R.string.settings__localization__subtype_popup_mapping)) {
@@ -378,15 +375,16 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
val popupMappingLabels = remember(popupMappings) {
selectListValues + popupMappings.values.map { it.label }
}
val expanded = remember { mutableStateOf(false) }
var expanded by remember { mutableStateOf(false) }
val selectedIndex = popupMappingIds.indexOf(popupMapping).coerceAtLeast(0)
JetPrefDropdown(
options = popupMappingLabels,
FlorisDropdownMenu(
items = popupMappingLabels,
expanded = expanded,
selectedOptionIndex = selectedIndex,
selectedIndex = selectedIndex,
isError = showSelectAsError && selectedIndex == 0,
onSelectOption = { popupMapping = popupMappingIds[it] },
appearance = JetPrefDropdownMenuDefaults.outlined(shape = ShapeDefaults.Small),
onSelectItem = { popupMapping = popupMappingIds[it] },
onExpandRequest = { expanded = true },
onDismissRequest = { expanded = false },
)
}
SubtypePropertyDropdown(stringRes(R.string.settings__localization__subtype_characters_layout), LayoutType.CHARACTERS)
@@ -407,18 +405,19 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
val nlpProviderMappingLabels = remember(nlpProviderMappings) {
selectListValues + nlpProviderMappings.values.map { it }
}
val expanded = remember { mutableStateOf(false) }
var expanded by remember { mutableStateOf(false) }
val selectedIndex = nlpProviderMappingIds.indexOf(nlpProviders.suggestion).coerceAtLeast(0)
JetPrefDropdown(
options = nlpProviderMappingLabels,
FlorisDropdownMenu(
items = nlpProviderMappingLabels,
expanded = expanded,
selectedOptionIndex = selectedIndex,
selectedIndex = selectedIndex,
isError = showSelectAsError && selectedIndex == 0,
onSelectOption = { nlpProviders = SubtypeNlpProviderMap(
onSelectItem = { nlpProviders = SubtypeNlpProviderMap(
suggestion = nlpProviderMappingIds[it],
spelling = nlpProviderMappingIds[it]
) },
appearance = JetPrefDropdownMenuDefaults.outlined(shape = ShapeDefaults.Small),
onExpandRequest = { expanded = true },
onDismissRequest = { expanded = false },
)
}
@@ -434,14 +433,15 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
val composerNames = remember(composers) {
selectListValues + composers.values.map { it.label }
}
val expanded = remember { mutableStateOf(false) }
JetPrefDropdown(
options = composerNames,
var expanded by remember { mutableStateOf(false) }
FlorisDropdownMenu(
items = composerNames,
expanded = expanded,
selectedOptionIndex = composerIds.indexOf(composer).coerceAtLeast(0),
selectedIndex = composerIds.indexOf(composer).coerceAtLeast(0),
isError = showSelectAsError && composer == SelectComponentName,
onSelectOption = { composer = composerIds[it] },
appearance = JetPrefDropdownMenuDefaults.outlined(shape = ShapeDefaults.Small),
onSelectItem = { composer = composerIds[it] },
onExpandRequest = { expanded = true },
onDismissRequest = { expanded = false },
)
}
SubtypeProperty(stringRes(R.string.settings__localization__subtype_currency_set)) {
@@ -451,14 +451,15 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
val currencySetNames = remember(currencySets) {
selectListValues + currencySets.values.map { it.label }
}
val expanded = remember { mutableStateOf(false) }
JetPrefDropdown(
options = currencySetNames,
var expanded by remember { mutableStateOf(false) }
FlorisDropdownMenu(
items = currencySetNames,
expanded = expanded,
selectedOptionIndex = currencySetIds.indexOf(currencySet).coerceAtLeast(0),
selectedIndex = currencySetIds.indexOf(currencySet).coerceAtLeast(0),
isError = showSelectAsError && currencySet == SelectComponentName,
onSelectOption = { currencySet = currencySetIds[it] },
appearance = JetPrefDropdownMenuDefaults.outlined(shape = ShapeDefaults.Small),
onSelectItem = { currencySet = currencySetIds[it] },
onExpandRequest = { expanded = true },
onDismissRequest = { expanded = false },
)
}
@@ -556,15 +557,16 @@ private fun SubtypeLayoutDropdown(
val layoutIds = remember(layouts) { SelectListKeys + layouts.keys.toList() }
val layoutLabels = remember(layouts) { selectListValues + layouts.values.map { it.label } }
val layoutId = remember(layoutMap) { layoutMap[layoutType] }
val expanded = remember { mutableStateOf(false) }
var expanded by remember { mutableStateOf(false) }
val selectedIndex = layoutIds.indexOf(layoutId).coerceAtLeast(0)
JetPrefDropdown(
options = layoutLabels,
FlorisDropdownMenu(
items = layoutLabels,
expanded = expanded,
selectedOptionIndex = selectedIndex,
selectedIndex = selectedIndex,
isError = showSelectAsError && selectedIndex == 0,
onSelectOption = { onLayoutMapChanged(layoutMap.copy(layoutType = layoutType, componentName = layoutIds[it])!!) },
appearance = JetPrefDropdownMenuDefaults.outlined(shape = ShapeDefaults.Small),
onSelectItem = { onLayoutMapChanged(layoutMap.copy(layoutType, layoutIds[it])!!) },
onExpandRequest = { expanded = true },
onDismissRequest = { expanded = false },
)
}

View File

@@ -27,13 +27,15 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistory
import dev.patrickgold.florisboard.ime.media.emoji.EmojiHistoryHelper
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSkinTone
import dev.patrickgold.florisboard.ime.media.emoji.EmojiSuggestionType
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.pluralsRes
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
import dev.patrickgold.jetpref.datastore.ui.ListPreference
@@ -42,8 +44,6 @@ import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
import kotlinx.coroutines.launch
import org.florisboard.lib.compose.pluralsRes
import org.florisboard.lib.compose.stringRes
@OptIn(ExperimentalJetPrefDatastoreUi::class)
@Composable
@@ -52,7 +52,7 @@ fun MediaScreen() = FlorisScreen {
previewFieldVisible = true
iconSpaceReserved = true
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
var shouldDelete by remember { mutableStateOf<ShouldDelete?>(null) }
val scope = rememberCoroutineScope()

View File

@@ -24,10 +24,10 @@ import dev.patrickgold.florisboard.ime.smartbar.CandidatesDisplayMode
import dev.patrickgold.florisboard.ime.smartbar.ExtendedActionsPlacement
import dev.patrickgold.florisboard.ime.smartbar.SmartbarLayout
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.ListPreference
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
import org.florisboard.lib.compose.stringRes
@Composable
fun SmartbarScreen() = FlorisScreen {
@@ -68,7 +68,7 @@ fun SmartbarScreen() = FlorisScreen {
// TODO: schedule to remove this preference in the future, but keep it for now so users
// know why the setting is not available anymore. Also force enable it for UI display.
SideEffect {
// prefs.smartbar.sharedActionsAutoExpandCollapse.set(true)
prefs.smartbar.sharedActionsAutoExpandCollapse.set(true)
}
SwitchPreference(
prefs.smartbar.sharedActionsAutoExpandCollapse,

View File

@@ -77,7 +77,13 @@ import dev.patrickgold.florisboard.app.ext.FONTS
import dev.patrickgold.florisboard.app.ext.IMAGES
import dev.patrickgold.florisboard.lib.ValidationResult
import dev.patrickgold.florisboard.lib.cache.CacheManager
import dev.patrickgold.florisboard.lib.compose.DpSizeSaver
import dev.patrickgold.florisboard.lib.compose.FlorisChip
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
import dev.patrickgold.florisboard.lib.compose.FlorisTextButton
import dev.patrickgold.florisboard.lib.compose.Validation
import dev.patrickgold.florisboard.lib.compose.florisVerticalScroll
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.ExtensionValidation
import dev.patrickgold.florisboard.lib.rememberValidationResult
import dev.patrickgold.jetpref.material.ui.ColorRepresentation
@@ -88,14 +94,7 @@ import dev.patrickgold.jetpref.material.ui.JetPrefDropdown
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
import dev.patrickgold.jetpref.material.ui.JetPrefTextField
import dev.patrickgold.jetpref.material.ui.rememberJetPrefColorPickerState
import java.io.File
import org.florisboard.lib.color.ColorPalette
import org.florisboard.lib.compose.DpSizeSaver
import org.florisboard.lib.compose.FlorisChip
import org.florisboard.lib.compose.FlorisIconButton
import org.florisboard.lib.compose.FlorisTextButton
import org.florisboard.lib.compose.florisVerticalScroll
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.kotlin.curlyFormat
import org.florisboard.lib.kotlin.io.subDir
import org.florisboard.lib.kotlin.toStringWithoutDotZero
@@ -132,6 +131,7 @@ import org.florisboard.lib.snygg.value.SnyggUndefinedValue
import org.florisboard.lib.snygg.value.SnyggUriValue
import org.florisboard.lib.snygg.value.SnyggValue
import org.florisboard.lib.snygg.value.SnyggValueEncoder
import java.io.File
internal val SnyggEmptyPropertyInfoForAdding = PropertyInfo(
rule = SnyggEmptyRuleForAdding,

View File

@@ -85,19 +85,18 @@ import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.lib.NATIVE_NULLPTR
import dev.patrickgold.florisboard.lib.compose.FlorisChip
import dev.patrickgold.florisboard.lib.compose.FlorisHyperlinkText
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.util.InputMethodUtils
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
import dev.patrickgold.jetpref.material.ui.JetPrefDropdown
import dev.patrickgold.jetpref.material.ui.JetPrefTextField
import dev.patrickgold.jetpref.material.ui.JetPrefTextFieldDefaults
import org.florisboard.lib.android.showShortToast
import org.florisboard.lib.android.showShortToastSync
import org.florisboard.lib.android.stringRes
import org.florisboard.lib.compose.FlorisChip
import org.florisboard.lib.compose.FlorisIconButton
import org.florisboard.lib.compose.florisHorizontalScroll
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.kotlin.curlyFormat
import org.florisboard.lib.snygg.SnyggAnnotationRule
import org.florisboard.lib.snygg.SnyggAttributes
@@ -407,7 +406,7 @@ private fun EditCodeValueDialog(
}
if (!isFlorisBoardEnabled || !isFlorisBoardSelected) {
lastRecordingToast?.cancel()
lastRecordingToast = context.showShortToastSync(
lastRecordingToast = context.showShortToast(
R.string.settings__theme_editor__code_recording_requires_default_ime_floris,
"app_name" to context.stringRes(R.string.floris_app_name),
)
@@ -433,12 +432,12 @@ private fun EditCodeValueDialog(
val defaultReceiver = keyboardManager.inputEventDispatcher.keyEventReceiver
keyboardManager.inputEventDispatcher.keyEventReceiver = receiver
lastRecordingToast?.cancel()
lastRecordingToast = context.showShortToastSync(R.string.settings__theme_editor__code_recording_started)
lastRecordingToast = context.showShortToast(R.string.settings__theme_editor__code_recording_started)
focusRequester.requestFocus()
onDispose {
keyboardManager.inputEventDispatcher.keyEventReceiver = defaultReceiver
lastRecordingToast?.cancel()
lastRecordingToast = context.showShortToastSync(R.string.settings__theme_editor__code_recording_stopped)
lastRecordingToast = context.showShortToast(R.string.settings__theme_editor__code_recording_stopped)
}
}
}

View File

@@ -20,13 +20,13 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.ui.ListPreference
import dev.patrickgold.jetpref.datastore.ui.PreferenceLayout
import dev.patrickgold.jetpref.material.ui.ColorRepresentation
import dev.patrickgold.jetpref.material.ui.JetPrefAlertDialog
import org.florisboard.lib.compose.stringRes
private val FineTuneContentPadding = PaddingValues(horizontal = 8.dp)
@@ -37,7 +37,7 @@ fun FineTuneDialog(onDismiss: () -> Unit) {
onDismiss = onDismiss,
contentPadding = FineTuneContentPadding,
) {
PreferenceLayout(FlorisPreferenceStore, iconSpaceReserved = false) {
PreferenceLayout(florisPreferenceModel(), iconSpaceReserved = false) {
ListPreference(
listPref = prefs.theme.editorLevel,
title = stringRes(R.string.settings__theme_editor__fine_tune__level),

View File

@@ -30,6 +30,7 @@ import androidx.compose.material.icons.automirrored.filled.FormatAlignLeft
import androidx.compose.material.icons.automirrored.filled.FormatAlignRight
import androidx.compose.material.icons.automirrored.filled.WrapText
import androidx.compose.material.icons.filled.AttachFile
import androidx.compose.material.icons.filled.CheckBox
import androidx.compose.material.icons.filled.CheckBoxOutlineBlank
import androidx.compose.material.icons.filled.FontDownload
import androidx.compose.material.icons.filled.FormatAlignCenter
@@ -56,7 +57,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.jetpref.datastore.model.observeAsState
import org.florisboard.lib.snygg.value.SnyggCutCornerDpShapeValue
import org.florisboard.lib.snygg.value.SnyggDefinedVarValue
@@ -120,7 +121,7 @@ internal fun SnyggValueIcon(
modifier: Modifier = Modifier,
spec: SnyggValueIcon.Spec = SnyggValueIcon.Normal,
) {
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val context = LocalContext.current
val accentColor by prefs.theme.accentColor.observeAsState()

View File

@@ -70,9 +70,9 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.apptheme.Shapes
import dev.patrickgold.florisboard.app.ext.ExtensionComponentView
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponentEditor
@@ -80,10 +80,16 @@ import dev.patrickgold.florisboard.ime.theme.ThemeExtensionEditor
import dev.patrickgold.florisboard.ime.theme.ThemeManager
import dev.patrickgold.florisboard.ime.theme.extPreviewTheme
import dev.patrickgold.florisboard.lib.cache.CacheManager
import dev.patrickgold.florisboard.lib.compose.FlorisIconButton
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.PreviewKeyboardField
import dev.patrickgold.florisboard.lib.compose.Validation
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.florisVerticalScroll
import dev.patrickgold.florisboard.lib.compose.rememberPreviewFieldController
import dev.patrickgold.florisboard.lib.compose.rippleClickable
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.ExtensionValidation
import dev.patrickgold.florisboard.lib.rememberValidationResult
import dev.patrickgold.florisboard.themeManager
@@ -94,13 +100,6 @@ import dev.patrickgold.jetpref.material.ui.JetPrefTextField
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.florisboard.lib.android.showLongToast
import org.florisboard.lib.compose.FlorisIconButton
import org.florisboard.lib.compose.FlorisOutlinedBox
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
import org.florisboard.lib.compose.florisVerticalScroll
import org.florisboard.lib.compose.rippleClickable
import org.florisboard.lib.android.showLongToastSync
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.kotlin.io.subFile
import org.florisboard.lib.snygg.SnyggAnnotationRule
import org.florisboard.lib.snygg.SnyggElementRule
@@ -146,7 +145,7 @@ fun ThemeEditorScreen(
title = stringRes(R.string.ext__editor__edit_component__title_theme)
scrollable = false
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val context = LocalContext.current
val focusManager = LocalFocusManager.current
val themeManager by context.themeManager()
@@ -310,14 +309,14 @@ fun ThemeEditorScreen(
}
DisposableEffect(workspace.version) {
themeManager.previewThemeInfo.value = ThemeManager.ThemeInfo.DEFAULT.copy(
themeManager.previewThemeInfo = ThemeManager.ThemeInfo.DEFAULT.copy(
name = extPreviewTheme(System.currentTimeMillis().toString()),
config = editor.build(),
stylesheet = stylesheetEditor.build(),
loadedDir = workspace.extDir,
)
onDispose {
themeManager.previewThemeInfo.value = null
themeManager.previewThemeInfo = null
}
}
@@ -634,7 +633,7 @@ private fun ComponentMetaEditorDialog(
if (!allFieldsValid) {
showValidationErrors = true
} else if (id != editor.id && (workspace.editor as? ThemeExtensionEditor)?.themes?.find { it.id == id.trim() } != null) {
context.showLongToastSync("A theme with this ID already exists!")
context.showLongToast("A theme with this ID already exists!")
} else {
workspace.update {
editor.id = id.trim()

View File

@@ -29,24 +29,22 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.extensionManager
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.defaultFlorisOutlinedBox
import dev.patrickgold.florisboard.lib.compose.rippleClickable
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
import dev.patrickgold.florisboard.lib.observeAsNonNullState
import dev.patrickgold.florisboard.themeManager
import dev.patrickgold.jetpref.datastore.model.observeAsState
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
import org.florisboard.lib.compose.FlorisOutlinedBox
import org.florisboard.lib.compose.defaultFlorisOutlinedBox
import org.florisboard.lib.compose.rippleClickable
import org.florisboard.lib.compose.stringRes
import kotlinx.coroutines.launch
enum class ThemeManagerScreenAction(val id: String) {
SELECT_DAY("select-day"),
@@ -62,11 +60,10 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
})
previewFieldVisible = true
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val context = LocalContext.current
val extensionManager by context.extensionManager()
val themeManager by context.themeManager()
val scope = rememberCoroutineScope()
val indexedThemeExtensions by extensionManager.themes.observeAsNonNullState()
val extGroupedThemes = remember(indexedThemeExtensions) {
@@ -86,7 +83,7 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
val extComponentName = ExtensionComponentName(extId, componentId)
when (action) {
ThemeManagerScreenAction.SELECT_DAY,
ThemeManagerScreenAction.SELECT_NIGHT -> scope.launch {
ThemeManagerScreenAction.SELECT_NIGHT -> {
getThemeIdPref().set(extComponentName)
}
}
@@ -99,9 +96,9 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
content {
DisposableEffect(activeThemeId) {
themeManager.previewThemeId.value = activeThemeId
themeManager.previewThemeId = activeThemeId
onDispose {
themeManager.previewThemeId.value = null
themeManager.previewThemeId = null
}
}
val grayColor = LocalContentColor.current.copy(alpha = 0.56f)

View File

@@ -24,8 +24,8 @@ import androidx.compose.material.icons.filled.DarkMode
import androidx.compose.material.icons.filled.LightMode
import androidx.compose.material.icons.filled.WbTwilight
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import dev.patrickgold.florisboard.R
@@ -37,6 +37,7 @@ import dev.patrickgold.florisboard.app.ext.ExtensionListScreenType
import dev.patrickgold.florisboard.ime.theme.ThemeManager
import dev.patrickgold.florisboard.ime.theme.ThemeMode
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.ext.ExtensionComponentName
import dev.patrickgold.florisboard.themeManager
import dev.patrickgold.jetpref.datastore.model.observeAsState
@@ -46,7 +47,6 @@ import dev.patrickgold.jetpref.datastore.ui.LocalTimePickerPreference
import dev.patrickgold.jetpref.datastore.ui.Preference
import dev.patrickgold.jetpref.datastore.ui.isMaterialYou
import org.florisboard.lib.color.ColorMappings
import org.florisboard.lib.compose.stringRes
@Composable
fun ThemeScreen() = FlorisScreen {
@@ -59,8 +59,8 @@ fun ThemeScreen() = FlorisScreen {
@Composable
fun ThemeManager.getThemeLabel(id: ExtensionComponentName): String {
val configs by indexedThemeConfigs.collectAsState()
configs.first[id]?.let { return it.label }
val configs by indexedThemeConfigs.observeAsState()
configs?.get(id)?.let { return it.label }
return id.toString()
}

View File

@@ -34,14 +34,14 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.lib.compose.FlorisCanvasIcon
import dev.patrickgold.florisboard.lib.compose.FlorisErrorCard
import dev.patrickgold.florisboard.lib.compose.FlorisSimpleCard
import dev.patrickgold.florisboard.lib.compose.FlorisWarningCard
import dev.patrickgold.florisboard.lib.compose.observeAsState
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.util.launchActivity
import org.florisboard.lib.android.AndroidSettings
import org.florisboard.lib.compose.FlorisCanvasIcon
import org.florisboard.lib.compose.FlorisErrorCard
import org.florisboard.lib.compose.FlorisSimpleCard
import org.florisboard.lib.compose.FlorisWarningCard
import org.florisboard.lib.compose.observeAsState
import org.florisboard.lib.compose.stringRes
@Composable
fun SpellCheckerServiceSelector(florisSpellCheckerEnabled: MutableState<Boolean>) {

View File

@@ -30,8 +30,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.LocalNavController
@@ -39,17 +37,19 @@ import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.app.enumDisplayEntriesOf
import dev.patrickgold.florisboard.ime.keyboard.IncognitoMode
import dev.patrickgold.florisboard.ime.nlp.SpellingLanguageMode
import dev.patrickgold.florisboard.lib.compose.FlorisErrorCard
import dev.patrickgold.florisboard.lib.compose.FlorisHyperlinkText
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.model.observeAsState
import dev.patrickgold.jetpref.datastore.ui.DialogSliderPreference
import dev.patrickgold.jetpref.datastore.ui.ExperimentalJetPrefDatastoreUi
import dev.patrickgold.jetpref.datastore.ui.ListPreference
import dev.patrickgold.jetpref.datastore.ui.Preference
import dev.patrickgold.jetpref.datastore.ui.PreferenceGroup
import dev.patrickgold.jetpref.datastore.ui.SwitchPreference
import dev.patrickgold.jetpref.datastore.ui.vectorResource
import org.florisboard.lib.android.AndroidVersion
import org.florisboard.lib.compose.FlorisErrorCard
import org.florisboard.lib.compose.stringRes
@OptIn(ExperimentalJetPrefDatastoreUi::class)
@Composable
@@ -89,7 +89,7 @@ fun TypingScreen() = FlorisScreen {
)
ListPreference(
prefs.suggestion.incognitoMode,
icon = ImageVector.vectorResource(id = R.drawable.ic_incognito),
icon = vectorResource(id = R.drawable.ic_incognito),
title = stringRes(R.string.pref__suggestion__incognito_mode__label),
entries = enumDisplayEntriesOf(IncognitoMode::class),
)

View File

@@ -33,7 +33,6 @@ import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -41,27 +40,26 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.AppPrefs
import dev.patrickgold.florisboard.app.FlorisAppActivity
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.LocalNavController
import dev.patrickgold.florisboard.app.Routes
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.lib.compose.FlorisBulletSpacer
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
import dev.patrickgold.florisboard.lib.compose.FlorisScreenScope
import dev.patrickgold.florisboard.lib.compose.FlorisStep
import dev.patrickgold.florisboard.lib.compose.FlorisStepLayout
import dev.patrickgold.florisboard.lib.compose.FlorisStepState
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.util.InputMethodUtils
import dev.patrickgold.florisboard.lib.util.launchActivity
import dev.patrickgold.florisboard.lib.util.launchUrl
import dev.patrickgold.jetpref.datastore.model.observeAsState
import dev.patrickgold.jetpref.datastore.ui.PreferenceUiScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.florisboard.lib.android.AndroidVersion
import org.florisboard.lib.compose.FlorisBulletSpacer
import org.florisboard.lib.compose.FlorisStep
import org.florisboard.lib.compose.FlorisStepLayout
import org.florisboard.lib.compose.FlorisStepState
import org.florisboard.lib.compose.stringRes
@Composable
fun SetupScreen() = FlorisScreen {
@@ -72,8 +70,7 @@ fun SetupScreen() = FlorisScreen {
val navController = LocalNavController.current
val context = LocalContext.current
val prefs by FlorisPreferenceStore
val scope = rememberCoroutineScope()
val prefs by florisPreferenceModel()
val isFlorisBoardEnabled by InputMethodUtils.observeIsFlorisboardEnabled(foregroundOnly = true)
val isFlorisBoardSelected by InputMethodUtils.observeIsFlorisboardSelected(foregroundOnly = true)
@@ -81,12 +78,10 @@ fun SetupScreen() = FlorisScreen {
val requestNotification =
rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
scope.launch {
if (isGranted) {
prefs.internal.notificationPermissionState.set(NotificationPermissionState.GRANTED)
} else {
prefs.internal.notificationPermissionState.set(NotificationPermissionState.DENIED)
}
if (isGranted) {
prefs.internal.notificationPermissionState.set(NotificationPermissionState.GRANTED)
} else {
prefs.internal.notificationPermissionState.set(NotificationPermissionState.DENIED)
}
}
@@ -96,8 +91,7 @@ fun SetupScreen() = FlorisScreen {
context,
navController,
requestNotification,
hasNotificationPermission,
scope,
hasNotificationPermission
)
}
@@ -109,7 +103,6 @@ private fun FlorisScreenScope.content(
navController: NavController,
requestNotification: ManagedActivityResultLauncher<String, Boolean>,
hasNotificationPermission: NotificationPermissionState,
scope: CoroutineScope,
) {
val stepState = rememberSaveable(saver = FlorisStepState.Saver) {
@@ -165,7 +158,7 @@ private fun FlorisScreenScope.content(
Spacer(modifier = Modifier.height(16.dp))
},
steps = steps(
context, navController, requestNotification, scope
context, navController, requestNotification
),
footer = {
footer(context)
@@ -196,11 +189,10 @@ private fun footer(context: Context) {
}
@Composable
private fun PreferenceUiScope<FlorisPreferenceModel>.steps(
private fun PreferenceUiScope<AppPrefs>.steps(
context: Context,
navController: NavController,
requestNotification: ManagedActivityResultLauncher<String, Boolean>,
scope: CoroutineScope,
): List<FlorisStep> {
return listOfNotNull(
@@ -240,7 +232,7 @@ private fun PreferenceUiScope<FlorisPreferenceModel>.steps(
StepText(stringRes(R.string.setup__finish_up__description_p1))
StepText(stringRes(R.string.setup__finish_up__description_p2))
StepButton(label = stringRes(R.string.setup__finish_up__finish_btn)) {
scope.launch { this@steps.prefs.internal.isImeSetUp.set(true) }
this@steps.prefs.internal.isImeSetUp.set(true)
navController.navigate(Routes.Settings.Home) {
popUpTo(Routes.Setup.Screen) {
inclusive = true

View File

@@ -74,7 +74,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.mutableStateSetOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -88,7 +87,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.clipboardManager
import dev.patrickgold.florisboard.ime.ImeUiMode
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
@@ -102,23 +101,21 @@ import dev.patrickgold.florisboard.ime.smartbar.VerticalExitTransition
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.lib.compose.LocalLocalizedDateTimeFormatter
import dev.patrickgold.florisboard.lib.compose.autoMirrorForRtl
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
import dev.patrickgold.florisboard.lib.compose.florisVerticalScroll
import dev.patrickgold.florisboard.lib.compose.rippleClickable
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.observeAsNonNullState
import dev.patrickgold.florisboard.lib.observeAsTransformingState
import dev.patrickgold.florisboard.lib.util.NetworkUtils
import dev.patrickgold.jetpref.datastore.model.observeAsState
import java.time.Instant
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.florisboard.lib.android.AndroidKeyguardManager
import org.florisboard.lib.android.AndroidVersion
import org.florisboard.lib.android.showShortToastSync
import org.florisboard.lib.android.showShortToast
import org.florisboard.lib.android.systemService
import org.florisboard.lib.compose.LocalLocalizedDateTimeFormatter
import org.florisboard.lib.compose.autoMirrorForRtl
import org.florisboard.lib.compose.florisHorizontalScroll
import org.florisboard.lib.compose.florisVerticalScroll
import org.florisboard.lib.compose.rippleClickable
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.snygg.SnyggQueryAttributes
import org.florisboard.lib.snygg.ui.SnyggBox
import org.florisboard.lib.snygg.ui.SnyggButton
@@ -128,6 +125,7 @@ import org.florisboard.lib.snygg.ui.SnyggIcon
import org.florisboard.lib.snygg.ui.SnyggIconButton
import org.florisboard.lib.snygg.ui.SnyggRow
import org.florisboard.lib.snygg.ui.SnyggText
import java.time.Instant
private val ItemWidth = 200.dp
private val DialogWidth = 240.dp
@@ -138,8 +136,7 @@ const val CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO: Int = 0
fun ClipboardInputLayout(
modifier: Modifier = Modifier,
) {
val prefs by FlorisPreferenceStore
val scope = rememberCoroutineScope()
val prefs by florisPreferenceModel()
val context = LocalContext.current
val clipboardManager by context.clipboardManager()
val keyboardManager by context.keyboardManager()
@@ -147,12 +144,12 @@ fun ClipboardInputLayout(
val deviceLocked = androidKeyguardManager.let { it.isDeviceLocked || it.isKeyguardLocked }
val historyEnabled by prefs.clipboard.historyEnabled.observeAsState()
val unfilteredHistory by clipboardManager.history.observeAsNonNullState()
var isFilterRowShown by remember { mutableStateOf(false) }
val activeFilterTypes = remember { mutableStateSetOf<ItemType>() }
val unfilteredHistory by clipboardManager.history.observeAsNonNullState()
val filteredHistory = remember(unfilteredHistory, activeFilterTypes.toSet()) {
val history = remember(unfilteredHistory, activeFilterTypes.toSet()) {
if (activeFilterTypes.isEmpty()) {
unfilteredHistory
} else {
@@ -163,7 +160,7 @@ fun ClipboardInputLayout(
}
val gridState = rememberLazyStaggeredGridState()
var popupItem by remember(filteredHistory) { mutableStateOf<ClipboardItem?>(null) }
var popupItem by remember(history) { mutableStateOf<ClipboardItem?>(null) }
var showClearAllHistory by remember { mutableStateOf(false) }
fun isPopupSurfaceActive() = popupItem != null || showClearAllHistory
@@ -206,7 +203,7 @@ fun ClipboardInputLayout(
)
SnyggIconButton(
elementName = FlorisImeUi.ClipboardHeaderButton.elementName,
onClick = { scope.launch { prefs.clipboard.historyEnabled.set(!historyEnabled) } },
onClick = { prefs.clipboard.historyEnabled.set(!historyEnabled) },
modifier = sizeModifier.autoMirrorForRtl(),
enabled = !deviceLocked && !isPopupSurfaceActive(),
) {
@@ -222,7 +219,7 @@ fun ClipboardInputLayout(
elementName = FlorisImeUi.ClipboardHeaderButton.elementName,
onClick = { showClearAllHistory = true },
modifier = sizeModifier.autoMirrorForRtl(),
enabled = !deviceLocked && historyEnabled && filteredHistory.all.isNotEmpty() && !isPopupSurfaceActive(),
enabled = !deviceLocked && historyEnabled && unfilteredHistory.all.isNotEmpty() && !isPopupSurfaceActive(),
) {
SnyggIcon(
imageVector = Icons.Default.DeleteSweep,
@@ -467,17 +464,17 @@ fun ClipboardInputLayout(
columns = staggeredGridCells,
) {
clipboardItems(
items = filteredHistory.pinned,
items = history.pinned,
key = "pinned-header",
title = R.string.clipboard__group_pinned,
)
clipboardItems(
items = filteredHistory.recent,
items = history.recent,
key = "recent-header",
title = R.string.clipboard__group_recent,
)
clipboardItems(
items = filteredHistory.other,
items = history.other,
key = "other-header",
title = R.string.clipboard__group_other,
)
@@ -566,13 +563,7 @@ fun ClipboardInputLayout(
) {
SnyggText(
elementName = FlorisImeUi.ClipboardClearAllDialogMessage.elementName,
text = stringRes(
if (isFilterRowShown) {
R.string.clipboard__confirm_clear_filtered_history__message
} else {
R.string.clipboard__confirm_clear_unfiltered_history__message
}
),
text = stringRes(R.string.clipboard__confirm_clear_history__message),
)
SnyggRow(FlorisImeUi.ClipboardClearAllDialogButtons.elementName) {
Spacer(modifier = Modifier.weight(1f))
@@ -591,9 +582,10 @@ fun ClipboardInputLayout(
elementName = FlorisImeUi.ClipboardClearAllDialogButton.elementName,
attributes = mapOf("action" to "yes"),
onClick = {
clipboardManager.clearExactHistory(filteredHistory.unpinned)
context.showShortToastSync(R.string.clipboard__cleared_history)
clipboardManager.clearHistory()
context.showShortToast(R.string.clipboard__cleared_history)
showClearAllHistory = false
isFilterRowShown = false
},
) {
SnyggText(
@@ -637,7 +629,7 @@ fun ClipboardInputLayout(
text = stringRes(R.string.clipboard__disabled__message),
)
SnyggButton(FlorisImeUi.ClipboardHistoryDisabledButton.elementName,
onClick = { scope.launch { prefs.clipboard.historyEnabled.set(true) } },
onClick = { prefs.clipboard.historyEnabled.set(true) },
modifier = Modifier.align(Alignment.End),
) {
SnyggText(
@@ -674,7 +666,7 @@ fun ClipboardInputLayout(
HistoryLockedView()
} else {
if (historyEnabled) {
if (filteredHistory.all.isNotEmpty() || !activeFilterTypes.isEmpty()) {
if (history.all.isNotEmpty() || !activeFilterTypes.isEmpty()) {
HistoryMainView()
} else {
HistoryEmptyView()

View File

@@ -17,10 +17,11 @@
package dev.patrickgold.florisboard.ime.clipboard
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.appContext
import dev.patrickgold.florisboard.editorInstance
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardHistoryDao
@@ -43,7 +44,6 @@ import org.florisboard.lib.android.AndroidClipboardManager
import org.florisboard.lib.android.AndroidClipboardManager_OnPrimaryClipChangedListener
import org.florisboard.lib.android.setOrClearPrimaryClip
import org.florisboard.lib.android.showShortToast
import org.florisboard.lib.android.showShortToastSync
import org.florisboard.lib.android.systemService
import org.florisboard.lib.kotlin.tryOrNull
import java.io.Closeable
@@ -91,7 +91,7 @@ class ClipboardManager(
}
}
private val prefs by FlorisPreferenceStore
private val prefs by florisPreferenceModel()
private val appContext by context.appContext()
private val editorInstance by context.editorInstance()
private val systemClipboardManager = context.systemService(AndroidClipboardManager::class)
@@ -286,15 +286,6 @@ class ClipboardManager(
}
}
fun clearExactHistory(items: List<ClipboardItem>) {
ioScope.launch {
for (item in items) {
item.close(appContext)
}
clipHistoryDao?.delete(items)
}
}
/**
* Clears all unpinned items from the clipboard history
*/
@@ -364,7 +355,7 @@ class ClipboardManager(
val editorInstance by appContext.editorInstance()
editorInstance.commitClipboardItem(item).also { result ->
if (!result) {
appContext.showShortToastSync("Failed to paste item.")
appContext.showShortToast("Failed to paste item.")
}
}
}
@@ -400,8 +391,7 @@ class ClipboardManager(
private val now = System.currentTimeMillis()
val pinned = all.filter { it.isPinned }
val unpinned = all.filter { !it.isPinned }
val recent = unpinned.filter { (now - it.creationTimestampMs) < RECENT_TIMESPAN_MS }
val other = unpinned.filter { (now - it.creationTimestampMs) >= RECENT_TIMESPAN_MS }
val recent = all.filter { !it.isPinned && (now - it.creationTimestampMs < RECENT_TIMESPAN_MS) }
val other = all.filter { !it.isPinned && (now - it.creationTimestampMs >= RECENT_TIMESPAN_MS) }
}
}

View File

@@ -47,15 +47,15 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.apptheme.FlorisAppTheme
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.lib.compose.ProvideLocalizedResources
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.model.observeAsState
import org.florisboard.lib.android.AndroidClipboardManager
import org.florisboard.lib.android.AndroidVersion
import org.florisboard.lib.android.stringRes
import org.florisboard.lib.android.systemService
import org.florisboard.lib.compose.ProvideLocalizedResources
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.kotlin.mimeTypeFilterOf
class FlorisCopyToClipboardActivity : ComponentActivity() {
@@ -135,12 +135,8 @@ class FlorisCopyToClipboardActivity : ComponentActivity() {
@Composable
private fun Content() {
val prefs by FlorisPreferenceStore
ProvideLocalizedResources(
resourcesContext = this,
appName = R.string.app_name,
forceLayoutDirection = LayoutDirection.Ltr,
) {
val prefs by florisPreferenceModel()
ProvideLocalizedResources(this, forceLayoutDirection = LayoutDirection.Ltr) {
val theme by prefs.other.settingsTheme.observeAsState()
FlorisAppTheme(theme) {
BottomSheet {

View File

@@ -35,8 +35,8 @@ import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.ime.keyboard.KeyboardState
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.subtypeManager
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.snygg.ui.SnyggBox
import org.florisboard.lib.snygg.ui.SnyggColumn
import org.florisboard.lib.snygg.ui.SnyggListItem

View File

@@ -17,18 +17,15 @@
package dev.patrickgold.florisboard.ime.core
import android.content.Context
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.keyboard.CurrencySet
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.lib.FlorisLocale
import dev.patrickgold.florisboard.lib.devtools.flogDebug
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.florisboard.lib.kotlin.collectLatestIn
val SubtypeJsonConfig = Json {
encodeDefaults = true
@@ -41,9 +38,8 @@ val SubtypeJsonConfig = Json {
* helper methods for the in-keyboard language switch process.
*/
class SubtypeManager(context: Context) {
private val prefs by FlorisPreferenceStore
private val prefs by florisPreferenceModel()
private val keyboardManager by context.keyboardManager()
private val scope = CoroutineScope(Dispatchers.Default)
private val _subtypesFlow = MutableStateFlow(listOf<Subtype>())
val subtypesFlow = _subtypesFlow.asStateFlow()
@@ -58,7 +54,7 @@ class SubtypeManager(context: Context) {
private set(v) { _activeSubtypeFlow.value = v }
init {
prefs.localization.subtypes.asFlow().collectLatestIn(scope) { listRaw ->
prefs.localization.subtypes.observeForever { listRaw ->
flogDebug { listRaw }
val list = if (listRaw.isNotBlank()) {
SubtypeJsonConfig.decodeFromString<List<Subtype>>(listRaw)
@@ -70,7 +66,7 @@ class SubtypeManager(context: Context) {
}
}
private fun persistNewSubtypeList(list: List<Subtype>) = scope.launch {
private fun persistNewSubtypeList(list: List<Subtype>) {
val listRaw = SubtypeJsonConfig.encodeToString(list)
prefs.localization.subtypes.set(listRaw)
}
@@ -82,7 +78,7 @@ class SubtypeManager(context: Context) {
* @return The active subtype or null, if the subtype list is empty or no new active subtype
* could be determined.
*/
private fun evaluateActiveSubtype(list: List<Subtype>) = scope.launch {
private fun evaluateActiveSubtype(list: List<Subtype>) {
val activeSubtypeId = prefs.localization.activeSubtypeId.get()
val subtype = list.find { it.id == activeSubtypeId } ?: list.firstOrNull() ?: Subtype.DEFAULT
if (subtype.id != activeSubtypeId) {
@@ -188,7 +184,7 @@ class SubtypeManager(context: Context) {
/**
* Switch to the previous subtype in the subtype list if possible.
*/
fun switchToPrevSubtype() = scope.launch {
fun switchToPrevSubtype() {
val subtypeList = subtypes
val cachedActiveSubtype = activeSubtype
var triggerNextSubtype = false
@@ -211,7 +207,7 @@ class SubtypeManager(context: Context) {
/**
* Switch to the next subtype in the subtype list if possible.
*/
fun switchToNextSubtype() = scope.launch {
fun switchToNextSubtype() {
val subtypeList = subtypes
val cachedActiveSubtype = activeSubtype
var triggerNextSubtype = false
@@ -231,7 +227,7 @@ class SubtypeManager(context: Context) {
activeSubtype = newActiveSubtype
}
fun switchToSubtypeById(id: Long) = scope.launch {
fun switchToSubtypeById(id: Long) {
if (subtypes.any { it.id == id }) {
activeSubtype = getSubtypeById(id)!!
prefs.localization.activeSubtypeId.set(id)

View File

@@ -18,7 +18,7 @@ package dev.patrickgold.florisboard.ime.dictionary
import android.content.Context
import androidx.room.Room
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.nlp.SuggestionCandidate
import dev.patrickgold.florisboard.ime.nlp.WordSuggestionCandidate
import dev.patrickgold.florisboard.lib.FlorisLocale
@@ -29,7 +29,7 @@ import java.lang.ref.WeakReference
*/
class DictionaryManager private constructor(context: Context) {
private val applicationContext: WeakReference<Context> = WeakReference(context.applicationContext ?: context)
private val prefs by FlorisPreferenceStore
private val prefs by florisPreferenceModel()
private var florisUserDictionaryDatabase: FlorisUserDictionaryDatabase? = null
private var systemUserDictionaryDatabase: SystemUserDictionaryDatabase? = null

View File

@@ -40,16 +40,6 @@ import org.florisboard.lib.kotlin.guardedByLock
import kotlin.math.max
import kotlin.math.min
enum class OperationUnit {
CHARACTERS,
WORDS;
}
enum class OperationScope {
BEFORE_CURSOR,
AFTER_CURSOR;
}
@Suppress("BlockingMethodInNonBlockingContext")
abstract class AbstractEditorInstance(context: Context) {
companion object {
@@ -438,69 +428,41 @@ abstract class AbstractEditorInstance(context: Context) {
return true
}
protected suspend fun deleteAroundCursor(unit: OperationUnit, scope: OperationScope, n: Int = 0): Boolean {
protected fun deleteBeforeCursor(type: TextType, n: Int): Boolean {
val ic = currentInputConnection()
if (ic == null || n < 1) return false
val content = activeContent
// Cannot perform below check due to editors which lie about their correct selection
//if (content.selection.isValid && content.selection.start == 0) return true
val scopeText = when (scope) {
OperationScope.BEFORE_CURSOR -> content.textBeforeSelection
OperationScope.AFTER_CURSOR -> content.textAfterSelection
}
return (if (activeInfo.isRawInputEditor || scopeText.isEmpty()) {
val oldTextBeforeSelection = content.textBeforeSelection
return (if (activeInfo.isRawInputEditor || oldTextBeforeSelection.isEmpty()) {
// If editor is rich and text before selection is empty we seem to have an invalid state here, so we fall
// back to emulating a hardware backspace/forward delete.
val keyEventCode = when (scope) {
OperationScope.BEFORE_CURSOR -> KeyEvent.KEYCODE_DEL
OperationScope.AFTER_CURSOR -> KeyEvent.KEYCODE_FORWARD_DEL
// back to emulating a hardware backspace.
when (type) {
TextType.CHARACTERS -> sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL, count = n)
TextType.WORDS -> sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL, meta(ctrl = true), count = n)
}
val metaState = when (unit) {
OperationUnit.CHARACTERS -> meta()
OperationUnit.WORDS -> meta(ctrl = true)
}
sendDownUpKeyEvent(keyEventCode, metaState, count = n)
} else {
val locale = subtypeManager.activeSubtype.primaryLocale
when (scope) {
OperationScope.BEFORE_CURSOR -> {
val length = when (unit) {
OperationUnit.CHARACTERS -> breakIterators.measureLastUChars(scopeText, n, locale)
OperationUnit.WORDS -> breakIterators.measureLastUWords(scopeText, n, locale)
}
val selection = content.selection
val newSelection = selection.translatedBy(-length)
val newContent = content.generateCopy(
selection = newSelection,
textBeforeSelection = scopeText.dropLast(length),
)
expectedContentQueue.push(newContent)
ic.beginBatchEdit()
ic.finishComposingText()
ic.deleteSurroundingText(length, 0)
ic.setComposingRegion(newContent.composing)
ic.endBatchEdit()
}
OperationScope.AFTER_CURSOR -> {
val length = when (unit) {
OperationUnit.CHARACTERS -> breakIterators.measureUChars(scopeText, n, locale)
OperationUnit.WORDS -> breakIterators.measureUWords(scopeText, n, locale)
}
val selection = content.selection
val newSelection = selection.translatedBy(length)
val newContent = content.generateCopy(
selection = newSelection,
textAfterSelection = scopeText.drop(length),
)
expectedContentQueue.push(newContent)
ic.beginBatchEdit()
ic.finishComposingText()
ic.deleteSurroundingText(0, length)
ic.setComposingRegion(newContent.composing)
ic.endBatchEdit()
runBlocking {
val locale = subtypeManager.activeSubtype.primaryLocale
val length = when (type) {
TextType.CHARACTERS -> breakIterators.measureLastUChars(oldTextBeforeSelection, n, locale)
TextType.WORDS -> breakIterators.measureLastUWords(oldTextBeforeSelection, n, locale)
}
val selection = content.selection
val newSelection = selection.translatedBy(-length)
val newContent = content.generateCopy(
selection = newSelection,
textBeforeSelection = oldTextBeforeSelection.dropLast(length),
)
expectedContentQueue.push(newContent)
ic.beginBatchEdit()
ic.finishComposingText()
ic.deleteSurroundingText(length, 0)
ic.setComposingRegion(newContent.composing)
ic.endBatchEdit()
true
}
true
}).also {
deleteMoveLastCommitPosition()
}
@@ -661,6 +623,11 @@ abstract class AbstractEditorInstance(context: Context) {
return true
}
protected enum class TextType {
CHARACTERS,
WORDS;
}
private class ExpectedContentQueue {
private val list = guardedByLock { mutableListOf<EditorContent>() }

View File

@@ -91,9 +91,6 @@ data class EditorContent(
val currentWordText: String
get() = if (localCurrentWord.isValid) text.safeSubstring(localCurrentWord.start, localCurrentWord.end) else ""
val safeEditorBounds: EditorRange
get() = if (offset >= 0) EditorRange(0, offset + text.length) else EditorRange(0, 0)
companion object {
/**
* Default editor content which indicates an unspecified content. This is used for raw editors or if there is

View File

@@ -23,7 +23,7 @@ import android.view.KeyEvent
import androidx.core.view.inputmethod.InputConnectionCompat
import androidx.core.view.inputmethod.InputContentInfoCompat
import dev.patrickgold.florisboard.FlorisImeService
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.appContext
import dev.patrickgold.florisboard.clipboardManager
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
@@ -42,14 +42,14 @@ import dev.patrickgold.florisboard.nlpManager
import dev.patrickgold.florisboard.subtypeManager
import java.util.concurrent.atomic.AtomicInteger
import kotlinx.coroutines.runBlocking
import org.florisboard.lib.android.showShortToastSync
import org.florisboard.lib.android.showShortToast
class EditorInstance(context: Context) : AbstractEditorInstance(context) {
companion object {
private const val SPACE = " "
}
private val prefs by FlorisPreferenceStore
private val prefs by florisPreferenceModel()
private val appContext by context.appContext()
private val clipboardManager by context.clipboardManager()
private val keyboardManager by context.keyboardManager()
@@ -335,75 +335,49 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
*
* @return True on success, false if an error occurred or the input connection is invalid.
*/
fun deleteBackwards(unit: OperationUnit): Boolean {
fun deleteBackwards(): Boolean {
val content = activeContent
if (unit == OperationUnit.CHARACTERS) {
if (phantomSpace.isActive && content.currentWord.isValid && prefs.glide.immediateBackspaceDeletesWord.get()) {
return deleteBackwards(OperationUnit.WORDS)
}
if (phantomSpace.isActive && content.currentWord.isValid && prefs.glide.immediateBackspaceDeletesWord.get()) {
return deleteWordBackwards()
}
autoSpace.setInactive()
phantomSpace.setInactive()
return if (content.selection.isSelectionMode) {
commitText("")
} else runBlocking {
deleteAroundCursor(unit, OperationScope.BEFORE_CURSOR, n = 1)
} else {
deleteBeforeCursor(TextType.CHARACTERS, 1)
}
}
/**
* Executes a backward delete on this editor's text. If a text selection is active, all
* characters inside this selection will be removed, else only the left-most character from
* characters inside this selection will be removed, else only the left-most word from
* the cursor's position.
*
* @return True on success, false if an error occurred or the input connection is invalid.
*/
fun deleteForwards(unit: OperationUnit): Boolean {
val content = activeContent
fun deleteWordBackwards(): Boolean {
autoSpace.setInactive()
phantomSpace.setInactive()
return if (content.selection.isSelectionMode) {
return if (activeContent.selection.isSelectionMode) {
commitText("")
} else runBlocking {
deleteAroundCursor(unit, OperationScope.AFTER_CURSOR, n = 1)
} else {
deleteBeforeCursor(TextType.WORDS, 1)
}
}
fun setSelectionSurrounding(n: Int, unit: OperationUnit, scope: OperationScope): Boolean {
fun selectionSetNWordsLeft(n: Int): Boolean {
autoSpace.setInactive()
phantomSpace.setInactive()
val content = activeContent
val selection = content.selection
val safeEditorBounds = content.safeEditorBounds
if (selection.isNotValid) return false
when (scope) {
OperationScope.BEFORE_CURSOR -> {
if (n <= 0) {
return setSelection(selection.end, selection.end)
}
val textToAnalyze = content.text.substring(0, content.localSelection.end)
val length = runBlocking {
when (unit) {
OperationUnit.CHARACTERS -> breakIterators.measureLastUChars(textToAnalyze, n)
OperationUnit.WORDS -> breakIterators.measureLastUWords(textToAnalyze, n)
}
}
return setSelection((selection.end - length).coerceAtLeast(safeEditorBounds.start), selection.end)
}
OperationScope.AFTER_CURSOR -> {
if (n <= 0) {
return setSelection(selection.start, selection.start)
}
val textToAnalyze = content.text.substring(content.localSelection.start)
val length = runBlocking {
when (unit) {
OperationUnit.CHARACTERS -> breakIterators.measureUChars(textToAnalyze, n)
OperationUnit.WORDS -> breakIterators.measureUWords(textToAnalyze, n)
}
}
return setSelection(selection.start, (selection.start + length).coerceAtMost(safeEditorBounds.end))
}
if (n <= 0) {
return setSelection(selection.end, selection.end)
}
val textToAnalyze = content.text.substring(0, content.localSelection.end)
val length = runBlocking { breakIterators.measureLastUWords(textToAnalyze, n) }
return setSelection((selection.end - length).coerceAtLeast(0), selection.end)
}
/**
@@ -419,9 +393,9 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
if (text != null) {
clipboardManager.addNewPlaintext(text.toString())
} else {
appContext.showShortToastSync("Failed to retrieve selected text requested to cut: Eiter selection state is invalid or an error occurred within the input connection.")
appContext.showShortToast("Failed to retrieve selected text requested to cut: Eiter selection state is invalid or an error occurred within the input connection.")
}
return deleteBackwards(OperationUnit.CHARACTERS)
return deleteBackwards()
}
/**
@@ -437,7 +411,7 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
if (text != null) {
clipboardManager.addNewPlaintext(text.toString())
} else {
appContext.showShortToastSync("Failed to retrieve selected text requested to copy: Eiter selection state is invalid or an error occurred within the input connection.")
appContext.showShortToast("Failed to retrieve selected text requested to copy: Eiter selection state is invalid or an error occurred within the input connection.")
}
val activeSelection = activeContent.selection
return setSelection(activeSelection.end, activeSelection.end)
@@ -454,7 +428,7 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
phantomSpace.setInactive()
return commitClipboardItem(clipboardManager.primaryClip).also { result ->
if (!result) {
appContext.showShortToastSync("Failed to paste item.")
appContext.showShortToast("Failed to paste item.")
}
}
}

View File

@@ -21,7 +21,7 @@ import android.view.ViewConfiguration
import androidx.collection.SparseArrayCompat
import androidx.collection.isNotEmpty
import androidx.collection.set
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.keyboard.KeyData
import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
import dev.patrickgold.florisboard.ime.text.key.KeyCode
@@ -48,7 +48,7 @@ class InputEventDispatcher private constructor(private val repeatableKeyCodes: I
fun new(repeatableKeyCodes: IntArray = intArrayOf()) = InputEventDispatcher(repeatableKeyCodes.clone())
}
private val prefs by FlorisPreferenceStore
private val prefs by florisPreferenceModel()
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
private val pressedKeys = guardedByLock { SparseArrayCompat<PressedKeyInfo>() }

View File

@@ -21,7 +21,7 @@ import android.media.AudioManager
import android.provider.Settings
import android.view.HapticFeedbackConstants
import androidx.compose.runtime.staticCompositionLocalOf
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.keyboard.KeyData
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
@@ -46,7 +46,7 @@ class InputFeedbackController private constructor(private val ims: InputMethodSe
fun new(ims: InputMethodService) = InputFeedbackController(ims)
}
private val prefs by FlorisPreferenceStore
private val prefs by florisPreferenceModel()
private val audioManager = ims.systemServiceOrNull(AudioManager::class)
private val vibrator = ims.systemVibratorOrNull()

View File

@@ -19,10 +19,8 @@ package dev.patrickgold.florisboard.ime.keyboard
import android.content.Context
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowRightAlt
import androidx.compose.material.icons.automirrored.filled.Backspace
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
import androidx.compose.material.icons.automirrored.filled.KeyboardBackspace
import androidx.compose.material.icons.automirrored.filled.KeyboardReturn
import androidx.compose.material.icons.automirrored.filled.Redo
import androidx.compose.material.icons.automirrored.filled.Send
@@ -57,8 +55,7 @@ import dev.patrickgold.florisboard.ime.input.InputShiftState
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.key.KeyType
import dev.patrickgold.florisboard.lib.FlorisLocale
import dev.patrickgold.florisboard.lib.compose.vectorResource
import org.florisboard.lib.compose.icons.ForwardDelete
import dev.patrickgold.jetpref.datastore.ui.vectorResource
interface ComputingEvaluator {
val version: Int
@@ -237,9 +234,6 @@ fun ComputingEvaluator.computeImageVector(data: KeyData): ImageVector? {
}
}
}
KeyCode.FORWARD_DELETE -> {
Icons.AutoMirrored.Default.ForwardDelete
}
KeyCode.IME_UI_MODE_MEDIA -> {
Icons.Default.SentimentSatisfiedAlt
}

View File

@@ -36,7 +36,8 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.core.view.WindowInsetsCompat
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
import dev.patrickgold.florisboard.ime.smartbar.ExtendedActionsPlacement
import dev.patrickgold.florisboard.ime.smartbar.SmartbarLayout
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyboard
@@ -79,7 +80,7 @@ object FlorisImeSizing {
@Composable
fun smartbarUiHeight(): Dp {
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val smartbarEnabled by prefs.smartbar.enabled.observeAsState()
val smartbarLayout by prefs.smartbar.layout.observeAsState()
val extendedActionsExpanded by prefs.smartbar.extendedActionsExpanded.observeAsState()
@@ -112,7 +113,7 @@ object FlorisImeSizing {
@Composable
fun ProvideKeyboardRowBaseHeight(content: @Composable () -> Unit) {
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val resources = LocalContext.current.resources
val configuration = LocalConfiguration.current

View File

@@ -26,7 +26,7 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.MutableLiveData
import dev.patrickgold.florisboard.FlorisImeService
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.appContext
import dev.patrickgold.florisboard.clipboardManager
import dev.patrickgold.florisboard.editorInstance
@@ -39,7 +39,6 @@ import dev.patrickgold.florisboard.ime.editor.EditorContent
import dev.patrickgold.florisboard.ime.editor.FlorisEditorInfo
import dev.patrickgold.florisboard.ime.editor.ImeOptions
import dev.patrickgold.florisboard.ime.editor.InputAttributes
import dev.patrickgold.florisboard.ime.editor.OperationUnit
import dev.patrickgold.florisboard.ime.input.CapitalizationBehavior
import dev.patrickgold.florisboard.ime.input.InputEventDispatcher
import dev.patrickgold.florisboard.ime.input.InputKeyEventReceiver
@@ -75,8 +74,7 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.florisboard.lib.android.AndroidKeyguardManager
import org.florisboard.lib.android.showLongToast
import org.florisboard.lib.android.showLongToastSync
import org.florisboard.lib.android.showShortToastSync
import org.florisboard.lib.android.showShortToast
import org.florisboard.lib.android.systemService
import org.florisboard.lib.kotlin.collectIn
import org.florisboard.lib.kotlin.collectLatestIn
@@ -85,7 +83,7 @@ import java.util.concurrent.atomic.AtomicInteger
private val DoubleSpacePeriodMatcher = """([^.!?‽\s]\s)""".toRegex()
class KeyboardManager(context: Context) : InputKeyEventReceiver {
private val prefs by FlorisPreferenceStore
private val prefs by florisPreferenceModel()
private val appContext by context.appContext()
private val clipboardManager by context.clipboardManager()
private val editorInstance by context.editorInstance()
@@ -131,21 +129,21 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
keyboardCache.clear()
}
}
prefs.keyboard.numberRow.asFlow().collectLatestIn(scope) {
prefs.keyboard.numberRow.observeForever {
updateActiveEvaluators {
keyboardCache.clear(KeyboardMode.CHARACTERS)
}
}
prefs.keyboard.hintedNumberRowEnabled.asFlow().collectLatestIn(scope) {
prefs.keyboard.hintedNumberRowEnabled.observeForever {
updateActiveEvaluators()
}
prefs.keyboard.hintedSymbolsEnabled.asFlow().collectLatestIn(scope) {
prefs.keyboard.hintedSymbolsEnabled.observeForever {
updateActiveEvaluators()
}
prefs.keyboard.utilityKeyEnabled.asFlow().collectLatestIn(scope) {
prefs.keyboard.utilityKeyEnabled.observeForever {
updateActiveEvaluators()
}
prefs.keyboard.utilityKeyAction.asFlow().collectLatestIn(scope) {
prefs.keyboard.utilityKeyAction.observeForever {
updateActiveEvaluators()
}
activeState.collectLatestIn(scope) {
@@ -166,10 +164,10 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
editorInstance.activeContentFlow.collectIn(scope) { content ->
resetSuggestions(content)
}
prefs.devtools.enabled.asFlow().collectLatestIn(scope) {
prefs.devtools.enabled.observeForever {
reevaluateDebugFlags()
}
prefs.devtools.showDragAndDropHelpers.asFlow().collectLatestIn(scope) {
prefs.devtools.showDragAndDropHelpers.observeForever {
reevaluateDebugFlags()
}
}
@@ -239,7 +237,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
return subtypeManager.subtypes.size > 1
}
suspend fun toggleOneHandedMode() {
fun toggleOneHandedMode() {
prefs.keyboard.oneHandedModeEnabled.set(!prefs.keyboard.oneHandedModeEnabled.get())
}
@@ -417,30 +415,27 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
/**
* Handles a [KeyCode.DELETE] event.
*/
private fun handleBackwardDelete(unit: OperationUnit) {
if (inputEventDispatcher.isPressed(KeyCode.SHIFT)) {
return handleForwardDelete(unit)
}
private fun handleDelete() {
activeState.batchEdit {
it.isManualSelectionMode = false
it.isManualSelectionModeStart = false
it.isManualSelectionModeEnd = false
}
revertPreviouslyAcceptedCandidate()
editorInstance.deleteBackwards(unit)
editorInstance.deleteBackwards()
}
/**
* Handles a [KeyCode.FORWARD_DELETE] event.
* Handles a [KeyCode.DELETE_WORD] event.
*/
private fun handleForwardDelete(unit: OperationUnit) {
private fun handleDeleteWord() {
activeState.batchEdit {
it.isManualSelectionMode = false
it.isManualSelectionModeStart = false
it.isManualSelectionModeEnd = false
}
revertPreviouslyAcceptedCandidate()
editorInstance.deleteForwards(unit)
editorInstance.deleteWordBackwards()
}
/**
@@ -569,7 +564,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
if (inputEventDispatcher.isConsecutiveUp(data)) {
val text = editorInstance.run { activeContent.getTextBeforeCursor(2) }
if (text.length == 2 && DoubleSpacePeriodMatcher.matches(text)) {
editorInstance.deleteBackwards(OperationUnit.CHARACTERS)
editorInstance.deleteBackwards()
editorInstance.commitText(". ")
return
}
@@ -585,7 +580,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
/**
* Handles a [KeyCode.TOGGLE_INCOGNITO_MODE] event.
*/
private suspend fun handleToggleIncognitoMode() {
private fun handleToggleIncognitoMode() {
prefs.suggestion.forceIncognitoModeFromDynamic.set(!prefs.suggestion.forceIncognitoModeFromDynamic.get())
val newState = !activeState.isIncognitoMode
activeState.isIncognitoMode = newState
@@ -611,7 +606,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
private fun handleToggleAutocorrect() {
lastToastReference.get()?.cancel()
lastToastReference = WeakReference(
appContext.showLongToastSync("Autocorrect toggle is a placeholder and not yet implemented")
appContext.showLongToast("Autocorrect toggle is a placeholder and not yet implemented")
)
}
@@ -721,22 +716,20 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
clipboardManager.primaryClip?.let { clipboardManager.deleteClip(it) }
}
clipboardManager.updatePrimaryClip(null)
appContext.showShortToastSync(R.string.clipboard__cleared_primary_clip)
appContext.showShortToast(R.string.clipboard__cleared_primary_clip)
}
KeyCode.TOGGLE_COMPACT_LAYOUT -> scope.launch { toggleOneHandedMode() }
KeyCode.COMPACT_LAYOUT_TO_LEFT -> scope.launch {
KeyCode.TOGGLE_COMPACT_LAYOUT -> toggleOneHandedMode()
KeyCode.COMPACT_LAYOUT_TO_LEFT -> {
prefs.keyboard.oneHandedMode.set(OneHandedMode.START)
toggleOneHandedMode()
}
KeyCode.COMPACT_LAYOUT_TO_RIGHT -> scope.launch {
KeyCode.COMPACT_LAYOUT_TO_RIGHT -> {
prefs.keyboard.oneHandedMode.set(OneHandedMode.END)
toggleOneHandedMode()
}
KeyCode.DELETE -> handleBackwardDelete(OperationUnit.CHARACTERS)
KeyCode.DELETE_WORD -> handleBackwardDelete(OperationUnit.WORDS)
KeyCode.DELETE -> handleDelete()
KeyCode.DELETE_WORD -> handleDeleteWord()
KeyCode.ENTER -> handleEnter()
KeyCode.FORWARD_DELETE -> handleForwardDelete(OperationUnit.CHARACTERS)
KeyCode.FORWARD_DELETE_WORD -> handleForwardDelete(OperationUnit.WORDS)
KeyCode.IME_SHOW_UI -> FlorisImeService.showUi()
KeyCode.IME_HIDE_UI -> FlorisImeService.hideUi()
KeyCode.IME_PREV_SUBTYPE -> subtypeManager.switchToPrevSubtype()
@@ -760,7 +753,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
}
KeyCode.SYSTEM_PREV_INPUT_METHOD -> FlorisImeService.switchToPrevInputMethod()
KeyCode.SYSTEM_NEXT_INPUT_METHOD -> FlorisImeService.switchToNextInputMethod()
KeyCode.TOGGLE_SMARTBAR_VISIBILITY -> scope.launch {
KeyCode.TOGGLE_SMARTBAR_VISIBILITY -> {
prefs.smartbar.enabled.let { it.set(!it.get()) }
}
KeyCode.TOGGLE_ACTIONS_OVERFLOW -> {
@@ -769,7 +762,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
KeyCode.TOGGLE_ACTIONS_EDITOR -> {
activeState.isActionsEditorVisible = !activeState.isActionsEditorVisible
}
KeyCode.TOGGLE_INCOGNITO_MODE -> scope.launch { handleToggleIncognitoMode() }
KeyCode.TOGGLE_INCOGNITO_MODE -> handleToggleIncognitoMode()
KeyCode.TOGGLE_AUTOCORRECT -> handleToggleAutocorrect()
KeyCode.UNDO -> editorInstance.performUndo()
KeyCode.VIEW_CHARACTERS -> activeState.keyboardMode = KeyboardMode.CHARACTERS
@@ -871,20 +864,6 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
handleEnter()
return true
}
KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_SHIFT_RIGHT -> {
inputEventDispatcher.sendDown(TextKeyData.SHIFT)
return true
}
else -> return false
}
}
fun onHardwareKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
when (keyCode) {
KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_SHIFT_RIGHT -> {
inputEventDispatcher.sendUp(TextKeyData.SHIFT)
return true
}
else -> return false
}
}

View File

@@ -17,7 +17,7 @@
package dev.patrickgold.florisboard.ime.keyboard
import android.content.Context
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.appContext
import dev.patrickgold.florisboard.extensionManager
import dev.patrickgold.florisboard.ime.core.Subtype
@@ -78,7 +78,7 @@ data class DebugLayoutComputationResult(
* Class which manages layout loading and caching.
*/
class LayoutManager(context: Context) {
private val prefs by FlorisPreferenceStore
private val prefs by florisPreferenceModel()
private val appContext by context.appContext()
private val extensionManager by context.extensionManager()
private val keyboardManager by context.keyboardManager()

View File

@@ -16,12 +16,13 @@
package dev.patrickgold.florisboard.ime.media.emoji
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
import dev.patrickgold.florisboard.app.AppPrefs
import dev.patrickgold.florisboard.lib.devtools.flogError
import dev.patrickgold.jetpref.datastore.model.PreferenceSerializer
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
@Serializable
@@ -75,7 +76,7 @@ data class EmojiHistory(
object EmojiHistoryHelper {
private var emojiGuard = Mutex(locked = false)
suspend fun markEmojiUsed(prefs: FlorisPreferenceModel, emoji: Emoji): Unit = emojiGuard.withLock {
suspend fun markEmojiUsed(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
if (!prefs.emoji.historyEnabled.get()) {
return
}
@@ -120,7 +121,7 @@ object EmojiHistoryHelper {
)
}
suspend fun pinEmoji(prefs: FlorisPreferenceModel, emoji: Emoji): Unit = emojiGuard.withLock {
suspend fun pinEmoji(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
if (!prefs.emoji.historyEnabled.get()) {
return
}
@@ -137,7 +138,7 @@ object EmojiHistoryHelper {
prefs.emoji.historyData.set(dataMut.build())
}
suspend fun unpinEmoji(prefs: FlorisPreferenceModel, emoji: Emoji): Unit = emojiGuard.withLock {
suspend fun unpinEmoji(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
if (!prefs.emoji.historyEnabled.get()) {
return
}
@@ -154,7 +155,7 @@ object EmojiHistoryHelper {
prefs.emoji.historyData.set(dataMut.build())
}
suspend fun moveEmoji(prefs: FlorisPreferenceModel, emoji: Emoji, offset: Int): Unit = emojiGuard.withLock {
suspend fun moveEmoji(prefs: AppPrefs, emoji: Emoji, offset: Int) = emojiGuard.withLock {
if (!prefs.emoji.historyEnabled.get() || offset == 0) {
return
}
@@ -174,7 +175,7 @@ object EmojiHistoryHelper {
prefs.emoji.historyData.set(dataMut.build())
}
suspend fun removeEmoji(prefs: FlorisPreferenceModel, emoji: Emoji): Unit = emojiGuard.withLock {
suspend fun removeEmoji(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
if (!prefs.emoji.historyEnabled.get()) {
return
}
@@ -194,7 +195,7 @@ object EmojiHistoryHelper {
prefs.emoji.historyData.set(dataMut.build())
}
suspend fun deleteHistory(prefs: FlorisPreferenceModel): Unit = emojiGuard.withLock {
suspend fun deleteHistory(prefs: AppPrefs) = emojiGuard.withLock {
if (!prefs.emoji.historyEnabled.get()) {
return
}
@@ -202,7 +203,7 @@ object EmojiHistoryHelper {
prefs.emoji.historyData.set(EmojiHistory(pinned = dataMut.pinned, listOf()))
}
suspend fun deletePinned(prefs: FlorisPreferenceModel): Unit = emojiGuard.withLock {
suspend fun deletePinned(prefs: AppPrefs) = emojiGuard.withLock {
if (!prefs.emoji.historyEnabled.get()) {
return
}

View File

@@ -84,21 +84,21 @@ import androidx.compose.ui.window.Popup
import androidx.emoji2.text.EmojiCompat
import androidx.emoji2.widget.EmojiTextView
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.editorInstance
import dev.patrickgold.florisboard.ime.input.LocalInputFeedbackController
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.lib.compose.florisScrollbar
import dev.patrickgold.florisboard.lib.compose.header
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.jetpref.datastore.model.observeAsState
import kotlinx.coroutines.launch
import org.florisboard.lib.android.AndroidKeyguardManager
import org.florisboard.lib.android.showShortToast
import org.florisboard.lib.android.systemService
import org.florisboard.lib.compose.florisScrollbar
import org.florisboard.lib.compose.header
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.snygg.SnyggSelector
import org.florisboard.lib.snygg.ui.SnyggBox
import org.florisboard.lib.snygg.ui.SnyggIcon
@@ -134,7 +134,7 @@ fun EmojiPaletteView(
fullEmojiMappings: EmojiData,
modifier: Modifier = Modifier,
) {
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val context = LocalContext.current
val editorInstance by context.editorInstance()
val keyboardManager by context.keyboardManager()
@@ -216,7 +216,7 @@ fun EmojiPaletteView(
fun pageNumberToCategory(pageNumber: Int): EmojiCategory {
return when {
!emojiHistoryEnabled -> EmojiCategoryValues[pageNumber + 1]
!emojiHistoryEnabled -> EmojiCategoryValues[pageNumber+1]
else -> EmojiCategoryValues[pageNumber]
}
}
@@ -306,7 +306,6 @@ fun EmojiPaletteView(
snapshotFlow { pagerState.currentPage }.collect { page ->
lazyGridState.scrollToItem(0)
activeCategory = pageNumberToCategory(page)
recentlyUsedVersion++
}
}
@@ -538,7 +537,7 @@ private fun EmojiHistoryPopup(
onHistoryAction: () -> Unit,
onDismiss: () -> Unit,
) {
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val scope = rememberCoroutineScope()
val emojiKeyHeight = FlorisImeSizing.smartbarHeight
val context = LocalContext.current

View File

@@ -20,7 +20,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.util.stream.Collectors
import android.content.Context
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.core.Subtype
import dev.patrickgold.florisboard.ime.editor.EditorContent
import dev.patrickgold.florisboard.ime.nlp.EmojiSuggestionCandidate
@@ -41,7 +41,7 @@ import io.github.reactivecircus.cache4k.Cache
class EmojiSuggestionProvider(private val context: Context) : SuggestionProvider {
override val providerId = "org.florisboard.nlp.providers.emoji"
private val prefs by FlorisPreferenceStore
private val prefs by florisPreferenceModel()
private val lettersRegex = "^[A-Za-z]*$".toRegex()
private val cachedEmojiMappings = Cache.Builder().build<FlorisLocale, EmojiDataBySkinTone>()
@@ -70,17 +70,12 @@ class EmojiSuggestionProvider(private val context: Context) : SuggestionProvider
val emojis = cachedEmojiMappings.get(subtype.primaryLocale)?.get(preferredSkinTone) ?: emptyList()
val candidates = withContext(Dispatchers.Default) {
emojis.parallelStream()
.map { emoji ->
val nameWeight = emoji.name.containsWeighted(query, ignoreCase = true)
val keywordWeight = emoji.keywords
.any { it.contains(query, ignoreCase = true) }
.let { if (it) 1.0 else 0.0 }
emoji to (nameWeight * 0.7 + keywordWeight * 0.3)
.filter { emoji ->
emoji.name.contains(query, ignoreCase = true) &&
emoji.keywords.any { it.contains(query, ignoreCase = true) }
}
.sorted { (_, a), (_, b) -> b.compareTo(a) }
.limit(maxCandidateCount.toLong())
.filter { (_, a) -> a > 0 }
.map { (emoji, _) ->
.map { emoji ->
EmojiSuggestionCandidate(
emoji = emoji,
showName = showName,
@@ -133,11 +128,3 @@ class EmojiSuggestionProvider(private val context: Context) : SuggestionProvider
return emojiPartialName
}
}
private fun String.containsWeighted(other: String, ignoreCase: Boolean = false): Double = let { str ->
if (str.contains(other, ignoreCase = ignoreCase)) {
other.length.toDouble() / str.length.toDouble()
} else {
0.0
}
}

View File

@@ -20,7 +20,7 @@ import android.content.Context
import android.os.SystemClock
import android.util.LruCache
import androidx.lifecycle.MutableLiveData
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.clipboardManager
import dev.patrickgold.florisboard.editorInstance
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardItem
@@ -43,8 +43,8 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.florisboard.lib.kotlin.guardedByLock
import org.florisboard.lib.kotlin.collectLatestIn
import org.florisboard.lib.kotlin.guardedByLock
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
import kotlin.properties.Delegates
@@ -54,7 +54,7 @@ private const val BLANK_STR_PATTERN = "^\\s*$"
class NlpManager(context: Context) {
private val blankStrRegex = Regex(BLANK_STR_PATTERN)
private val prefs by FlorisPreferenceStore
private val prefs by florisPreferenceModel()
private val clipboardManager by context.clipboardManager()
private val editorInstance by context.editorInstance()
private val keyboardManager by context.keyboardManager()
@@ -93,13 +93,13 @@ class NlpManager(context: Context) {
clipboardManager.primaryClipFlow.collectLatestIn(scope) {
assembleCandidates()
}
prefs.suggestion.enabled.asFlow().collectLatestIn(scope) {
prefs.suggestion.enabled.observeForever {
assembleCandidates()
}
prefs.clipboard.suggestionEnabled.asFlow().collectLatestIn(scope) {
prefs.clipboard.suggestionEnabled.observeForever {
assembleCandidates()
}
prefs.emoji.suggestionEnabled.asFlow().collectLatestIn(scope) {
prefs.emoji.suggestionEnabled.observeForever {
assembleCandidates()
}
subtypeManager.activeSubtypeFlow.collectLatestIn(scope) { subtype ->
@@ -317,10 +317,8 @@ class NlpManager(context: Context) {
}*/
val isSelection = editorInstance.activeContent.selection.isSelectionMode
val isExpanded = list1.isNullOrEmpty() && list2.isNullOrEmpty() || isSelection
scope.launch {
prefs.smartbar.sharedActionsExpandWithAnimation.set(false)
prefs.smartbar.sharedActionsExpanded.set(isExpanded)
}
prefs.smartbar.sharedActionsExpandWithAnimation.set(false)
prefs.smartbar.sharedActionsExpanded.set(isExpanded)
}
fun addToDebugOverlay(word: String, info: SpellingResult) {

View File

@@ -26,16 +26,14 @@ import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
import androidx.compose.material.icons.filled.ZoomOutMap
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.input.LocalInputFeedbackController
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import org.florisboard.lib.compose.stringRes
import kotlinx.coroutines.launch
import dev.patrickgold.florisboard.lib.compose.stringRes
import org.florisboard.lib.snygg.ui.SnyggColumn
import org.florisboard.lib.snygg.ui.SnyggIcon
import org.florisboard.lib.snygg.ui.SnyggIconButton
@@ -46,8 +44,7 @@ fun RowScope.OneHandedPanel(
panelSide: OneHandedMode,
weight: Float,
) {
val prefs by FlorisPreferenceStore
val scope = rememberCoroutineScope()
val prefs by florisPreferenceModel()
val inputFeedbackController = LocalInputFeedbackController.current
SnyggColumn(
@@ -61,10 +58,8 @@ fun RowScope.OneHandedPanel(
SnyggIconButton(
FlorisImeUi.OneHandedPanelButton.elementName,
onClick = {
scope.launch {
inputFeedbackController.keyPress()
prefs.keyboard.oneHandedModeEnabled.set(false)
}
inputFeedbackController.keyPress()
prefs.keyboard.oneHandedModeEnabled.set(false)
},
modifier = Modifier
.fillMaxWidth()
@@ -79,10 +74,8 @@ fun RowScope.OneHandedPanel(
SnyggIconButton(
FlorisImeUi.OneHandedPanelButton.elementName,
onClick = {
scope.launch {
inputFeedbackController.keyPress()
prefs.keyboard.oneHandedMode.set(panelSide)
}
inputFeedbackController.keyPress()
prefs.keyboard.oneHandedMode.set(panelSide)
},
modifier = Modifier
.weight(1f)

View File

@@ -24,15 +24,12 @@ import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreHoriz
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import dev.patrickgold.florisboard.ime.keyboard.Key
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import org.florisboard.lib.snygg.SnyggQueryAttributes
import org.florisboard.lib.snygg.SnyggSelector
import org.florisboard.lib.snygg.ui.SnyggBox
@@ -41,8 +38,6 @@ import org.florisboard.lib.snygg.ui.SnyggIcon
import org.florisboard.lib.snygg.ui.SnyggRow
import org.florisboard.lib.snygg.ui.SnyggText
val GlobalStateNumPopupsShowing = MutableStateFlow(0)
@Composable
fun PopupBaseBox(
modifier: Modifier = Modifier,
@@ -50,13 +45,6 @@ fun PopupBaseBox(
key: Key,
shouldIndicateExtendedPopups: Boolean,
): Unit = with(LocalDensity.current) {
DisposableEffect(key) {
GlobalStateNumPopupsShowing.update { it + 1 }
onDispose {
GlobalStateNumPopupsShowing.update { it - 1 }
}
}
SnyggBox(
elementName = FlorisImeUi.KeyPopupBox.elementName,
attributes = attributes,

View File

@@ -31,7 +31,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import dev.patrickgold.florisboard.ime.keyboard.KeyboardState
import org.florisboard.lib.compose.conditional
import dev.patrickgold.florisboard.lib.compose.conditional
private val SheetOutOfBoundsBgColorInactive = Color(0x00000000)
private val SheetOutOfBoundsBgColorActive = Color(0x52000000)

View File

@@ -20,6 +20,7 @@ import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.waitForUpOrCancellation
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.width
@@ -39,16 +40,16 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.nlp.ClipboardSuggestionCandidate
import dev.patrickgold.florisboard.ime.nlp.SuggestionCandidate
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.lib.compose.conditional
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
import dev.patrickgold.florisboard.nlpManager
import dev.patrickgold.florisboard.subtypeManager
import dev.patrickgold.jetpref.datastore.model.observeAsState
import org.florisboard.lib.compose.conditional
import org.florisboard.lib.compose.florisHorizontalScroll
import org.florisboard.lib.snygg.SnyggSelector
import org.florisboard.lib.snygg.ui.SnyggBox
import org.florisboard.lib.snygg.ui.SnyggColumn
@@ -61,7 +62,7 @@ val CandidatesRowScrollbarHeight = 2.dp
@Composable
fun CandidatesRow(modifier: Modifier = Modifier) {
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val context = LocalContext.current
val keyboardManager by context.keyboardManager()
val nlpManager by context.nlpManager()

View File

@@ -17,14 +17,12 @@
package dev.patrickgold.florisboard.ime.smartbar
import android.os.Build
import android.view.View
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.rememberScrollState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -35,10 +33,10 @@ import androidx.compose.ui.layout.positionInParent
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.viewinterop.AndroidView
import dev.patrickgold.florisboard.ime.nlp.NlpInlineAutofillSuggestion
import dev.patrickgold.florisboard.ime.popup.GlobalStateNumPopupsShowing
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.lib.compose.florisHorizontalScroll
import dev.patrickgold.florisboard.lib.toIntOffset
import org.florisboard.lib.compose.florisHorizontalScroll
import org.florisboard.lib.snygg.SnyggPropertySet
import org.florisboard.lib.snygg.SnyggSinglePropertySet
import org.florisboard.lib.snygg.ui.rememberSnyggThemeQuery
@@ -59,8 +57,7 @@ fun InlineSuggestionsUi(
modifier: Modifier = Modifier,
) {
val scrollState = rememberScrollState()
val numPopupsShowing by GlobalStateNumPopupsShowing.collectAsState()
val isZOrderedOnTop = numPopupsShowing == 0
val almostEmptyRect = remember { android.graphics.Rect(0, 0, 1, 1) }
Row(
modifier
@@ -81,14 +78,19 @@ fun InlineSuggestionsUi(
modifier = Modifier.onGloballyPositioned { chipPos = it.positionInParent().toIntOffset() },
factory = { inlineSuggestion.view },
update = { view ->
view.isZOrderedOnTop = isZOrderedOnTop
view.clipBounds = android.graphics.Rect(
(xMin - chipPos.x).coerceAtLeast(0),
0,
(xMax - chipPos.x).coerceAtMost(view.width),
view.height,
)
view.visibility = if (view.clipBounds.isEmpty) View.INVISIBLE else View.VISIBLE
// The empty rect is a workaround for a bug (I suppose) where an empty rect causes
// no clipping, but we actually want to completely hide the view.
// Thus we just show the topmost pixel of the view, which due to the round shape
// of the theme should be transparent anyways.
if (view.clipBounds.isEmpty) {
view.clipBounds = almostEmptyRect
}
}
)
}

View File

@@ -24,6 +24,7 @@ import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.absoluteOffset
@@ -42,19 +43,16 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.isUnspecified
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
import dev.patrickgold.florisboard.ime.nlp.NlpInlineAutofill
import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionButton
@@ -62,12 +60,12 @@ import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionsRow
import dev.patrickgold.florisboard.ime.smartbar.quickaction.ToggleOverflowPanelAction
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.lib.compose.horizontalTween
import dev.patrickgold.florisboard.lib.compose.verticalTween
import dev.patrickgold.florisboard.nlpManager
import dev.patrickgold.jetpref.datastore.model.observeAsState
import kotlinx.coroutines.launch
import dev.patrickgold.jetpref.datastore.ui.vectorResource
import org.florisboard.lib.android.AndroidVersion
import org.florisboard.lib.compose.horizontalTween
import org.florisboard.lib.compose.verticalTween
import org.florisboard.lib.snygg.ui.SnyggBox
import org.florisboard.lib.snygg.ui.SnyggColumn
import org.florisboard.lib.snygg.ui.SnyggIcon
@@ -91,7 +89,7 @@ private val NoAnimationTween = tween<Float>(0)
@Composable
fun Smartbar() {
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val smartbarEnabled by prefs.smartbar.enabled.observeAsState()
val extendedActionsPlacement by prefs.smartbar.extendedActionsPlacement.observeAsState()
@@ -140,11 +138,10 @@ fun Smartbar() {
@Composable
private fun SmartbarMainRow(modifier: Modifier = Modifier) {
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val context = LocalContext.current
val keyboardManager by context.keyboardManager()
val nlpManager by context.nlpManager()
val scope = rememberCoroutineScope()
val inlineSuggestions by NlpInlineAutofill.suggestions.collectAsState()
LaunchedEffect(inlineSuggestions) {
@@ -167,9 +164,7 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
if (/* was */ sharedActionsExpanded) {
keyboardManager.activeState.isActionsOverflowVisible = false
}
scope.launch {
prefs.smartbar.sharedActionsExpanded.set(!sharedActionsExpanded)
}
prefs.smartbar.sharedActionsExpanded.set(!sharedActionsExpanded)
},
modifier = Modifier.sizeIn(maxHeight = FlorisImeSizing.smartbarHeight).aspectRatio(1f)
) {
@@ -187,7 +182,7 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
} else {
Icons.AutoMirrored.Default.KeyboardArrowRight
}
val incognitoIcon = ImageVector.vectorResource(id = R.drawable.ic_incognito)
val incognitoIcon = vectorResource(id = R.drawable.ic_incognito)
val incognitoDisplayMode = prefs.keyboard.incognitoDisplayMode.observeAsState()
val isIncognitoMode = keyboardManager.activeState.isIncognitoMode
val icon = if (isIncognitoMode) {
@@ -249,9 +244,7 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
if (/* was */ extendedActionsExpanded) {
keyboardManager.activeState.isActionsOverflowVisible = false
}
scope.launch {
prefs.smartbar.extendedActionsExpanded.set(!extendedActionsExpanded)
}
prefs.smartbar.extendedActionsExpanded.set(!extendedActionsExpanded)
},
modifier = Modifier.sizeIn(maxHeight = FlorisImeSizing.smartbarHeight).aspectRatio(1f)
) {
@@ -311,9 +304,7 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
SideEffect {
if (!shouldAnimate) {
scope.launch {
prefs.smartbar.sharedActionsExpandWithAnimation.set(true)
}
prefs.smartbar.sharedActionsExpandWithAnimation.set(true)
}
}
@@ -368,7 +359,7 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
@Composable
private fun SmartbarSecondaryRow(modifier: Modifier = Modifier) {
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val smartbarLayout by prefs.smartbar.layout.observeAsState()
val secondaryRowStyle = rememberSnyggThemeQuery(FlorisImeUi.SmartbarExtendedActionsRow.elementName)
val windowStyle = rememberSnyggThemeQuery(FlorisImeUi.Window.elementName)

View File

@@ -25,9 +25,9 @@ import dev.patrickgold.florisboard.ime.keyboard.KeyData
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.lib.compose.stringRes
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import org.florisboard.lib.compose.stringRes
@Serializable
sealed class QuickAction {
@@ -87,7 +87,6 @@ fun QuickAction.computeDisplayName(evaluator: ComputingEvaluator): String {
KeyCode.CLIPBOARD_CUT -> R.string.quick_action__clipboard_cut
KeyCode.CLIPBOARD_PASTE -> R.string.quick_action__clipboard_paste
KeyCode.CLIPBOARD_SELECT_ALL -> R.string.quick_action__clipboard_select_all
KeyCode.FORWARD_DELETE -> R.string.quick_action__forward_delete
KeyCode.IME_UI_MODE_CLIPBOARD -> R.string.quick_action__ime_ui_mode_clipboard
KeyCode.IME_UI_MODE_MEDIA -> R.string.quick_action__ime_ui_mode_media
KeyCode.LANGUAGE_SWITCH -> R.string.quick_action__language_switch

View File

@@ -86,7 +86,6 @@ data class QuickActionArrangement(
QuickAction.InsertKey(TextKeyData.CLIPBOARD_PASTE),
QuickAction.InsertKey(TextKeyData.CLIPBOARD_SELECT_ALL),
QuickAction.InsertKey(TextKeyData.LANGUAGE_SWITCH),
QuickAction.InsertKey(TextKeyData.FORWARD_DELETE),
),
hiddenActions = listOf(
),

View File

@@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyGridItemInfo
@@ -51,16 +52,16 @@ import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.toSize
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
import dev.patrickgold.florisboard.ime.text.key.KeyCode
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.florisboard.lib.compose.stringRes
import dev.patrickgold.florisboard.lib.toIntOffset
import org.florisboard.lib.compose.stringRes
import kotlinx.coroutines.runBlocking
import org.florisboard.lib.snygg.ui.SnyggBox
import org.florisboard.lib.snygg.ui.SnyggButton
import org.florisboard.lib.snygg.ui.SnyggColumn
import org.florisboard.lib.snygg.ui.SnyggIcon
import org.florisboard.lib.snygg.ui.SnyggIconButton
@@ -73,7 +74,7 @@ private val DragMarkerAction = QuickAction.InsertKey(TextKeyData(code = KeyCode.
@Composable
fun QuickActionsEditorPanel() {
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val context = LocalContext.current
val keyboardManager by context.keyboardManager()
@@ -234,9 +235,7 @@ fun QuickActionsEditorPanel() {
dynamicActions.filter { it != NoopAction && it != DragMarkerAction },
hiddenActions.filter { it != NoopAction && it != DragMarkerAction },
)
runBlocking {
prefs.smartbar.actionArrangement.set(newActionArrangement)
}
prefs.smartbar.actionArrangement.set(newActionArrangement)
if (keyboardManager.activeState.isActionsEditorVisible) {
keyboardManager.activeState.isActionsEditorVisible = false
}

View File

@@ -30,19 +30,19 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
import dev.patrickgold.florisboard.keyboardManager
import dev.patrickgold.jetpref.datastore.model.observeAsState
import org.florisboard.lib.compose.stringRes
import org.florisboard.lib.snygg.ui.SnyggBox
import dev.patrickgold.florisboard.lib.compose.stringRes
import org.florisboard.lib.snygg.ui.SnyggButton
import dev.patrickgold.jetpref.datastore.model.observeAsState
import org.florisboard.lib.snygg.ui.SnyggBox
import org.florisboard.lib.snygg.ui.SnyggText
@Composable
fun QuickActionsOverflowPanel() {
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val context = LocalContext.current
val keyboardManager by context.keyboardManager()

View File

@@ -16,7 +16,6 @@
package dev.patrickgold.florisboard.ime.smartbar.quickaction
import android.annotation.SuppressLint
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxSize
@@ -29,7 +28,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.smartbar.SmartbarLayout
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
import dev.patrickgold.florisboard.keyboardManager
@@ -38,13 +37,12 @@ import org.florisboard.lib.snygg.ui.SnyggRow
internal val ToggleOverflowPanelAction = QuickAction.InsertKey(TextKeyData.TOGGLE_ACTIONS_OVERFLOW)
@SuppressLint("UnusedBoxWithConstraintsScope")
@Composable
fun QuickActionsRow(
elementName: String,
modifier: Modifier = Modifier,
) = with(LocalDensity.current) {
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val context = LocalContext.current
val keyboardManager by context.keyboardManager()

View File

@@ -31,7 +31,7 @@ import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.LayoutDirection
import dev.patrickgold.florisboard.R
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.smartbar.IncognitoDisplayMode
import dev.patrickgold.florisboard.ime.smartbar.InlineSuggestionsStyleCache
import dev.patrickgold.florisboard.ime.smartbar.Smartbar
@@ -49,7 +49,7 @@ fun TextInputLayout(
val context = LocalContext.current
val keyboardManager by context.keyboardManager()
val prefs by FlorisPreferenceStore
val prefs by florisPreferenceModel()
val state by keyboardManager.activeState.collectAsState()
val evaluator by keyboardManager.activeEvaluator.collectAsState()

View File

@@ -17,7 +17,7 @@
package dev.patrickgold.florisboard.ime.text.gestures
import android.content.Context
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
import dev.patrickgold.florisboard.app.florisPreferenceModel
import dev.patrickgold.florisboard.ime.nlp.WordSuggestionCandidate
import dev.patrickgold.florisboard.ime.text.keyboard.TextKey
import dev.patrickgold.florisboard.keyboardManager
@@ -39,7 +39,7 @@ class GlideTypingManager(context: Context) : GlideTypingGesture.Listener {
private const val MAX_SUGGESTION_COUNT = 8
}
private val prefs by FlorisPreferenceStore
private val prefs by florisPreferenceModel()
private val keyboardManager by context.keyboardManager()
private val nlpManager by context.nlpManager()
private val subtypeManager by context.subtypeManager()

Some files were not shown because too many files have changed in this diff Show More