Compare commits
42 Commits
flest
...
fix/use-co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1252b3b2c3 | ||
|
|
8d9bacbefd | ||
|
|
a1031acd8e | ||
|
|
7a3af3eb54 | ||
|
|
7a433e9860 | ||
|
|
f687873cc5 | ||
|
|
c6a84fd324 | ||
|
|
dcc490cc9d | ||
|
|
d7b52cd489 | ||
|
|
fc7700395c | ||
|
|
9f07702852 | ||
|
|
f111544d81 | ||
|
|
fc09bb74c0 | ||
|
|
674bdccc40 | ||
|
|
3e69dddaa9 | ||
|
|
3c175bc60d | ||
|
|
83150e17ab | ||
|
|
2d6646e674 | ||
|
|
cc9511edca | ||
|
|
f25df31147 | ||
|
|
0ccb0c33b4 | ||
|
|
7ea4e8c827 | ||
|
|
93c76640f4 | ||
|
|
c2c874a9e0 | ||
|
|
b14877da8c | ||
|
|
2fb1fdb281 | ||
|
|
52646ecba1 | ||
|
|
7905dae807 | ||
|
|
e73ac5f96f | ||
|
|
15039fb020 | ||
|
|
c035e19cfc | ||
|
|
8d30403676 | ||
|
|
2886a24fcc | ||
|
|
352c98205c | ||
|
|
18c76b7195 | ||
|
|
ea0712225a | ||
|
|
1132eaf59b | ||
|
|
cba85f1c0e | ||
|
|
15dba9f33a | ||
|
|
3859528120 | ||
|
|
046d253382 | ||
|
|
c55a87862f |
20
README.md
20
README.md
@@ -29,7 +29,7 @@ fully respecting your privacy. Currently in early-beta state.
|
||||
<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-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))
|
||||
**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))
|
||||
|
||||
</p>
|
||||
<p>
|
||||
@@ -47,7 +47,7 @@ fully respecting your privacy. Currently in early-beta state.
|
||||
<p><a href="https://apt.izzysoft.de/fdroid/index/apk/dev.patrickgold.florisboard.beta"><img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" height="64" alt="IzzySoft repo badge"></a></p>
|
||||
<p>
|
||||
|
||||
**Google Play**: Join the [FlorisBoard Test Group](https://groups.google.com/g/florisboard-public-alpha-test), then visit the [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-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))
|
||||
|
||||
</p>
|
||||
<p>
|
||||
@@ -96,6 +96,22 @@ 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)
|
||||
|
||||
@@ -200,17 +200,20 @@ 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"))
|
||||
|
||||
@@ -608,6 +608,12 @@
|
||||
"authors": [ "waelwindows" ],
|
||||
"direction": "ltr"
|
||||
},
|
||||
{
|
||||
"id": "czech",
|
||||
"label": "Czech",
|
||||
"authors": [ "bmondream" ],
|
||||
"direction": "ltr"
|
||||
},
|
||||
{
|
||||
"id": "devanagari",
|
||||
"label": "Devanagari",
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
[
|
||||
[
|
||||
{ "$": "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" }
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -34,9 +34,7 @@
|
||||
]
|
||||
},
|
||||
"h": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 7717, "label": "ḥ" }
|
||||
]
|
||||
"main": { "$": "auto_text_key", "code": 7717, "label": "ḥ" }
|
||||
},
|
||||
"i": {
|
||||
"main": { "$": "auto_text_key", "code": 237, "label": "í" },
|
||||
@@ -49,13 +47,11 @@
|
||||
]
|
||||
},
|
||||
"l": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 7735, "label": "ḷ" }
|
||||
]
|
||||
"main": { "$": "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": "ń" }
|
||||
]
|
||||
},
|
||||
@@ -73,9 +69,7 @@
|
||||
]
|
||||
},
|
||||
"r": {
|
||||
"relevant": [
|
||||
{ "$": "auto_text_key", "code": 691, "label": "ʳ" }
|
||||
]
|
||||
"main": { "$": "auto_text_key", "code": 691, "label": "ʳ" }
|
||||
},
|
||||
"s": {
|
||||
"relevant": [
|
||||
@@ -112,4 +106,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -23,8 +23,10 @@ import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Handler
|
||||
import android.util.Log
|
||||
import androidx.core.os.UserManagerCompat
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.clipboard.ClipboardManager
|
||||
import dev.patrickgold.florisboard.ime.core.SubtypeManager
|
||||
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
|
||||
@@ -40,7 +42,11 @@ import dev.patrickgold.florisboard.lib.devtools.Flog
|
||||
import dev.patrickgold.florisboard.lib.devtools.LogTopic
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogError
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionManager
|
||||
import dev.patrickgold.jetpref.datastore.JetPref
|
||||
import dev.patrickgold.jetpref.datastore.runtime.initAndroid
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
|
||||
import org.florisboard.lib.kotlin.tryOrNull
|
||||
import org.florisboard.libnative.dummyAdd
|
||||
@@ -63,8 +69,9 @@ class FlorisApplication : Application() {
|
||||
}
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val mainHandler by lazy { Handler(mainLooper) }
|
||||
private val scope = CoroutineScope(Dispatchers.Default)
|
||||
val preferenceStoreLoaded = MutableStateFlow(false)
|
||||
|
||||
val cacheManager = lazy { CacheManager(this) }
|
||||
val clipboardManager = lazy { ClipboardManager(this) }
|
||||
@@ -80,7 +87,6 @@ class FlorisApplication : Application() {
|
||||
super.onCreate()
|
||||
FlorisApplicationReference = WeakReference(this)
|
||||
try {
|
||||
JetPref.configure(saveIntervalMs = 500)
|
||||
Flog.install(
|
||||
context = this,
|
||||
isFloggingEnabled = BuildConfig.DEBUG,
|
||||
@@ -108,7 +114,14 @@ class FlorisApplication : Application() {
|
||||
|
||||
fun init() {
|
||||
cacheDir?.deleteContentsRecursively()
|
||||
prefs.initializeBlocking(this)
|
||||
scope.launch {
|
||||
val result = FlorisPreferenceStore.initAndroid(
|
||||
context = this@FlorisApplication,
|
||||
datastoreName = FlorisPreferenceModel.NAME,
|
||||
)
|
||||
Log.i("PREFS", result.toString())
|
||||
preferenceStoreLoaded.value = true
|
||||
}
|
||||
extensionManager.value.init()
|
||||
clipboardManager.value.initializeForContext(this)
|
||||
DictionaryManager.init(this)
|
||||
|
||||
@@ -75,8 +75,8 @@ import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import dev.patrickgold.florisboard.app.FlorisAppActivity
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.devtools.DevtoolsOverlay
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.ImeUiMode
|
||||
import dev.patrickgold.florisboard.ime.clipboard.ClipboardInputLayout
|
||||
import dev.patrickgold.florisboard.ime.core.SelectSubtypePanel
|
||||
@@ -102,7 +102,6 @@ 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
|
||||
@@ -118,12 +117,14 @@ import org.florisboard.lib.android.AndroidInternalR
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.isOrientationLandscape
|
||||
import org.florisboard.lib.android.isOrientationPortrait
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.android.systemServiceOrNull
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
import org.florisboard.lib.compose.ProvideLocalizedResources
|
||||
import org.florisboard.lib.kotlin.collectIn
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggButton
|
||||
import org.florisboard.lib.snygg.ui.SnyggRow
|
||||
import org.florisboard.lib.snygg.ui.SnyggSurfaceView
|
||||
import org.florisboard.lib.snygg.ui.SnyggText
|
||||
import org.florisboard.lib.snygg.ui.rememberSnyggThemeQuery
|
||||
|
||||
@@ -246,12 +247,12 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
}
|
||||
}
|
||||
}
|
||||
ims.showShortToast("Failed to find voice IME, do you have one installed?")
|
||||
ims.showShortToastSync("Failed to find voice IME, do you have one installed?")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val editorInstance by editorInstance()
|
||||
private val keyboardManager by keyboardManager()
|
||||
private val nlpManager by nlpManager()
|
||||
@@ -277,21 +278,21 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
super.onCreate()
|
||||
FlorisImeServiceReference = WeakReference(this)
|
||||
WindowCompat.setDecorFitsSystemWindows(window.window!!, false)
|
||||
subtypeManager.activeSubtypeFlow.collectLatestIn(lifecycleScope) { subtype ->
|
||||
subtypeManager.activeSubtypeFlow.collectIn(lifecycleScope) { subtype ->
|
||||
val config = Configuration(resources.configuration)
|
||||
if (prefs.localization.displayKeyboardLabelsInSubtypeLanguage.get()) {
|
||||
config.setLocale(subtype.primaryLocale.base)
|
||||
}
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
prefs.localization.displayKeyboardLabelsInSubtypeLanguage.observeForever { shouldSync ->
|
||||
prefs.localization.displayKeyboardLabelsInSubtypeLanguage.asFlow().collectIn(lifecycleScope) { shouldSync ->
|
||||
val config = Configuration(resources.configuration)
|
||||
if (shouldSync) {
|
||||
config.setLocale(subtypeManager.activeSubtype.primaryLocale.base)
|
||||
}
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
prefs.physicalKeyboard.showOnScreenKeyboard.observeForever {
|
||||
prefs.physicalKeyboard.showOnScreenKeyboard.asFlow().collectIn(lifecycleScope) {
|
||||
updateInputViewShown()
|
||||
}
|
||||
@Suppress("DEPRECATION") // We do not retrieve the wallpaper but only listen to changes
|
||||
@@ -408,7 +409,6 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
flogInfo(LogTopic.IMS_EVENTS)
|
||||
}
|
||||
isWindowShown = true
|
||||
themeManager.updateActiveTheme()
|
||||
inputFeedbackController.updateSystemPrefsState()
|
||||
}
|
||||
|
||||
@@ -572,7 +572,10 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
|
||||
@Composable
|
||||
private fun ImeUiWrapper() {
|
||||
ProvideLocalizedResources(resourcesContext) {
|
||||
ProvideLocalizedResources(
|
||||
resourcesContext,
|
||||
appName = R.string.app_name,
|
||||
) {
|
||||
ProvideKeyboardRowBaseHeight {
|
||||
CompositionLocalProvider(LocalInputFeedbackController provides inputFeedbackController) {
|
||||
FlorisImeTheme {
|
||||
@@ -617,9 +620,14 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
clickAndSemanticsModifier = Modifier
|
||||
// Do not remove below line or touch input may get stuck
|
||||
.pointerInteropFilter { false },
|
||||
supportsBackgroundImage = true,
|
||||
supportsBackgroundImage = false,
|
||||
allowClip = false,
|
||||
) {
|
||||
SnyggSurfaceView(
|
||||
elementName = FlorisImeUi.Window.elementName,
|
||||
attributes = attributes,
|
||||
modifier = Modifier.matchParentSize(),
|
||||
)
|
||||
val configuration = LocalConfiguration.current
|
||||
val bottomOffset by if (configuration.isOrientationPortrait()) {
|
||||
prefs.keyboard.bottomOffsetPortrait
|
||||
@@ -671,10 +679,13 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean =
|
||||
if (keyboardManager.onHardwareKeyDown(keyCode, event)) true
|
||||
else super.onKeyDown(keyCode, event)
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
return keyboardManager.onHardwareKeyDown(keyCode, event) || 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 {
|
||||
@@ -709,7 +720,11 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val state by keyboardManager.activeState.collectAsState()
|
||||
|
||||
ProvideLocalizedResources(resourcesContext, forceLayoutDirection = LayoutDirection.Ltr) {
|
||||
ProvideLocalizedResources(
|
||||
resourcesContext,
|
||||
appName = R.string.app_name,
|
||||
forceLayoutDirection = LayoutDirection.Ltr,
|
||||
) {
|
||||
FlorisImeTheme {
|
||||
BottomSheetHostUi(
|
||||
isShowing = state.isBottomSheetShowing() || state.isSubtypeSelectionShowing(),
|
||||
@@ -761,7 +776,11 @@ class FlorisImeService : LifecycleInputMethodService() {
|
||||
|
||||
@Composable
|
||||
fun Content() {
|
||||
ProvideLocalizedResources(resourcesContext, forceLayoutDirection = LayoutDirection.Ltr) {
|
||||
ProvideLocalizedResources(
|
||||
resourcesContext,
|
||||
appName = R.string.app_name,
|
||||
forceLayoutDirection = LayoutDirection.Ltr,
|
||||
) {
|
||||
FlorisImeTheme {
|
||||
val activeEditorInfo by editorInstance.activeInfoFlow.collectAsState()
|
||||
SnyggBox(FlorisImeUi.ExtractedLandscapeInputLayout.elementName) {
|
||||
|
||||
@@ -20,7 +20,7 @@ import android.service.textservice.SpellCheckerService
|
||||
import android.view.textservice.SentenceSuggestionsInfo
|
||||
import android.view.textservice.SuggestionsInfo
|
||||
import android.view.textservice.TextInfo
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.dictionary.DictionaryManager
|
||||
import dev.patrickgold.florisboard.ime.nlp.SpellingLanguageMode
|
||||
@@ -33,7 +33,7 @@ import kotlinx.coroutines.runBlocking
|
||||
import org.florisboard.lib.kotlin.map
|
||||
|
||||
class FlorisSpellCheckerService : SpellCheckerService() {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val dictionaryManager get() = DictionaryManager.default()
|
||||
private val nlpManager by nlpManager()
|
||||
private val subtypeManager by subtypeManager()
|
||||
|
||||
@@ -20,6 +20,7 @@ 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
|
||||
@@ -53,11 +54,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.JetPref
|
||||
import dev.patrickgold.jetpref.datastore.annotations.Preferences
|
||||
import dev.patrickgold.jetpref.datastore.jetprefDataStoreOf
|
||||
import dev.patrickgold.jetpref.datastore.model.LocalTime
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceData
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceMigrationEntry
|
||||
@@ -69,9 +70,14 @@ import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.android.isOrientationPortrait
|
||||
import org.florisboard.lib.color.DEFAULT_GREEN
|
||||
|
||||
fun florisPreferenceModel() = JetPref.getOrCreatePreferenceModel(AppPrefs::class, ::AppPrefs)
|
||||
val FlorisPreferenceStore = jetprefDataStoreOf(FlorisPreferenceModel::class)
|
||||
|
||||
@Preferences
|
||||
abstract class FlorisPreferenceModel : PreferenceModel() {
|
||||
companion object {
|
||||
const val NAME = "florisboard-app-prefs"
|
||||
}
|
||||
|
||||
class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
val clipboard = Clipboard()
|
||||
inner class Clipboard {
|
||||
val useInternalClipboard = boolean(
|
||||
@@ -758,11 +764,11 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
},
|
||||
serializer = ColorPreferenceSerializer,
|
||||
)
|
||||
val sunriseTime = time(
|
||||
val sunriseTime = localTime(
|
||||
key = "theme__sunrise_time",
|
||||
default = LocalTime(6, 0),
|
||||
)
|
||||
val sunsetTime = time(
|
||||
val sunsetTime = localTime(
|
||||
key = "theme__sunset_time",
|
||||
default = LocalTime(18, 0),
|
||||
)
|
||||
@@ -854,6 +860,11 @@ class AppPrefs : PreferenceModel("florisboard-app-prefs") {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -39,26 +39,30 @@ 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"),
|
||||
@@ -73,7 +77,8 @@ val LocalNavController = staticCompositionLocalOf<NavController> {
|
||||
}
|
||||
|
||||
class FlorisAppActivity : ComponentActivity() {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val appContext by appContext()
|
||||
private val cacheManager by cacheManager()
|
||||
private var appTheme by mutableStateOf(AppTheme.AUTO)
|
||||
private var showAppIcon = true
|
||||
@@ -83,40 +88,43 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// Splash screen should be installed before calling super.onCreate()
|
||||
installSplashScreen().apply {
|
||||
setKeepOnScreenCondition { !prefs.datastoreReadyStatus.get() }
|
||||
setKeepOnScreenCondition { !appContext.preferenceStoreLoaded.value }
|
||||
}
|
||||
super.onCreate(savedInstanceState)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
prefs.other.settingsTheme.observe(this) {
|
||||
prefs.other.settingsTheme.asFlow().collectIn(lifecycleScope) {
|
||||
appTheme = it
|
||||
}
|
||||
prefs.other.settingsLanguage.observe(this) {
|
||||
prefs.other.settingsLanguage.asFlow().collectIn(lifecycleScope) {
|
||||
val config = Configuration(resources.configuration)
|
||||
val locale = if (it == "auto") FlorisLocale.default() else FlorisLocale.fromTag(it)
|
||||
config.setLocale(locale.base)
|
||||
resourcesContext = createConfigurationContext(config)
|
||||
}
|
||||
if (AndroidVersion.ATMOST_API28_P) {
|
||||
prefs.other.showAppIcon.observe(this) {
|
||||
prefs.other.showAppIcon.asFlow().collectIn(lifecycleScope) {
|
||||
showAppIcon = it
|
||||
}
|
||||
}
|
||||
|
||||
//Check if android 13+ is running and the NotificationPermission is not set
|
||||
if (AndroidVersion.ATLEAST_API33_T &&
|
||||
prefs.internal.notificationPermissionState.get() == NotificationPermissionState.NOT_SET
|
||||
) {
|
||||
// update pref value to show the setup screen again again
|
||||
prefs.internal.isImeSetUp.set(false)
|
||||
}
|
||||
|
||||
// We defer the setContent call until the datastore model is loaded, until then the splash screen stays drawn
|
||||
prefs.datastoreReadyStatus.observe(this) { isModelLoaded ->
|
||||
if (!isModelLoaded) return@observe
|
||||
val isModelLoaded = AtomicBoolean(false)
|
||||
appContext.preferenceStoreLoaded.collectIn(lifecycleScope) { loaded ->
|
||||
if (!loaded || isModelLoaded.getAndSet(true)) return@collectIn
|
||||
// Check if android 13+ is running and the NotificationPermission is not set
|
||||
if (AndroidVersion.ATLEAST_API33_T &&
|
||||
prefs.internal.notificationPermissionState.get() == NotificationPermissionState.NOT_SET
|
||||
) {
|
||||
// update pref value to show the setup screen again
|
||||
prefs.internal.isImeSetUp.set(false)
|
||||
}
|
||||
AppVersionUtils.updateVersionOnInstallAndLastUse(this, prefs)
|
||||
setContent {
|
||||
ProvideLocalizedResources(resourcesContext) {
|
||||
ProvideLocalizedResources(
|
||||
resourcesContext,
|
||||
appName = R.string.app_name,
|
||||
) {
|
||||
FlorisAppTheme(theme = appTheme) {
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
AppContent()
|
||||
@@ -189,7 +197,7 @@ class FlorisAppActivity : ComponentActivity() {
|
||||
Routes.AppNavHost(
|
||||
modifier = Modifier.weight(1.0f),
|
||||
navController = navController,
|
||||
startDestination = if (isImeSetUp) Routes.Settings.Home else Routes.Setup.Screen,
|
||||
startDestination = if (isImeSetUp) Routes.Settings.Home::class else Routes.Setup.Screen::class,
|
||||
)
|
||||
PreviewKeyboardField(previewFieldController)
|
||||
}
|
||||
|
||||
@@ -17,12 +17,17 @@
|
||||
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
|
||||
@@ -30,6 +35,7 @@ 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
|
||||
@@ -70,113 +76,185 @@ 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 org.florisboard.lib.kotlin.curlyFormat
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("FunctionName", "ConstPropertyName")
|
||||
object Routes {
|
||||
object Setup {
|
||||
const val Screen = "setup"
|
||||
@Serializable
|
||||
object Screen
|
||||
}
|
||||
|
||||
object Settings {
|
||||
const val Home = "settings"
|
||||
@Serializable
|
||||
@Deeplink("settings/home")
|
||||
object Home
|
||||
|
||||
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")
|
||||
object Localization
|
||||
|
||||
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/select-locale")
|
||||
object SelectLocale
|
||||
|
||||
const val Keyboard = "settings/keyboard"
|
||||
const val InputFeedback = "settings/keyboard/input-feedback"
|
||||
@Serializable
|
||||
@Deeplink("settings/localization/language-pack-manage")
|
||||
data class LanguagePackManager(val action: LanguagePackManagerScreenAction)
|
||||
|
||||
const val Smartbar = "settings/smartbar"
|
||||
@Serializable
|
||||
@Deeplink("settings/localization/subtype/add")
|
||||
object SubtypeAdd
|
||||
|
||||
const val Typing = "settings/typing"
|
||||
@Serializable
|
||||
@Deeplink("settings/localization/subtype/edit")
|
||||
data class SubtypeEdit(val id: Long)
|
||||
|
||||
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")
|
||||
object Theme
|
||||
|
||||
const val Gestures = "settings/gestures"
|
||||
@Serializable
|
||||
@Deeplink("settings/theme/manage")
|
||||
data class ThemeManager(val action: ThemeManagerScreenAction)
|
||||
|
||||
const val Clipboard = "settings/clipboard"
|
||||
@Serializable
|
||||
@Deeplink("settings/keyboard")
|
||||
object Keyboard
|
||||
|
||||
const val Media = "settings/media"
|
||||
@Serializable
|
||||
@Deeplink("settings/keyboard/input-feedback")
|
||||
object InputFeedback
|
||||
|
||||
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/smartbar")
|
||||
object Smartbar
|
||||
|
||||
const val About = "settings/about"
|
||||
const val ProjectLicense = "settings/about/project-license"
|
||||
const val ThirdPartyLicenses = "settings/about/third-party-licenses"
|
||||
@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
|
||||
}
|
||||
|
||||
object Devtools {
|
||||
const val Home = "devtools"
|
||||
@Serializable
|
||||
@Deeplink("devtools")
|
||||
object Home
|
||||
|
||||
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/locales")
|
||||
object AndroidLocales
|
||||
|
||||
const val ExportDebugLog = "export-debug-log"
|
||||
@Serializable
|
||||
@Deeplink("devtools/android/settings")
|
||||
data class AndroidSettings(val name: String)
|
||||
|
||||
@Serializable
|
||||
@Deeplink("export-debug-log")
|
||||
object ExportDebugLog
|
||||
}
|
||||
|
||||
object Ext {
|
||||
const val Home = "ext"
|
||||
@Serializable
|
||||
@Deeplink("ext")
|
||||
object Home
|
||||
|
||||
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/list")
|
||||
data class List(val type: ExtensionListScreenType, val showUpdate: Boolean? = 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/edit")
|
||||
data class Edit(val id: String, @SerialName("create") val serialType: String? = null)
|
||||
|
||||
const val Export = "ext/export/{id}"
|
||||
fun Export(id: String) = Export.curlyFormat("id" to id)
|
||||
@Serializable
|
||||
@Deeplink("ext/export")
|
||||
data class Export(val id: String)
|
||||
|
||||
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/import")
|
||||
data class Import(val type: ExtensionImportScreenType, val uuid: String? = null)
|
||||
|
||||
const val View = "ext/view/{id}"
|
||||
fun View(id: String) = View.curlyFormat("id" to id)
|
||||
@Serializable
|
||||
@Deeplink("ext/view")
|
||||
data class View(val id: String)
|
||||
|
||||
const val CheckUpdates = "ext/check-updates"
|
||||
@Serializable
|
||||
@Deeplink("ext/check-updates")
|
||||
object CheckUpdates
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AppNavHost(
|
||||
modifier: Modifier,
|
||||
navController: NavHostController,
|
||||
startDestination: String,
|
||||
startDestination: KClass<*>,
|
||||
) {
|
||||
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,
|
||||
@@ -187,109 +265,103 @@ object Routes {
|
||||
exitTransition = {
|
||||
slideOut { IntOffset(-it.width, 0) } + fadeOut()
|
||||
},
|
||||
popEnterTransition = {
|
||||
slideIn { IntOffset(-it.width, 0) } + fadeIn()
|
||||
},
|
||||
popEnterTransition = { EnterTransition.None },
|
||||
popExitTransition = {
|
||||
slideOut { IntOffset(it.width, 0) } + fadeOut()
|
||||
}
|
||||
scaleOut(
|
||||
targetScale = 0.85F,
|
||||
transformOrigin = TransformOrigin(pivotFractionX = 0.8f, pivotFractionY = 0.5f)
|
||||
) + fadeOut(spring(stiffness = Spring.StiffnessMedium))
|
||||
},
|
||||
) {
|
||||
composable(Setup.Screen) { SetupScreen() }
|
||||
composable<Setup.Screen> { SetupScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.Home) { HomeScreen() }
|
||||
composableWithDeepLink(Settings.Home::class) { HomeScreen() }
|
||||
|
||||
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.Localization::class) { LocalizationScreen() }
|
||||
composableWithDeepLink(Settings.SelectLocale::class) { SelectLocaleScreen() }
|
||||
composableWithDeepLink(Settings.LanguagePackManager::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Settings.LanguagePackManager>()
|
||||
LanguagePackManagerScreen(payload.action)
|
||||
}
|
||||
composableWithDeepLink(Settings.SubtypeAdd) { SubtypeEditorScreen(null) }
|
||||
composableWithDeepLink(Settings.SubtypeEdit) { navBackStack ->
|
||||
val id = navBackStack.arguments?.getString("id")?.toLongOrNull()
|
||||
SubtypeEditorScreen(id)
|
||||
composableWithDeepLink(Settings.SubtypeAdd::class) { SubtypeEditorScreen(null) }
|
||||
composableWithDeepLink(Settings.SubtypeEdit::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Settings.SubtypeEdit>()
|
||||
SubtypeEditorScreen(payload.id)
|
||||
}
|
||||
|
||||
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.Theme::class) { ThemeScreen() }
|
||||
composableWithDeepLink(Settings.ThemeManager::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Settings.ThemeManager>()
|
||||
ThemeManagerScreen(payload.action)
|
||||
}
|
||||
|
||||
composableWithDeepLink(Settings.Keyboard) { KeyboardScreen() }
|
||||
composableWithDeepLink(Settings.InputFeedback) { InputFeedbackScreen() }
|
||||
composableWithDeepLink(Settings.Keyboard::class) { KeyboardScreen() }
|
||||
composableWithDeepLink(Settings.InputFeedback::class) { InputFeedbackScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.Smartbar) { SmartbarScreen() }
|
||||
composableWithDeepLink(Settings.Smartbar::class) { SmartbarScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.Typing) { TypingScreen() }
|
||||
composableWithDeepLink(Settings.Typing::class) { TypingScreen() }
|
||||
|
||||
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.Dictionary::class) { DictionaryScreen() }
|
||||
composableWithDeepLink(Settings.UserDictionary::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Settings.UserDictionary>()
|
||||
UserDictionaryScreen(payload.type)
|
||||
}
|
||||
|
||||
composableWithDeepLink(Settings.Gestures) { GesturesScreen() }
|
||||
composableWithDeepLink(Settings.Gestures::class) { GesturesScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.Clipboard) { ClipboardScreen() }
|
||||
composableWithDeepLink(Settings.Clipboard::class) { ClipboardScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.Media) { MediaScreen() }
|
||||
composableWithDeepLink(Settings.Media::class) { MediaScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.Other) { OtherScreen() }
|
||||
composableWithDeepLink(Settings.PhysicalKeyboard) { PhysicalKeyboardScreen() }
|
||||
composableWithDeepLink(Settings.Backup) { BackupScreen() }
|
||||
composableWithDeepLink(Settings.Restore) { RestoreScreen() }
|
||||
composableWithDeepLink(Settings.Other::class) { OtherScreen() }
|
||||
composableWithDeepLink(Settings.PhysicalKeyboard::class) { PhysicalKeyboardScreen() }
|
||||
composableWithDeepLink(Settings.Backup::class) { BackupScreen() }
|
||||
composableWithDeepLink(Settings.Restore::class) { RestoreScreen() }
|
||||
|
||||
composableWithDeepLink(Settings.About) { AboutScreen() }
|
||||
composableWithDeepLink(Settings.ProjectLicense) { ProjectLicenseScreen() }
|
||||
composableWithDeepLink(Settings.ThirdPartyLicenses) { ThirdPartyLicensesScreen() }
|
||||
composableWithDeepLink(Settings.About::class) { AboutScreen() }
|
||||
composableWithDeepLink(Settings.ProjectLicense::class) { ProjectLicenseScreen() }
|
||||
composableWithDeepLink(Settings.ThirdPartyLicenses::class) { ThirdPartyLicensesScreen() }
|
||||
|
||||
composableWithDeepLink(Devtools.Home) { DevtoolsScreen() }
|
||||
composableWithDeepLink(Devtools.AndroidLocales) { AndroidLocalesScreen() }
|
||||
composableWithDeepLink(Devtools.AndroidSettings) { navBackStack ->
|
||||
val name = navBackStack.arguments?.getString("name")
|
||||
AndroidSettingsScreen(name)
|
||||
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.ExportDebugLog) { ExportDebugLogScreen() }
|
||||
composableWithDeepLink(Devtools.ExportDebugLog::class) { ExportDebugLogScreen() }
|
||||
|
||||
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.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.Edit) { navBackStack ->
|
||||
val extensionId = navBackStack.arguments?.getString("id")
|
||||
val serialType = navBackStack.arguments?.getString("serial_type")
|
||||
composableWithDeepLink(Ext.Edit::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Ext.Edit>()
|
||||
val extensionId = payload.id
|
||||
val serialType = payload.serialType
|
||||
ExtensionEditScreen(
|
||||
id = extensionId.toString(),
|
||||
id = extensionId,
|
||||
createSerialType = serialType.takeIf { !it.isNullOrBlank() },
|
||||
)
|
||||
}
|
||||
composableWithDeepLink(Ext.Export) { navBackStack ->
|
||||
val extensionId = navBackStack.arguments?.getString("id")
|
||||
ExtensionExportScreen(id = extensionId.toString())
|
||||
composableWithDeepLink(Ext.Export::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Ext.Export>()
|
||||
val extensionId = payload.id
|
||||
ExtensionExportScreen(id = extensionId)
|
||||
}
|
||||
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.Import::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Ext.Import>()
|
||||
val uuid = payload.uuid
|
||||
ExtensionImportScreen(payload.type, uuid)
|
||||
}
|
||||
composableWithDeepLink(Ext.View) { navBackStack ->
|
||||
val extensionId = navBackStack.arguments?.getString("id")
|
||||
ExtensionViewScreen(id = extensionId.toString())
|
||||
composableWithDeepLink(Ext.View::class) { navBackStack ->
|
||||
val payload = navBackStack.toRoute<Ext.View>()
|
||||
val extensionId = payload.id
|
||||
ExtensionViewScreen(id = extensionId)
|
||||
}
|
||||
composableWithDeepLink(Ext.CheckUpdates) {
|
||||
composableWithDeepLink(Ext.CheckUpdates::class) {
|
||||
CheckUpdatesScreen()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,8 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
import dev.patrickgold.florisboard.app.AppTheme
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.color.ColorMappings
|
||||
|
||||
/*private val AmoledDarkColorPalette = darkColorScheme(
|
||||
@@ -79,7 +78,7 @@ fun getColorScheme(
|
||||
context: Context,
|
||||
theme: AppTheme,
|
||||
): ColorScheme {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val accentColor by prefs.other.accentColor.observeAsState()
|
||||
val isDark = isSystemInDarkTheme()
|
||||
|
||||
|
||||
@@ -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.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.compose.FlorisIconButton
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
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.showLongToast("Exported available system locales to \"${txtFile.path}\"")
|
||||
context.showLongToastSync("Exported available system locales to \"${txtFile.path}\"")
|
||||
} catch (e: Exception) {
|
||||
context.showLongToast(
|
||||
context.showLongToastSync(
|
||||
R.string.error__snackbar_message_template,
|
||||
"error_message" to e.message.toString(),
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -29,7 +29,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -40,7 +39,8 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
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 org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.snygg.SnyggMissingSchemaException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import org.florisboard.lib.android.AndroidVersion
|
||||
import org.florisboard.lib.snygg.SnyggMissingSchemaException
|
||||
|
||||
private val CardBackground = Color.Black.copy(0.6f)
|
||||
private val DateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss", FlorisLocale.default().base)
|
||||
@@ -64,7 +64,8 @@ private val DateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss", FlorisLocale.
|
||||
@Composable
|
||||
fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
val context = LocalContext.current
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val appContext by context.appContext()
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val themeManager by context.themeManager()
|
||||
|
||||
@@ -73,9 +74,10 @@ 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.observeAsState()
|
||||
val themeInfo by themeManager.activeThemeInfo.collectAsState()
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides Color.White,
|
||||
@@ -97,8 +99,8 @@ fun DevtoolsOverlay(modifier: Modifier = Modifier) {
|
||||
if (devtoolsEnabled && showInlineAutofillOverlay && AndroidVersion.ATLEAST_API30_R) {
|
||||
DevtoolsInlineAutofillOverlay()
|
||||
}
|
||||
val loadFailure = themeInfo?.loadFailure
|
||||
if (loadFailure != null) {
|
||||
val loadFailure = themeInfo.loadFailure
|
||||
if (loadFailure != null && prefsLoaded) {
|
||||
DevtoolsStylesheetFailedToLoadOverlay(loadFailure)
|
||||
}
|
||||
}
|
||||
@@ -163,13 +165,13 @@ private fun DevtoolsLastLayoutComputationOverlay(debugLayoutResult: DebugLayoutC
|
||||
return@DevtoolsOverlayBox
|
||||
}
|
||||
DevtoolsSubGroup(title = "main") {
|
||||
PrintResult(debugLayoutResult!!.main)
|
||||
PrintResult(debugLayoutResult.main)
|
||||
}
|
||||
DevtoolsSubGroup(title = "mod") {
|
||||
PrintResult(debugLayoutResult!!.mod)
|
||||
PrintResult(debugLayoutResult.mod)
|
||||
}
|
||||
DevtoolsSubGroup(title = "ext") {
|
||||
PrintResult(debugLayoutResult!!.ext)
|
||||
PrintResult(debugLayoutResult.ext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.LocalNavController
|
||||
@@ -28,17 +29,18 @@ 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 :)"
|
||||
@@ -52,6 +54,7 @@ fun DevtoolsScreen() = FlorisScreen {
|
||||
val context = LocalContext.current
|
||||
val navController = LocalNavController.current
|
||||
val extensionManager by context.extensionManager()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val (showDialog, setShowDialog) = remember { mutableStateOf(false) }
|
||||
|
||||
@@ -110,15 +113,17 @@ fun DevtoolsScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.devtools__reset_quick_actions_to_default__label),
|
||||
summary = stringRes(R.string.devtools__reset_quick_actions_to_default__summary),
|
||||
onClick = {
|
||||
prefs.smartbar.actionArrangement.set(QuickActionArrangement.Default)
|
||||
context.showLongToast(R.string.devtools__reset_quick_actions_to_default__toast_success)
|
||||
scope.launch {
|
||||
prefs.smartbar.actionArrangement.set(QuickActionArrangement.Default)
|
||||
}
|
||||
context.showLongToastSync(R.string.devtools__reset_quick_actions_to_default__toast_success)
|
||||
},
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
)
|
||||
Preference(
|
||||
title = stringRes(R.string.devtools__reset_flag__label, "flag_name" to "isImeSetUp"),
|
||||
summary = stringRes(R.string.devtools__reset_flag_is_ime_set_up__summary),
|
||||
onClick = { prefs.internal.isImeSetUp.set(false) },
|
||||
onClick = { scope.launch { prefs.internal.isImeSetUp.set(false) } },
|
||||
enabledIf = { prefs.devtools.enabled isEqualTo true },
|
||||
)
|
||||
Preference(
|
||||
@@ -200,14 +205,14 @@ fun DevtoolsScreen() = FlorisScreen {
|
||||
title = "keyboardExtensions",
|
||||
summary = extensionManager.keyboardExtensions.internalModuleDir.absolutePath,
|
||||
onClick = {
|
||||
context.showLongToast(extensionManager.keyboardExtensions.internalModuleDir.absolutePath)
|
||||
context.showLongToastSync(extensionManager.keyboardExtensions.internalModuleDir.absolutePath)
|
||||
},
|
||||
)
|
||||
Preference(
|
||||
title = "themes",
|
||||
summary = extensionManager.themes.internalModuleDir.absolutePath,
|
||||
onClick = {
|
||||
context.showLongToast(extensionManager.themes.internalModuleDir.absolutePath)
|
||||
context.showLongToastSync(extensionManager.themes.internalModuleDir.absolutePath)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,15 +38,16 @@ import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.sp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisButton
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
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
|
||||
@@ -54,7 +55,7 @@ fun ExportDebugLogScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.devtools__debuglog__title)
|
||||
scrollable = false
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val clipboardManager by context.clipboardManager()
|
||||
|
||||
@@ -74,7 +75,7 @@ fun ExportDebugLogScreen() = FlorisScreen {
|
||||
FlorisButton(
|
||||
onClick = {
|
||||
clipboardManager.addNewPlaintext(debugLog!!.joinToString("\n"))
|
||||
context.showShortToast(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
context.showShortToastSync(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
},
|
||||
modifier = Modifier,
|
||||
text = stringRes(R.string.devtools__debuglog__copy_log),
|
||||
@@ -83,7 +84,7 @@ fun ExportDebugLogScreen() = FlorisScreen {
|
||||
FlorisButton(
|
||||
onClick = {
|
||||
clipboardManager.addNewPlaintext(formattedDebugLog!!.joinToString("\n"))
|
||||
context.showShortToast(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
context.showShortToastSync(context.getString(R.string.devtools__debuglog__copied_to_clipboard))
|
||||
},
|
||||
text = stringRes(R.string.devtools__debuglog__copy_for_github),
|
||||
enabled = debugLog != null,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
fun CheckUpdatesScreen() = FlorisScreen {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -48,9 +48,7 @@ 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
|
||||
@@ -59,7 +57,11 @@ 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
|
||||
@@ -186,9 +188,9 @@ fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = Fl
|
||||
allowOutsideDismissal = true,
|
||||
onNeutral = {
|
||||
if (file.delete()) {
|
||||
context.showShortToast("Successfully deleted")
|
||||
context.showShortToastSync("Successfully deleted")
|
||||
} else {
|
||||
context.showShortToast("Failed to delete")
|
||||
context.showShortToastSync("Failed to delete")
|
||||
}
|
||||
dialogFile = null
|
||||
version++
|
||||
@@ -196,18 +198,18 @@ fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = Fl
|
||||
onConfirm = {
|
||||
val newFile = file.parentFile!!.subFile(fileNameInput).canonicalFile
|
||||
if (newFile.parentFile != file.canonicalFile.parentFile) {
|
||||
context.showLongToast("Invalid file name!")
|
||||
context.showLongToastSync("Invalid file name!")
|
||||
return@JetPrefAlertDialog
|
||||
}
|
||||
if (newFile.exists()) {
|
||||
context.showShortToast("Filename already exists.")
|
||||
context.showShortToastSync("Filename already exists.")
|
||||
return@JetPrefAlertDialog
|
||||
}
|
||||
val success = file.renameTo(newFile)
|
||||
if (success) {
|
||||
context.showShortToast("Successfully renamed")
|
||||
context.showShortToastSync("Successfully renamed")
|
||||
} else {
|
||||
context.showShortToast("Failed to rename the file.")
|
||||
context.showShortToastSync("Failed to rename the file.")
|
||||
}
|
||||
dialogFile = null
|
||||
version++
|
||||
@@ -257,13 +259,13 @@ fun ExtensionEditFilesScreen(workspace: CacheManager.ExtEditorWorkspace<*>) = Fl
|
||||
dir.mkdirs()
|
||||
val file = dir.subFile(fileName)
|
||||
if (file.parentDir != workspace.extDir.subDir(dest)) {
|
||||
context.showShortToast("Invalid file name")
|
||||
context.showShortToastSync("Invalid file name")
|
||||
} else if (file.exists()) {
|
||||
context.showShortToast("File already exists")
|
||||
context.showShortToastSync("File already exists")
|
||||
} else {
|
||||
val tempFile = result.first
|
||||
if (!tempFile.renameTo(file)) {
|
||||
context.showShortToast("Failed to rename file")
|
||||
context.showShortToastSync("Failed to rename file")
|
||||
tempFile.delete()
|
||||
}
|
||||
currentImportDest = null
|
||||
|
||||
@@ -42,7 +42,9 @@ 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
|
||||
@@ -60,15 +62,9 @@ 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
|
||||
@@ -85,11 +81,16 @@ 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.android.showLongToast
|
||||
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.kotlin.io.deleteContentsRecursively
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
@@ -287,7 +288,7 @@ private fun EditScreen(
|
||||
stylesheetFile.writeText(stylesheet)
|
||||
}.onFailure {
|
||||
// TODO: better error handling
|
||||
context.showLongToast(it.message.toString())
|
||||
context.showLongToastSync(it.message.toString())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
@@ -352,7 +353,7 @@ private fun EditScreen(
|
||||
)
|
||||
Preference(
|
||||
onClick = { workspace.currentAction = EditorAction.ManageFiles },
|
||||
icon = vectorResource(R.drawable.ic_file_blank),
|
||||
icon = ImageVector.vectorResource(R.drawable.ic_file_blank),
|
||||
title = stringRes(R.string.ext__editor__files__title),
|
||||
)
|
||||
}
|
||||
@@ -665,7 +666,7 @@ private fun <T : ExtensionComponent> CreateComponentScreen(
|
||||
when (createFrom) {
|
||||
CreateFrom.EMPTY -> {
|
||||
if (editor.themes.any { it.id == newId.trim() }) {
|
||||
context.showLongToast("A theme with this ID already exists!")
|
||||
context.showLongToastSync("A theme with this ID already exists!")
|
||||
} else {
|
||||
val componentEditor = ThemeExtensionComponentEditor(
|
||||
id = newId.trim(),
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.florisboard.lib.android.showLongToast
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.ext.Extension
|
||||
import dev.patrickgold.florisboard.lib.ext.ExtensionDefaults
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
|
||||
@Composable
|
||||
fun ExtensionExportScreen(id: String) {
|
||||
@@ -61,9 +62,9 @@ private fun ExportScreen(ext: Extension) = FlorisScreen {
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
runCatching { extensionManager.export(ext, uri) }.onSuccess {
|
||||
context.showLongToast(R.string.ext__export__success)
|
||||
context.showLongToastSync(R.string.ext__export__success)
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(R.string.ext__export__failure, "error_message" to error.localizedMessage)
|
||||
context.showLongToastSync(R.string.ext__export__failure, "error_message" to error.localizedMessage)
|
||||
}
|
||||
navController.popBackStack()
|
||||
},
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.showLongToast(R.string.ext__import__success)
|
||||
context.showLongToastSync(R.string.ext__import__success)
|
||||
navController.popBackStack()
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(R.string.ext__import__failure, "error_message" to error.localizedMessage)
|
||||
context.showLongToastSync(R.string.ext__import__failure, "error_message" to error.localizedMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 dev.patrickgold.florisboard.lib.compose.FlorisChip
|
||||
import org.florisboard.lib.compose.FlorisChip
|
||||
|
||||
@Composable
|
||||
fun ExtensionKeywordChip(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
internal fun ExtensionNotFoundScreen(id: String) = FlorisScreen {
|
||||
|
||||
@@ -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.showLongToast(
|
||||
context.showLongToastSync(
|
||||
R.string.error__snackbar_message,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.util.launchUrl
|
||||
import org.florisboard.lib.android.stringRes
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisCanvasIcon
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisScreen
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
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
|
||||
|
||||
@Composable
|
||||
fun AboutScreen() = FlorisScreen {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 dev.patrickgold.florisboard.lib.compose.florisScrollbar
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.compose.florisScrollbar
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@Composable
|
||||
fun ThirdPartyLicensesScreen() = FlorisScreen {
|
||||
|
||||
@@ -32,6 +32,7 @@ import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -41,28 +42,33 @@ 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.jetprefDatastoreDir
|
||||
import dev.patrickgold.jetpref.datastore.runtime.AndroidAppDataStorage
|
||||
import dev.patrickgold.jetpref.datastore.runtime.FileBasedStorage
|
||||
import dev.patrickgold.jetpref.material.ui.JetPrefListItem
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.florisboard.lib.android.showLongToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.android.writeFromFile
|
||||
import org.florisboard.lib.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
|
||||
@@ -136,6 +142,7 @@ fun BackupScreen() = FlorisScreen {
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val cacheManager by context.cacheManager()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
var backupDestination by remember { mutableStateOf(Backup.Destination.FILE_SYS) }
|
||||
val backupFilesSelector = remember { Backup.FilesSelector() }
|
||||
@@ -155,22 +162,24 @@ fun BackupScreen() = FlorisScreen {
|
||||
context.contentResolver.writeFromFile(uri, backupWorkspace!!.zipFile)
|
||||
backupWorkspace!!.close()
|
||||
}.onSuccess {
|
||||
context.showLongToast(R.string.backup_and_restore__back_up__success)
|
||||
context.showLongToastSync(R.string.backup_and_restore__back_up__success)
|
||||
navController.popBackStack()
|
||||
}.onFailure { error ->
|
||||
flogError { error.stackTraceToString() }
|
||||
context.showLongToast(R.string.backup_and_restore__back_up__failure, "error_message" to error.message)
|
||||
context.showLongToastSync(R.string.backup_and_restore__back_up__failure, "error_message" to error.message)
|
||||
backupWorkspace = null
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
fun prepareBackupWorkspace() {
|
||||
suspend fun prepareBackupWorkspace() {
|
||||
val workspace = cacheManager.backupAndRestore.new()
|
||||
if (backupFilesSelector.jetprefDatastore) {
|
||||
context.jetprefDatastoreDir.let { dir ->
|
||||
dir.copyRecursively(workspace.inputDir.subDir(dir.name))
|
||||
}
|
||||
val fileBasedStorage = workspace.inputDir
|
||||
.subDir(AndroidAppDataStorage.JETPREF_DIR_NAME)
|
||||
.subFile("${FlorisPreferenceModel.NAME}.${AndroidAppDataStorage.JETPREF_FILE_EXT}")
|
||||
.let { FileBasedStorage(it.path) }
|
||||
FlorisPreferenceStore.export(fileBasedStorage).getOrThrow()
|
||||
}
|
||||
val workspaceFilesDir = workspace.inputDir.subDir("files")
|
||||
if (backupFilesSelector.imeKeyboard) {
|
||||
@@ -225,7 +234,7 @@ fun BackupScreen() = FlorisScreen {
|
||||
backupWorkspace = workspace
|
||||
}
|
||||
|
||||
fun prepareAndPerformBackup() {
|
||||
suspend fun prepareAndPerformBackup() {
|
||||
runCatching {
|
||||
if (backupWorkspace == null || backupWorkspace!!.isClosed()) {
|
||||
prepareBackupWorkspace()
|
||||
@@ -265,7 +274,7 @@ fun BackupScreen() = FlorisScreen {
|
||||
)
|
||||
ButtonBarButton(
|
||||
onClick = {
|
||||
prepareAndPerformBackup()
|
||||
scope.launch { prepareAndPerformBackup() }
|
||||
},
|
||||
text = stringRes(R.string.action__back_up),
|
||||
enabled = backupFilesSelector.atLeastOneSelected(),
|
||||
|
||||
@@ -27,7 +27,9 @@ 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
|
||||
@@ -36,7 +38,6 @@ 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
|
||||
@@ -45,9 +46,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
|
||||
@@ -156,7 +157,7 @@ fun OtherScreen() = FlorisScreen {
|
||||
enabledIf = { AndroidVersion.ATMOST_API28_P },
|
||||
)
|
||||
Preference(
|
||||
icon = vectorResource(R.drawable.ic_keyboard_keys),
|
||||
icon = ImageVector.vectorResource(R.drawable.ic_keyboard_keys),
|
||||
title = stringRes(R.string.physical_keyboard__title),
|
||||
onClick = { navController.navigate(Routes.Settings.PhysicalKeyboard) },
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -43,47 +43,46 @@ 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.JetPref
|
||||
import dev.patrickgold.jetpref.datastore.runtime.AndroidAppDataStorage
|
||||
import dev.patrickgold.jetpref.datastore.runtime.FileBasedStorage
|
||||
import dev.patrickgold.jetpref.datastore.runtime.ImportStrategy
|
||||
import dev.patrickgold.jetpref.datastore.ui.Preference
|
||||
import 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
|
||||
@@ -91,13 +90,12 @@ fun RestoreScreen() = FlorisScreen {
|
||||
title = stringRes(R.string.backup_and_restore__restore__title)
|
||||
previewFieldVisible = false
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val cacheManager by context.cacheManager()
|
||||
|
||||
val restoreFilesSelector = remember { Backup.FilesSelector() }
|
||||
var restoreMode by remember { mutableStateOf(Restore.Mode.MERGE) }
|
||||
var importStrategy by remember { mutableStateOf(ImportStrategy.Merge) }
|
||||
// TODO: rememberCoroutineScope() is unusable because it provides the scope in a cancelled state, which does
|
||||
// not make sense at all. I suspect that this is a bug and once it is resolved we can use it here again.
|
||||
val restoreScope = remember { CoroutineScope(Dispatchers.Main) }
|
||||
@@ -138,7 +136,7 @@ fun RestoreScreen() = FlorisScreen {
|
||||
}
|
||||
restoreWorkspace = workspace
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(
|
||||
context.showLongToastSync(
|
||||
R.string.backup_and_restore__restore__failure,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
@@ -148,15 +146,13 @@ fun RestoreScreen() = FlorisScreen {
|
||||
|
||||
suspend fun performRestore() {
|
||||
val workspace = restoreWorkspace!!
|
||||
val shouldReset = restoreMode == Restore.Mode.ERASE_AND_OVERWRITE
|
||||
val shouldReset = importStrategy == ImportStrategy.Erase
|
||||
if (restoreFilesSelector.jetprefDatastore) {
|
||||
val datastoreFile = workspace.outputDir
|
||||
.subDir(JetPref.JETPREF_DIR_NAME)
|
||||
.subFile("${prefs.name}.${JetPref.JETPREF_FILE_EXT}")
|
||||
if (datastoreFile.exists()) {
|
||||
prefs.datastorePersistenceHandler?.loadPrefs(datastoreFile, shouldReset)
|
||||
prefs.datastorePersistenceHandler?.persistPrefs()
|
||||
}
|
||||
val fileBasedStorage = workspace.outputDir
|
||||
.subDir(AndroidAppDataStorage.JETPREF_DIR_NAME)
|
||||
.subFile("${FlorisPreferenceModel.NAME}.${AndroidAppDataStorage.JETPREF_FILE_EXT}")
|
||||
.let { FileBasedStorage(it.path) }
|
||||
FlorisPreferenceStore.import(importStrategy, fileBasedStorage).getOrThrow()
|
||||
}
|
||||
val workspaceFilesDir = workspace.outputDir.subDir("files")
|
||||
if (restoreFilesSelector.imeKeyboard) {
|
||||
@@ -275,16 +271,16 @@ fun RestoreScreen() = FlorisScreen {
|
||||
) {
|
||||
RadioListItem(
|
||||
onClick = {
|
||||
restoreMode = Restore.Mode.MERGE
|
||||
importStrategy = ImportStrategy.Merge
|
||||
},
|
||||
selected = restoreMode == Restore.Mode.MERGE,
|
||||
selected = importStrategy == ImportStrategy.Merge,
|
||||
text = stringRes(R.string.backup_and_restore__restore__mode_merge),
|
||||
)
|
||||
RadioListItem(
|
||||
onClick = {
|
||||
restoreMode = Restore.Mode.ERASE_AND_OVERWRITE
|
||||
importStrategy = ImportStrategy.Erase
|
||||
},
|
||||
selected = restoreMode == Restore.Mode.ERASE_AND_OVERWRITE,
|
||||
selected = importStrategy == ImportStrategy.Erase,
|
||||
text = stringRes(R.string.backup_and_restore__restore__mode_erase_and_overwrite),
|
||||
)
|
||||
}
|
||||
@@ -293,7 +289,7 @@ fun RestoreScreen() = FlorisScreen {
|
||||
runCatching {
|
||||
restoreDataFromFileSystemLauncher.launch("*/*")
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(
|
||||
context.showLongToastSync(
|
||||
R.string.backup_and_restore__restore__failure,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -55,11 +55,8 @@ 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
|
||||
@@ -68,7 +65,11 @@ 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)
|
||||
@@ -143,16 +144,16 @@ fun UserDictionaryScreen(type: UserDictionaryType) = FlorisScreen {
|
||||
UserDictionaryType.SYSTEM -> dictionaryManager.systemUserDictionaryDatabase()
|
||||
}
|
||||
if (db == null) {
|
||||
context.showLongToast("Database handle is null, failed to import")
|
||||
context.showLongToastSync("Database handle is null, failed to import")
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
runCatching {
|
||||
db.importCombinedList(context, uri)
|
||||
}.onSuccess {
|
||||
buildUi()
|
||||
context.showLongToast(R.string.settings__udm__dictionary_import_success)
|
||||
context.showLongToastSync(R.string.settings__udm__dictionary_import_success)
|
||||
}.onFailure { error ->
|
||||
context.showLongToast("Error: ${error.localizedMessage}")
|
||||
context.showLongToastSync("Error: ${error.localizedMessage}")
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -168,15 +169,15 @@ fun UserDictionaryScreen(type: UserDictionaryType) = FlorisScreen {
|
||||
UserDictionaryType.SYSTEM -> dictionaryManager.systemUserDictionaryDatabase()
|
||||
}
|
||||
if (db == null) {
|
||||
context.showLongToast("Database handle is null, failed to export")
|
||||
context.showLongToastSync("Database handle is null, failed to export")
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
runCatching {
|
||||
db.exportCombinedList(context, uri)
|
||||
}.onSuccess {
|
||||
context.showLongToast(R.string.settings__udm__dictionary_export_success)
|
||||
context.showLongToastSync(R.string.settings__udm__dictionary_export_success)
|
||||
}.onFailure { error ->
|
||||
context.showLongToast("Error: ${error.localizedMessage}")
|
||||
context.showLongToastSync("Error: ${error.localizedMessage}")
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -23,14 +23,13 @@ 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 dev.patrickgold.jetpref.datastore.ui.SwitchPreference
|
||||
import org.florisboard.lib.compose.FlorisInfoCard
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
@OptIn(ExperimentalJetPrefDatastoreUi::class)
|
||||
@Composable
|
||||
|
||||
@@ -23,7 +23,6 @@ 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
|
||||
@@ -31,6 +30,7 @@ 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -41,26 +41,27 @@ 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");
|
||||
@@ -75,7 +76,7 @@ fun LanguagePackManagerScreen(action: LanguagePackManagerScreenAction?) = Floris
|
||||
else -> error("LanguagePack manager screen action must not be null")
|
||||
})
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val extensionManager by context.extensionManager()
|
||||
@@ -199,7 +200,7 @@ fun LanguagePackManagerScreen(action: LanguagePackManagerScreenAction?) = Floris
|
||||
runCatching {
|
||||
extensionManager.delete(languagePackExtToDelete!!)
|
||||
}.onFailure { error ->
|
||||
context.showLongToast(
|
||||
context.showLongToastSync(
|
||||
R.string.error__snackbar_message,
|
||||
"error_message" to error.localizedMessage,
|
||||
)
|
||||
|
||||
@@ -45,8 +45,6 @@ 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
|
||||
@@ -55,8 +53,9 @@ 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 = {
|
||||
|
||||
@@ -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 florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val navController = LocalNavController.current
|
||||
|
||||
val displayLanguageNamesIn by prefs.localization.displayLanguageNamesIn.observeAsState()
|
||||
|
||||
@@ -37,6 +37,7 @@ 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
|
||||
@@ -58,9 +59,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
|
||||
@@ -74,19 +75,20 @@ 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 kotlinx.serialization.encodeToString
|
||||
import org.florisboard.lib.compose.FlorisButtonBar
|
||||
import org.florisboard.lib.compose.FlorisDropdownLikeButton
|
||||
import org.florisboard.lib.compose.florisScrollbar
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
|
||||
|
||||
private val SelectComponentName = ExtensionComponentName("00", "00")
|
||||
private val SelectNlpProviderId = SelectComponentName.toString()
|
||||
@@ -186,7 +188,7 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
val selectValue = stringRes(R.string.settings__localization__subtype_select_placeholder)
|
||||
val selectListValues = remember(selectValue) { listOf(selectValue) }
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
val configuration = LocalConfiguration.current
|
||||
@@ -366,6 +368,7 @@ 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)) {
|
||||
@@ -375,16 +378,15 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
val popupMappingLabels = remember(popupMappings) {
|
||||
selectListValues + popupMappings.values.map { it.label }
|
||||
}
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
val selectedIndex = popupMappingIds.indexOf(popupMapping).coerceAtLeast(0)
|
||||
FlorisDropdownMenu(
|
||||
items = popupMappingLabels,
|
||||
JetPrefDropdown(
|
||||
options = popupMappingLabels,
|
||||
expanded = expanded,
|
||||
selectedIndex = selectedIndex,
|
||||
selectedOptionIndex = selectedIndex,
|
||||
isError = showSelectAsError && selectedIndex == 0,
|
||||
onSelectItem = { popupMapping = popupMappingIds[it] },
|
||||
onExpandRequest = { expanded = true },
|
||||
onDismissRequest = { expanded = false },
|
||||
onSelectOption = { popupMapping = popupMappingIds[it] },
|
||||
appearance = JetPrefDropdownMenuDefaults.outlined(shape = ShapeDefaults.Small),
|
||||
)
|
||||
}
|
||||
SubtypePropertyDropdown(stringRes(R.string.settings__localization__subtype_characters_layout), LayoutType.CHARACTERS)
|
||||
@@ -405,19 +407,18 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
val nlpProviderMappingLabels = remember(nlpProviderMappings) {
|
||||
selectListValues + nlpProviderMappings.values.map { it }
|
||||
}
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
val selectedIndex = nlpProviderMappingIds.indexOf(nlpProviders.suggestion).coerceAtLeast(0)
|
||||
FlorisDropdownMenu(
|
||||
items = nlpProviderMappingLabels,
|
||||
JetPrefDropdown(
|
||||
options = nlpProviderMappingLabels,
|
||||
expanded = expanded,
|
||||
selectedIndex = selectedIndex,
|
||||
selectedOptionIndex = selectedIndex,
|
||||
isError = showSelectAsError && selectedIndex == 0,
|
||||
onSelectItem = { nlpProviders = SubtypeNlpProviderMap(
|
||||
onSelectOption = { nlpProviders = SubtypeNlpProviderMap(
|
||||
suggestion = nlpProviderMappingIds[it],
|
||||
spelling = nlpProviderMappingIds[it]
|
||||
) },
|
||||
onExpandRequest = { expanded = true },
|
||||
onDismissRequest = { expanded = false },
|
||||
appearance = JetPrefDropdownMenuDefaults.outlined(shape = ShapeDefaults.Small),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -433,15 +434,14 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
val composerNames = remember(composers) {
|
||||
selectListValues + composers.values.map { it.label }
|
||||
}
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
FlorisDropdownMenu(
|
||||
items = composerNames,
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
JetPrefDropdown(
|
||||
options = composerNames,
|
||||
expanded = expanded,
|
||||
selectedIndex = composerIds.indexOf(composer).coerceAtLeast(0),
|
||||
selectedOptionIndex = composerIds.indexOf(composer).coerceAtLeast(0),
|
||||
isError = showSelectAsError && composer == SelectComponentName,
|
||||
onSelectItem = { composer = composerIds[it] },
|
||||
onExpandRequest = { expanded = true },
|
||||
onDismissRequest = { expanded = false },
|
||||
onSelectOption = { composer = composerIds[it] },
|
||||
appearance = JetPrefDropdownMenuDefaults.outlined(shape = ShapeDefaults.Small),
|
||||
)
|
||||
}
|
||||
SubtypeProperty(stringRes(R.string.settings__localization__subtype_currency_set)) {
|
||||
@@ -451,15 +451,14 @@ fun SubtypeEditorScreen(id: Long?) = FlorisScreen {
|
||||
val currencySetNames = remember(currencySets) {
|
||||
selectListValues + currencySets.values.map { it.label }
|
||||
}
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
FlorisDropdownMenu(
|
||||
items = currencySetNames,
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
JetPrefDropdown(
|
||||
options = currencySetNames,
|
||||
expanded = expanded,
|
||||
selectedIndex = currencySetIds.indexOf(currencySet).coerceAtLeast(0),
|
||||
selectedOptionIndex = currencySetIds.indexOf(currencySet).coerceAtLeast(0),
|
||||
isError = showSelectAsError && currencySet == SelectComponentName,
|
||||
onSelectItem = { currencySet = currencySetIds[it] },
|
||||
onExpandRequest = { expanded = true },
|
||||
onDismissRequest = { expanded = false },
|
||||
onSelectOption = { currencySet = currencySetIds[it] },
|
||||
appearance = JetPrefDropdownMenuDefaults.outlined(shape = ShapeDefaults.Small),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -557,16 +556,15 @@ 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] }
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
val selectedIndex = layoutIds.indexOf(layoutId).coerceAtLeast(0)
|
||||
FlorisDropdownMenu(
|
||||
items = layoutLabels,
|
||||
JetPrefDropdown(
|
||||
options = layoutLabels,
|
||||
expanded = expanded,
|
||||
selectedIndex = selectedIndex,
|
||||
selectedOptionIndex = selectedIndex,
|
||||
isError = showSelectAsError && selectedIndex == 0,
|
||||
onSelectItem = { onLayoutMapChanged(layoutMap.copy(layoutType, layoutIds[it])!!) },
|
||||
onExpandRequest = { expanded = true },
|
||||
onDismissRequest = { expanded = false },
|
||||
onSelectOption = { onLayoutMapChanged(layoutMap.copy(layoutType = layoutType, componentName = layoutIds[it])!!) },
|
||||
appearance = JetPrefDropdownMenuDefaults.outlined(shape = ShapeDefaults.Small),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,15 +27,13 @@ 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
|
||||
@@ -44,6 +42,8 @@ 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 florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
|
||||
var shouldDelete by remember { mutableStateOf<ShouldDelete?>(null) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dev.patrickgold.florisboard.lib.compose
|
||||
package dev.patrickgold.florisboard.app.settings.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceSerializer
|
||||
@@ -77,13 +77,7 @@ 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
|
||||
@@ -94,7 +88,14 @@ 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
|
||||
@@ -131,7 +132,6 @@ 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,
|
||||
|
||||
@@ -85,18 +85,19 @@ 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
|
||||
@@ -406,7 +407,7 @@ private fun EditCodeValueDialog(
|
||||
}
|
||||
if (!isFlorisBoardEnabled || !isFlorisBoardSelected) {
|
||||
lastRecordingToast?.cancel()
|
||||
lastRecordingToast = context.showShortToast(
|
||||
lastRecordingToast = context.showShortToastSync(
|
||||
R.string.settings__theme_editor__code_recording_requires_default_ime_floris,
|
||||
"app_name" to context.stringRes(R.string.floris_app_name),
|
||||
)
|
||||
@@ -432,12 +433,12 @@ private fun EditCodeValueDialog(
|
||||
val defaultReceiver = keyboardManager.inputEventDispatcher.keyEventReceiver
|
||||
keyboardManager.inputEventDispatcher.keyEventReceiver = receiver
|
||||
lastRecordingToast?.cancel()
|
||||
lastRecordingToast = context.showShortToast(R.string.settings__theme_editor__code_recording_started)
|
||||
lastRecordingToast = context.showShortToastSync(R.string.settings__theme_editor__code_recording_started)
|
||||
focusRequester.requestFocus()
|
||||
onDispose {
|
||||
keyboardManager.inputEventDispatcher.keyEventReceiver = defaultReceiver
|
||||
lastRecordingToast?.cancel()
|
||||
lastRecordingToast = context.showShortToast(R.string.settings__theme_editor__code_recording_stopped)
|
||||
lastRecordingToast = context.showShortToastSync(R.string.settings__theme_editor__code_recording_stopped)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,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(florisPreferenceModel(), iconSpaceReserved = false) {
|
||||
PreferenceLayout(FlorisPreferenceStore, iconSpaceReserved = false) {
|
||||
ListPreference(
|
||||
listPref = prefs.theme.editorLevel,
|
||||
title = stringRes(R.string.settings__theme_editor__fine_tune__level),
|
||||
|
||||
@@ -30,7 +30,6 @@ import androidx.compose.material.icons.automirrored.filled.FormatAlignLeft
|
||||
import androidx.compose.material.icons.automirrored.filled.FormatAlignRight
|
||||
import androidx.compose.material.icons.automirrored.filled.WrapText
|
||||
import androidx.compose.material.icons.filled.AttachFile
|
||||
import androidx.compose.material.icons.filled.CheckBox
|
||||
import androidx.compose.material.icons.filled.CheckBoxOutlineBlank
|
||||
import androidx.compose.material.icons.filled.FontDownload
|
||||
import androidx.compose.material.icons.filled.FormatAlignCenter
|
||||
@@ -57,7 +56,7 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.snygg.value.SnyggCutCornerDpShapeValue
|
||||
import org.florisboard.lib.snygg.value.SnyggDefinedVarValue
|
||||
@@ -121,7 +120,7 @@ internal fun SnyggValueIcon(
|
||||
modifier: Modifier = Modifier,
|
||||
spec: SnyggValueIcon.Spec = SnyggValueIcon.Normal,
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val accentColor by prefs.theme.accentColor.observeAsState()
|
||||
|
||||
|
||||
@@ -70,9 +70,9 @@ import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.app.apptheme.Shapes
|
||||
import dev.patrickgold.florisboard.app.ext.ExtensionComponentView
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponentEditor
|
||||
@@ -80,16 +80,10 @@ 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
|
||||
@@ -100,6 +94,13 @@ 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
|
||||
@@ -145,7 +146,7 @@ fun ThemeEditorScreen(
|
||||
title = stringRes(R.string.ext__editor__edit_component__title_theme)
|
||||
scrollable = false
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
val themeManager by context.themeManager()
|
||||
@@ -309,14 +310,14 @@ fun ThemeEditorScreen(
|
||||
}
|
||||
|
||||
DisposableEffect(workspace.version) {
|
||||
themeManager.previewThemeInfo = ThemeManager.ThemeInfo.DEFAULT.copy(
|
||||
themeManager.previewThemeInfo.value = ThemeManager.ThemeInfo.DEFAULT.copy(
|
||||
name = extPreviewTheme(System.currentTimeMillis().toString()),
|
||||
config = editor.build(),
|
||||
stylesheet = stylesheetEditor.build(),
|
||||
loadedDir = workspace.extDir,
|
||||
)
|
||||
onDispose {
|
||||
themeManager.previewThemeInfo = null
|
||||
themeManager.previewThemeInfo.value = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -633,7 +634,7 @@ private fun ComponentMetaEditorDialog(
|
||||
if (!allFieldsValid) {
|
||||
showValidationErrors = true
|
||||
} else if (id != editor.id && (workspace.editor as? ThemeExtensionEditor)?.themes?.find { it.id == id.trim() } != null) {
|
||||
context.showLongToast("A theme with this ID already exists!")
|
||||
context.showLongToastSync("A theme with this ID already exists!")
|
||||
} else {
|
||||
workspace.update {
|
||||
editor.id = id.trim()
|
||||
|
||||
@@ -29,22 +29,24 @@ import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.theme.ThemeExtensionComponent
|
||||
import dev.patrickgold.florisboard.lib.compose.FlorisOutlinedBox
|
||||
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"),
|
||||
@@ -60,10 +62,11 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
})
|
||||
previewFieldVisible = true
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val extensionManager by context.extensionManager()
|
||||
val themeManager by context.themeManager()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val indexedThemeExtensions by extensionManager.themes.observeAsNonNullState()
|
||||
val extGroupedThemes = remember(indexedThemeExtensions) {
|
||||
@@ -83,7 +86,7 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
val extComponentName = ExtensionComponentName(extId, componentId)
|
||||
when (action) {
|
||||
ThemeManagerScreenAction.SELECT_DAY,
|
||||
ThemeManagerScreenAction.SELECT_NIGHT -> {
|
||||
ThemeManagerScreenAction.SELECT_NIGHT -> scope.launch {
|
||||
getThemeIdPref().set(extComponentName)
|
||||
}
|
||||
}
|
||||
@@ -96,9 +99,9 @@ fun ThemeManagerScreen(action: ThemeManagerScreenAction?) = FlorisScreen {
|
||||
|
||||
content {
|
||||
DisposableEffect(activeThemeId) {
|
||||
themeManager.previewThemeId = activeThemeId
|
||||
themeManager.previewThemeId.value = activeThemeId
|
||||
onDispose {
|
||||
themeManager.previewThemeId = null
|
||||
themeManager.previewThemeId.value = null
|
||||
}
|
||||
}
|
||||
val grayColor = LocalContentColor.current.copy(alpha = 0.56f)
|
||||
|
||||
@@ -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,7 +37,6 @@ 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
|
||||
@@ -47,6 +46,7 @@ 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.observeAsState()
|
||||
configs?.get(id)?.let { return it.label }
|
||||
val configs by indexedThemeConfigs.collectAsState()
|
||||
configs[id]?.let { return it.label }
|
||||
return id.toString()
|
||||
}
|
||||
|
||||
|
||||
@@ -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>) {
|
||||
|
||||
@@ -30,6 +30,8 @@ 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
|
||||
@@ -37,19 +39,17 @@ 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 = vectorResource(id = R.drawable.ic_incognito),
|
||||
icon = ImageVector.vectorResource(id = R.drawable.ic_incognito),
|
||||
title = stringRes(R.string.pref__suggestion__incognito_mode__label),
|
||||
entries = enumDisplayEntriesOf(IncognitoMode::class),
|
||||
)
|
||||
|
||||
@@ -33,6 +33,7 @@ import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -40,26 +41,27 @@ 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 {
|
||||
@@ -70,7 +72,8 @@ fun SetupScreen() = FlorisScreen {
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val isFlorisBoardEnabled by InputMethodUtils.observeIsFlorisboardEnabled(foregroundOnly = true)
|
||||
val isFlorisBoardSelected by InputMethodUtils.observeIsFlorisboardSelected(foregroundOnly = true)
|
||||
@@ -78,10 +81,12 @@ fun SetupScreen() = FlorisScreen {
|
||||
|
||||
val requestNotification =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||
if (isGranted) {
|
||||
prefs.internal.notificationPermissionState.set(NotificationPermissionState.GRANTED)
|
||||
} else {
|
||||
prefs.internal.notificationPermissionState.set(NotificationPermissionState.DENIED)
|
||||
scope.launch {
|
||||
if (isGranted) {
|
||||
prefs.internal.notificationPermissionState.set(NotificationPermissionState.GRANTED)
|
||||
} else {
|
||||
prefs.internal.notificationPermissionState.set(NotificationPermissionState.DENIED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +96,8 @@ fun SetupScreen() = FlorisScreen {
|
||||
context,
|
||||
navController,
|
||||
requestNotification,
|
||||
hasNotificationPermission
|
||||
hasNotificationPermission,
|
||||
scope,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -103,6 +109,7 @@ private fun FlorisScreenScope.content(
|
||||
navController: NavController,
|
||||
requestNotification: ManagedActivityResultLauncher<String, Boolean>,
|
||||
hasNotificationPermission: NotificationPermissionState,
|
||||
scope: CoroutineScope,
|
||||
) {
|
||||
|
||||
val stepState = rememberSaveable(saver = FlorisStepState.Saver) {
|
||||
@@ -158,7 +165,7 @@ private fun FlorisScreenScope.content(
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
},
|
||||
steps = steps(
|
||||
context, navController, requestNotification
|
||||
context, navController, requestNotification, scope
|
||||
),
|
||||
footer = {
|
||||
footer(context)
|
||||
@@ -189,10 +196,11 @@ private fun footer(context: Context) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PreferenceUiScope<AppPrefs>.steps(
|
||||
private fun PreferenceUiScope<FlorisPreferenceModel>.steps(
|
||||
context: Context,
|
||||
navController: NavController,
|
||||
requestNotification: ManagedActivityResultLauncher<String, Boolean>,
|
||||
scope: CoroutineScope,
|
||||
): List<FlorisStep> {
|
||||
|
||||
return listOfNotNull(
|
||||
@@ -232,7 +240,7 @@ private fun PreferenceUiScope<AppPrefs>.steps(
|
||||
StepText(stringRes(R.string.setup__finish_up__description_p1))
|
||||
StepText(stringRes(R.string.setup__finish_up__description_p2))
|
||||
StepButton(label = stringRes(R.string.setup__finish_up__finish_btn)) {
|
||||
this@steps.prefs.internal.isImeSetUp.set(true)
|
||||
scope.launch { this@steps.prefs.internal.isImeSetUp.set(true) }
|
||||
navController.navigate(Routes.Settings.Home) {
|
||||
popUpTo(Routes.Setup.Screen) {
|
||||
inclusive = true
|
||||
|
||||
@@ -74,6 +74,7 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.mutableStateSetOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -87,7 +88,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.ime.ImeUiMode
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
|
||||
@@ -101,21 +102,24 @@ 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.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
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
|
||||
@@ -125,7 +129,6 @@ 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
|
||||
@@ -136,7 +139,8 @@ const val CLIPBOARD_HISTORY_NUM_GRID_COLUMNS_AUTO: Int = 0
|
||||
fun ClipboardInputLayout(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val scope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
val clipboardManager by context.clipboardManager()
|
||||
val keyboardManager by context.keyboardManager()
|
||||
@@ -203,7 +207,7 @@ fun ClipboardInputLayout(
|
||||
)
|
||||
SnyggIconButton(
|
||||
elementName = FlorisImeUi.ClipboardHeaderButton.elementName,
|
||||
onClick = { prefs.clipboard.historyEnabled.set(!historyEnabled) },
|
||||
onClick = { scope.launch { prefs.clipboard.historyEnabled.set(!historyEnabled) } },
|
||||
modifier = sizeModifier.autoMirrorForRtl(),
|
||||
enabled = !deviceLocked && !isPopupSurfaceActive(),
|
||||
) {
|
||||
@@ -583,7 +587,7 @@ fun ClipboardInputLayout(
|
||||
attributes = mapOf("action" to "yes"),
|
||||
onClick = {
|
||||
clipboardManager.clearHistory()
|
||||
context.showShortToast(R.string.clipboard__cleared_history)
|
||||
context.showShortToastSync(R.string.clipboard__cleared_history)
|
||||
showClearAllHistory = false
|
||||
isFilterRowShown = false
|
||||
},
|
||||
@@ -629,7 +633,7 @@ fun ClipboardInputLayout(
|
||||
text = stringRes(R.string.clipboard__disabled__message),
|
||||
)
|
||||
SnyggButton(FlorisImeUi.ClipboardHistoryDisabledButton.elementName,
|
||||
onClick = { prefs.clipboard.historyEnabled.set(true) },
|
||||
onClick = { scope.launch { prefs.clipboard.historyEnabled.set(true) } },
|
||||
modifier = Modifier.align(Alignment.End),
|
||||
) {
|
||||
SnyggText(
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
package dev.patrickgold.florisboard.ime.clipboard
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardHistoryDao
|
||||
@@ -44,6 +43,7 @@ import org.florisboard.lib.android.AndroidClipboardManager
|
||||
import org.florisboard.lib.android.AndroidClipboardManager_OnPrimaryClipChangedListener
|
||||
import org.florisboard.lib.android.setOrClearPrimaryClip
|
||||
import org.florisboard.lib.android.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.android.systemService
|
||||
import org.florisboard.lib.kotlin.tryOrNull
|
||||
import java.io.Closeable
|
||||
@@ -91,7 +91,7 @@ class ClipboardManager(
|
||||
}
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val appContext by context.appContext()
|
||||
private val editorInstance by context.editorInstance()
|
||||
private val systemClipboardManager = context.systemService(AndroidClipboardManager::class)
|
||||
@@ -355,7 +355,7 @@ class ClipboardManager(
|
||||
val editorInstance by appContext.editorInstance()
|
||||
editorInstance.commitClipboardItem(item).also { result ->
|
||||
if (!result) {
|
||||
appContext.showShortToast("Failed to paste item.")
|
||||
appContext.showShortToastSync("Failed to paste item.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,8 +135,12 @@ class FlorisCopyToClipboardActivity : ComponentActivity() {
|
||||
|
||||
@Composable
|
||||
private fun Content() {
|
||||
val prefs by florisPreferenceModel()
|
||||
ProvideLocalizedResources(this, forceLayoutDirection = LayoutDirection.Ltr) {
|
||||
val prefs by FlorisPreferenceStore
|
||||
ProvideLocalizedResources(
|
||||
resourcesContext = this,
|
||||
appName = R.string.app_name,
|
||||
forceLayoutDirection = LayoutDirection.Ltr,
|
||||
) {
|
||||
val theme by prefs.other.settingsTheme.observeAsState()
|
||||
FlorisAppTheme(theme) {
|
||||
BottomSheet {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -17,15 +17,18 @@
|
||||
package dev.patrickgold.florisboard.ime.core
|
||||
|
||||
import android.content.Context
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.keyboard.CurrencySet
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogDebug
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
|
||||
val SubtypeJsonConfig = Json {
|
||||
encodeDefaults = true
|
||||
@@ -38,8 +41,9 @@ val SubtypeJsonConfig = Json {
|
||||
* helper methods for the in-keyboard language switch process.
|
||||
*/
|
||||
class SubtypeManager(context: Context) {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
private val scope = CoroutineScope(Dispatchers.Default)
|
||||
|
||||
private val _subtypesFlow = MutableStateFlow(listOf<Subtype>())
|
||||
val subtypesFlow = _subtypesFlow.asStateFlow()
|
||||
@@ -54,7 +58,7 @@ class SubtypeManager(context: Context) {
|
||||
private set(v) { _activeSubtypeFlow.value = v }
|
||||
|
||||
init {
|
||||
prefs.localization.subtypes.observeForever { listRaw ->
|
||||
prefs.localization.subtypes.asFlow().collectLatestIn(scope) { listRaw ->
|
||||
flogDebug { listRaw }
|
||||
val list = if (listRaw.isNotBlank()) {
|
||||
SubtypeJsonConfig.decodeFromString<List<Subtype>>(listRaw)
|
||||
@@ -66,7 +70,7 @@ class SubtypeManager(context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun persistNewSubtypeList(list: List<Subtype>) {
|
||||
private fun persistNewSubtypeList(list: List<Subtype>) = scope.launch {
|
||||
val listRaw = SubtypeJsonConfig.encodeToString(list)
|
||||
prefs.localization.subtypes.set(listRaw)
|
||||
}
|
||||
@@ -78,7 +82,7 @@ class SubtypeManager(context: Context) {
|
||||
* @return The active subtype or null, if the subtype list is empty or no new active subtype
|
||||
* could be determined.
|
||||
*/
|
||||
private fun evaluateActiveSubtype(list: List<Subtype>) {
|
||||
private fun evaluateActiveSubtype(list: List<Subtype>) = scope.launch {
|
||||
val activeSubtypeId = prefs.localization.activeSubtypeId.get()
|
||||
val subtype = list.find { it.id == activeSubtypeId } ?: list.firstOrNull() ?: Subtype.DEFAULT
|
||||
if (subtype.id != activeSubtypeId) {
|
||||
@@ -184,7 +188,7 @@ class SubtypeManager(context: Context) {
|
||||
/**
|
||||
* Switch to the previous subtype in the subtype list if possible.
|
||||
*/
|
||||
fun switchToPrevSubtype() {
|
||||
fun switchToPrevSubtype() = scope.launch {
|
||||
val subtypeList = subtypes
|
||||
val cachedActiveSubtype = activeSubtype
|
||||
var triggerNextSubtype = false
|
||||
@@ -207,7 +211,7 @@ class SubtypeManager(context: Context) {
|
||||
/**
|
||||
* Switch to the next subtype in the subtype list if possible.
|
||||
*/
|
||||
fun switchToNextSubtype() {
|
||||
fun switchToNextSubtype() = scope.launch {
|
||||
val subtypeList = subtypes
|
||||
val cachedActiveSubtype = activeSubtype
|
||||
var triggerNextSubtype = false
|
||||
@@ -227,7 +231,7 @@ class SubtypeManager(context: Context) {
|
||||
activeSubtype = newActiveSubtype
|
||||
}
|
||||
|
||||
fun switchToSubtypeById(id: Long) {
|
||||
fun switchToSubtypeById(id: Long) = scope.launch {
|
||||
if (subtypes.any { it.id == id }) {
|
||||
activeSubtype = getSubtypeById(id)!!
|
||||
prefs.localization.activeSubtypeId.set(id)
|
||||
|
||||
@@ -18,7 +18,7 @@ package dev.patrickgold.florisboard.ime.dictionary
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.nlp.SuggestionCandidate
|
||||
import dev.patrickgold.florisboard.ime.nlp.WordSuggestionCandidate
|
||||
import dev.patrickgold.florisboard.lib.FlorisLocale
|
||||
@@ -29,7 +29,7 @@ import java.lang.ref.WeakReference
|
||||
*/
|
||||
class DictionaryManager private constructor(context: Context) {
|
||||
private val applicationContext: WeakReference<Context> = WeakReference(context.applicationContext ?: context)
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
|
||||
private var florisUserDictionaryDatabase: FlorisUserDictionaryDatabase? = null
|
||||
private var systemUserDictionaryDatabase: SystemUserDictionaryDatabase? = null
|
||||
|
||||
@@ -40,6 +40,16 @@ 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 {
|
||||
@@ -428,41 +438,69 @@ abstract class AbstractEditorInstance(context: Context) {
|
||||
return true
|
||||
}
|
||||
|
||||
protected fun deleteBeforeCursor(type: TextType, n: Int): Boolean {
|
||||
protected suspend fun deleteAroundCursor(unit: OperationUnit, scope: OperationScope, n: Int = 0): 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 oldTextBeforeSelection = content.textBeforeSelection
|
||||
return (if (activeInfo.isRawInputEditor || oldTextBeforeSelection.isEmpty()) {
|
||||
val scopeText = when (scope) {
|
||||
OperationScope.BEFORE_CURSOR -> content.textBeforeSelection
|
||||
OperationScope.AFTER_CURSOR -> content.textAfterSelection
|
||||
}
|
||||
return (if (activeInfo.isRawInputEditor || scopeText.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.
|
||||
when (type) {
|
||||
TextType.CHARACTERS -> sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL, count = n)
|
||||
TextType.WORDS -> sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL, meta(ctrl = true), count = n)
|
||||
// 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
|
||||
}
|
||||
val metaState = when (unit) {
|
||||
OperationUnit.CHARACTERS -> meta()
|
||||
OperationUnit.WORDS -> meta(ctrl = true)
|
||||
}
|
||||
sendDownUpKeyEvent(keyEventCode, metaState, count = n)
|
||||
} else {
|
||||
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 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()
|
||||
}
|
||||
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()
|
||||
}
|
||||
@@ -623,11 +661,6 @@ abstract class AbstractEditorInstance(context: Context) {
|
||||
return true
|
||||
}
|
||||
|
||||
protected enum class TextType {
|
||||
CHARACTERS,
|
||||
WORDS;
|
||||
}
|
||||
|
||||
private class ExpectedContentQueue {
|
||||
private val list = guardedByLock { mutableListOf<EditorContent>() }
|
||||
|
||||
|
||||
@@ -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.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardFileStorage
|
||||
@@ -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.showShortToast
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
|
||||
class EditorInstance(context: Context) : AbstractEditorInstance(context) {
|
||||
companion object {
|
||||
private const val SPACE = " "
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val appContext by context.appContext()
|
||||
private val clipboardManager by context.clipboardManager()
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
@@ -335,34 +335,37 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
|
||||
*
|
||||
* @return True on success, false if an error occurred or the input connection is invalid.
|
||||
*/
|
||||
fun deleteBackwards(): Boolean {
|
||||
fun deleteBackwards(unit: OperationUnit): Boolean {
|
||||
val content = activeContent
|
||||
if (phantomSpace.isActive && content.currentWord.isValid && prefs.glide.immediateBackspaceDeletesWord.get()) {
|
||||
return deleteWordBackwards()
|
||||
if (unit == OperationUnit.CHARACTERS) {
|
||||
if (phantomSpace.isActive && content.currentWord.isValid && prefs.glide.immediateBackspaceDeletesWord.get()) {
|
||||
return deleteBackwards(OperationUnit.WORDS)
|
||||
}
|
||||
}
|
||||
autoSpace.setInactive()
|
||||
phantomSpace.setInactive()
|
||||
return if (content.selection.isSelectionMode) {
|
||||
commitText("")
|
||||
} else {
|
||||
deleteBeforeCursor(TextType.CHARACTERS, 1)
|
||||
} else runBlocking {
|
||||
deleteAroundCursor(unit, OperationScope.BEFORE_CURSOR, n = 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 word from
|
||||
* characters inside this selection will be removed, else only the left-most character from
|
||||
* the cursor's position.
|
||||
*
|
||||
* @return True on success, false if an error occurred or the input connection is invalid.
|
||||
*/
|
||||
fun deleteWordBackwards(): Boolean {
|
||||
fun deleteForwards(unit: OperationUnit): Boolean {
|
||||
val content = activeContent
|
||||
autoSpace.setInactive()
|
||||
phantomSpace.setInactive()
|
||||
return if (activeContent.selection.isSelectionMode) {
|
||||
return if (content.selection.isSelectionMode) {
|
||||
commitText("")
|
||||
} else {
|
||||
deleteBeforeCursor(TextType.WORDS, 1)
|
||||
} else runBlocking {
|
||||
deleteAroundCursor(unit, OperationScope.AFTER_CURSOR, n = 1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,9 +396,9 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
|
||||
if (text != null) {
|
||||
clipboardManager.addNewPlaintext(text.toString())
|
||||
} else {
|
||||
appContext.showShortToast("Failed to retrieve selected text requested to cut: Eiter selection state is invalid or an error occurred within the input connection.")
|
||||
appContext.showShortToastSync("Failed to retrieve selected text requested to cut: Eiter selection state is invalid or an error occurred within the input connection.")
|
||||
}
|
||||
return deleteBackwards()
|
||||
return deleteBackwards(OperationUnit.CHARACTERS)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -411,7 +414,7 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
|
||||
if (text != null) {
|
||||
clipboardManager.addNewPlaintext(text.toString())
|
||||
} else {
|
||||
appContext.showShortToast("Failed to retrieve selected text requested to copy: Eiter selection state is invalid or an error occurred within the input connection.")
|
||||
appContext.showShortToastSync("Failed to retrieve selected text requested to copy: Eiter selection state is invalid or an error occurred within the input connection.")
|
||||
}
|
||||
val activeSelection = activeContent.selection
|
||||
return setSelection(activeSelection.end, activeSelection.end)
|
||||
@@ -428,7 +431,7 @@ class EditorInstance(context: Context) : AbstractEditorInstance(context) {
|
||||
phantomSpace.setInactive()
|
||||
return commitClipboardItem(clipboardManager.primaryClip).also { result ->
|
||||
if (!result) {
|
||||
appContext.showShortToast("Failed to paste item.")
|
||||
appContext.showShortToastSync("Failed to paste item.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import android.view.ViewConfiguration
|
||||
import androidx.collection.SparseArrayCompat
|
||||
import androidx.collection.isNotEmpty
|
||||
import androidx.collection.set
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.keyboard.KeyData
|
||||
import dev.patrickgold.florisboard.ime.text.gestures.SwipeAction
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
@@ -48,7 +48,7 @@ class InputEventDispatcher private constructor(private val repeatableKeyCodes: I
|
||||
fun new(repeatableKeyCodes: IntArray = intArrayOf()) = InputEventDispatcher(repeatableKeyCodes.clone())
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
|
||||
|
||||
private val pressedKeys = guardedByLock { SparseArrayCompat<PressedKeyInfo>() }
|
||||
|
||||
@@ -21,7 +21,7 @@ import android.media.AudioManager
|
||||
import android.provider.Settings
|
||||
import android.view.HapticFeedbackConstants
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.keyboard.KeyData
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
@@ -46,7 +46,7 @@ class InputFeedbackController private constructor(private val ims: InputMethodSe
|
||||
fun new(ims: InputMethodService) = InputFeedbackController(ims)
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
|
||||
private val audioManager = ims.systemServiceOrNull(AudioManager::class)
|
||||
private val vibrator = ims.systemVibratorOrNull()
|
||||
|
||||
@@ -19,8 +19,10 @@ 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
|
||||
@@ -55,7 +57,8 @@ 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.jetpref.datastore.ui.vectorResource
|
||||
import dev.patrickgold.florisboard.lib.compose.vectorResource
|
||||
import org.florisboard.lib.compose.icons.ForwardDelete
|
||||
|
||||
interface ComputingEvaluator {
|
||||
val version: Int
|
||||
@@ -234,6 +237,9 @@ fun ComputingEvaluator.computeImageVector(data: KeyData): ImageVector? {
|
||||
}
|
||||
}
|
||||
}
|
||||
KeyCode.FORWARD_DELETE -> {
|
||||
Icons.AutoMirrored.Default.ForwardDelete
|
||||
}
|
||||
KeyCode.IME_UI_MODE_MEDIA -> {
|
||||
Icons.Default.SentimentSatisfiedAlt
|
||||
}
|
||||
|
||||
@@ -36,8 +36,7 @@ import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.ime.onehanded.OneHandedMode
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.smartbar.ExtendedActionsPlacement
|
||||
import dev.patrickgold.florisboard.ime.smartbar.SmartbarLayout
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyboard
|
||||
@@ -80,7 +79,7 @@ object FlorisImeSizing {
|
||||
|
||||
@Composable
|
||||
fun smartbarUiHeight(): Dp {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val smartbarEnabled by prefs.smartbar.enabled.observeAsState()
|
||||
val smartbarLayout by prefs.smartbar.layout.observeAsState()
|
||||
val extendedActionsExpanded by prefs.smartbar.extendedActionsExpanded.observeAsState()
|
||||
@@ -113,7 +112,7 @@ object FlorisImeSizing {
|
||||
|
||||
@Composable
|
||||
fun ProvideKeyboardRowBaseHeight(content: @Composable () -> Unit) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val resources = LocalContext.current.resources
|
||||
val configuration = LocalConfiguration.current
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import dev.patrickgold.florisboard.FlorisImeService
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
@@ -39,6 +39,7 @@ 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
|
||||
@@ -74,7 +75,8 @@ 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.showShortToast
|
||||
import org.florisboard.lib.android.showLongToastSync
|
||||
import org.florisboard.lib.android.showShortToastSync
|
||||
import org.florisboard.lib.android.systemService
|
||||
import org.florisboard.lib.kotlin.collectIn
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
@@ -83,7 +85,7 @@ import java.util.concurrent.atomic.AtomicInteger
|
||||
private val DoubleSpacePeriodMatcher = """([^.!?‽\s]\s)""".toRegex()
|
||||
|
||||
class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val appContext by context.appContext()
|
||||
private val clipboardManager by context.clipboardManager()
|
||||
private val editorInstance by context.editorInstance()
|
||||
@@ -129,21 +131,21 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
keyboardCache.clear()
|
||||
}
|
||||
}
|
||||
prefs.keyboard.numberRow.observeForever {
|
||||
prefs.keyboard.numberRow.asFlow().collectLatestIn(scope) {
|
||||
updateActiveEvaluators {
|
||||
keyboardCache.clear(KeyboardMode.CHARACTERS)
|
||||
}
|
||||
}
|
||||
prefs.keyboard.hintedNumberRowEnabled.observeForever {
|
||||
prefs.keyboard.hintedNumberRowEnabled.asFlow().collectLatestIn(scope) {
|
||||
updateActiveEvaluators()
|
||||
}
|
||||
prefs.keyboard.hintedSymbolsEnabled.observeForever {
|
||||
prefs.keyboard.hintedSymbolsEnabled.asFlow().collectLatestIn(scope) {
|
||||
updateActiveEvaluators()
|
||||
}
|
||||
prefs.keyboard.utilityKeyEnabled.observeForever {
|
||||
prefs.keyboard.utilityKeyEnabled.asFlow().collectLatestIn(scope) {
|
||||
updateActiveEvaluators()
|
||||
}
|
||||
prefs.keyboard.utilityKeyAction.observeForever {
|
||||
prefs.keyboard.utilityKeyAction.asFlow().collectLatestIn(scope) {
|
||||
updateActiveEvaluators()
|
||||
}
|
||||
activeState.collectLatestIn(scope) {
|
||||
@@ -164,10 +166,10 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
editorInstance.activeContentFlow.collectIn(scope) { content ->
|
||||
resetSuggestions(content)
|
||||
}
|
||||
prefs.devtools.enabled.observeForever {
|
||||
prefs.devtools.enabled.asFlow().collectLatestIn(scope) {
|
||||
reevaluateDebugFlags()
|
||||
}
|
||||
prefs.devtools.showDragAndDropHelpers.observeForever {
|
||||
prefs.devtools.showDragAndDropHelpers.asFlow().collectLatestIn(scope) {
|
||||
reevaluateDebugFlags()
|
||||
}
|
||||
}
|
||||
@@ -237,7 +239,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
return subtypeManager.subtypes.size > 1
|
||||
}
|
||||
|
||||
fun toggleOneHandedMode() {
|
||||
suspend fun toggleOneHandedMode() {
|
||||
prefs.keyboard.oneHandedModeEnabled.set(!prefs.keyboard.oneHandedModeEnabled.get())
|
||||
}
|
||||
|
||||
@@ -415,27 +417,30 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
/**
|
||||
* Handles a [KeyCode.DELETE] event.
|
||||
*/
|
||||
private fun handleDelete() {
|
||||
private fun handleBackwardDelete(unit: OperationUnit) {
|
||||
if (inputEventDispatcher.isPressed(KeyCode.SHIFT)) {
|
||||
return handleForwardDelete(unit)
|
||||
}
|
||||
activeState.batchEdit {
|
||||
it.isManualSelectionMode = false
|
||||
it.isManualSelectionModeStart = false
|
||||
it.isManualSelectionModeEnd = false
|
||||
}
|
||||
revertPreviouslyAcceptedCandidate()
|
||||
editorInstance.deleteBackwards()
|
||||
editorInstance.deleteBackwards(unit)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a [KeyCode.DELETE_WORD] event.
|
||||
* Handles a [KeyCode.FORWARD_DELETE] event.
|
||||
*/
|
||||
private fun handleDeleteWord() {
|
||||
private fun handleForwardDelete(unit: OperationUnit) {
|
||||
activeState.batchEdit {
|
||||
it.isManualSelectionMode = false
|
||||
it.isManualSelectionModeStart = false
|
||||
it.isManualSelectionModeEnd = false
|
||||
}
|
||||
revertPreviouslyAcceptedCandidate()
|
||||
editorInstance.deleteWordBackwards()
|
||||
editorInstance.deleteForwards(unit)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -564,7 +569,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()
|
||||
editorInstance.deleteBackwards(OperationUnit.CHARACTERS)
|
||||
editorInstance.commitText(". ")
|
||||
return
|
||||
}
|
||||
@@ -580,7 +585,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
/**
|
||||
* Handles a [KeyCode.TOGGLE_INCOGNITO_MODE] event.
|
||||
*/
|
||||
private fun handleToggleIncognitoMode() {
|
||||
private suspend fun handleToggleIncognitoMode() {
|
||||
prefs.suggestion.forceIncognitoModeFromDynamic.set(!prefs.suggestion.forceIncognitoModeFromDynamic.get())
|
||||
val newState = !activeState.isIncognitoMode
|
||||
activeState.isIncognitoMode = newState
|
||||
@@ -606,7 +611,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
private fun handleToggleAutocorrect() {
|
||||
lastToastReference.get()?.cancel()
|
||||
lastToastReference = WeakReference(
|
||||
appContext.showLongToast("Autocorrect toggle is a placeholder and not yet implemented")
|
||||
appContext.showLongToastSync("Autocorrect toggle is a placeholder and not yet implemented")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -716,20 +721,22 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
clipboardManager.primaryClip?.let { clipboardManager.deleteClip(it) }
|
||||
}
|
||||
clipboardManager.updatePrimaryClip(null)
|
||||
appContext.showShortToast(R.string.clipboard__cleared_primary_clip)
|
||||
appContext.showShortToastSync(R.string.clipboard__cleared_primary_clip)
|
||||
}
|
||||
KeyCode.TOGGLE_COMPACT_LAYOUT -> toggleOneHandedMode()
|
||||
KeyCode.COMPACT_LAYOUT_TO_LEFT -> {
|
||||
KeyCode.TOGGLE_COMPACT_LAYOUT -> scope.launch { toggleOneHandedMode() }
|
||||
KeyCode.COMPACT_LAYOUT_TO_LEFT -> scope.launch {
|
||||
prefs.keyboard.oneHandedMode.set(OneHandedMode.START)
|
||||
toggleOneHandedMode()
|
||||
}
|
||||
KeyCode.COMPACT_LAYOUT_TO_RIGHT -> {
|
||||
KeyCode.COMPACT_LAYOUT_TO_RIGHT -> scope.launch {
|
||||
prefs.keyboard.oneHandedMode.set(OneHandedMode.END)
|
||||
toggleOneHandedMode()
|
||||
}
|
||||
KeyCode.DELETE -> handleDelete()
|
||||
KeyCode.DELETE_WORD -> handleDeleteWord()
|
||||
KeyCode.DELETE -> handleBackwardDelete(OperationUnit.CHARACTERS)
|
||||
KeyCode.DELETE_WORD -> handleBackwardDelete(OperationUnit.WORDS)
|
||||
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()
|
||||
@@ -753,7 +760,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
}
|
||||
KeyCode.SYSTEM_PREV_INPUT_METHOD -> FlorisImeService.switchToPrevInputMethod()
|
||||
KeyCode.SYSTEM_NEXT_INPUT_METHOD -> FlorisImeService.switchToNextInputMethod()
|
||||
KeyCode.TOGGLE_SMARTBAR_VISIBILITY -> {
|
||||
KeyCode.TOGGLE_SMARTBAR_VISIBILITY -> scope.launch {
|
||||
prefs.smartbar.enabled.let { it.set(!it.get()) }
|
||||
}
|
||||
KeyCode.TOGGLE_ACTIONS_OVERFLOW -> {
|
||||
@@ -762,7 +769,7 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
|
||||
KeyCode.TOGGLE_ACTIONS_EDITOR -> {
|
||||
activeState.isActionsEditorVisible = !activeState.isActionsEditorVisible
|
||||
}
|
||||
KeyCode.TOGGLE_INCOGNITO_MODE -> handleToggleIncognitoMode()
|
||||
KeyCode.TOGGLE_INCOGNITO_MODE -> scope.launch { handleToggleIncognitoMode() }
|
||||
KeyCode.TOGGLE_AUTOCORRECT -> handleToggleAutocorrect()
|
||||
KeyCode.UNDO -> editorInstance.performUndo()
|
||||
KeyCode.VIEW_CHARACTERS -> activeState.keyboardMode = KeyboardMode.CHARACTERS
|
||||
@@ -864,6 +871,20 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package dev.patrickgold.florisboard.ime.keyboard
|
||||
|
||||
import android.content.Context
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
@@ -78,7 +78,7 @@ data class DebugLayoutComputationResult(
|
||||
* Class which manages layout loading and caching.
|
||||
*/
|
||||
class LayoutManager(context: Context) {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val appContext by context.appContext()
|
||||
private val extensionManager by context.extensionManager()
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
|
||||
@@ -16,13 +16,12 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.media.emoji
|
||||
|
||||
import dev.patrickgold.florisboard.app.AppPrefs
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceModel
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogError
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceSerializer
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@Serializable
|
||||
@@ -76,7 +75,7 @@ data class EmojiHistory(
|
||||
object EmojiHistoryHelper {
|
||||
private var emojiGuard = Mutex(locked = false)
|
||||
|
||||
suspend fun markEmojiUsed(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
suspend fun markEmojiUsed(prefs: FlorisPreferenceModel, emoji: Emoji): Unit = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
@@ -121,7 +120,7 @@ object EmojiHistoryHelper {
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun pinEmoji(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
suspend fun pinEmoji(prefs: FlorisPreferenceModel, emoji: Emoji): Unit = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
@@ -138,7 +137,7 @@ object EmojiHistoryHelper {
|
||||
prefs.emoji.historyData.set(dataMut.build())
|
||||
}
|
||||
|
||||
suspend fun unpinEmoji(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
suspend fun unpinEmoji(prefs: FlorisPreferenceModel, emoji: Emoji): Unit = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
@@ -155,7 +154,7 @@ object EmojiHistoryHelper {
|
||||
prefs.emoji.historyData.set(dataMut.build())
|
||||
}
|
||||
|
||||
suspend fun moveEmoji(prefs: AppPrefs, emoji: Emoji, offset: Int) = emojiGuard.withLock {
|
||||
suspend fun moveEmoji(prefs: FlorisPreferenceModel, emoji: Emoji, offset: Int): Unit = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get() || offset == 0) {
|
||||
return
|
||||
}
|
||||
@@ -175,7 +174,7 @@ object EmojiHistoryHelper {
|
||||
prefs.emoji.historyData.set(dataMut.build())
|
||||
}
|
||||
|
||||
suspend fun removeEmoji(prefs: AppPrefs, emoji: Emoji) = emojiGuard.withLock {
|
||||
suspend fun removeEmoji(prefs: FlorisPreferenceModel, emoji: Emoji): Unit = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
@@ -195,7 +194,7 @@ object EmojiHistoryHelper {
|
||||
prefs.emoji.historyData.set(dataMut.build())
|
||||
}
|
||||
|
||||
suspend fun deleteHistory(prefs: AppPrefs) = emojiGuard.withLock {
|
||||
suspend fun deleteHistory(prefs: FlorisPreferenceModel): Unit = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
@@ -203,7 +202,7 @@ object EmojiHistoryHelper {
|
||||
prefs.emoji.historyData.set(EmojiHistory(pinned = dataMut.pinned, listOf()))
|
||||
}
|
||||
|
||||
suspend fun deletePinned(prefs: AppPrefs) = emojiGuard.withLock {
|
||||
suspend fun deletePinned(prefs: FlorisPreferenceModel): Unit = emojiGuard.withLock {
|
||||
if (!prefs.emoji.historyEnabled.get()) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -39,6 +38,8 @@ import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.shape.GenericShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
|
||||
@@ -46,8 +47,6 @@ import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.PushPin
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.TabRowDefaults
|
||||
@@ -55,6 +54,7 @@ import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
@@ -63,6 +63,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -83,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.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.ime.input.LocalInputFeedbackController
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
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
|
||||
@@ -133,7 +134,7 @@ fun EmojiPaletteView(
|
||||
fullEmojiMappings: EmojiData,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val editorInstance by context.editorInstance()
|
||||
val keyboardManager by context.keyboardManager()
|
||||
@@ -172,7 +173,6 @@ fun EmojiPaletteView(
|
||||
}
|
||||
}
|
||||
var recentlyUsedVersion by remember { mutableIntStateOf(0) }
|
||||
val lazyListState = rememberLazyGridState()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
@Composable
|
||||
@@ -207,22 +207,111 @@ fun EmojiPaletteView(
|
||||
)
|
||||
}
|
||||
|
||||
Column(modifier = modifier) {
|
||||
fun calculatePageNumbers(): Int {
|
||||
return when {
|
||||
!emojiHistoryEnabled -> EmojiCategoryValues.size - 1
|
||||
else -> EmojiCategoryValues.size
|
||||
}
|
||||
}
|
||||
|
||||
fun pageNumberToCategory(pageNumber: Int): EmojiCategory {
|
||||
return when {
|
||||
!emojiHistoryEnabled -> EmojiCategoryValues[pageNumber + 1]
|
||||
else -> EmojiCategoryValues[pageNumber]
|
||||
}
|
||||
}
|
||||
|
||||
fun categoryToPageNumber(category: EmojiCategory): Int {
|
||||
return if (emojiHistoryEnabled) {
|
||||
EmojiCategoryValues.indexOf(category)
|
||||
} else {
|
||||
EmojiCategoryValues.indexOf(category) - 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun EmojiCategoriesTabRow(
|
||||
activeCategory: EmojiCategory,
|
||||
onCategoryChange: (EmojiCategory) -> Unit,
|
||||
) {
|
||||
val inputFeedbackController = LocalInputFeedbackController.current
|
||||
val selectedTabIndex = categoryToPageNumber(activeCategory)
|
||||
val style = rememberSnyggThemeQuery(FlorisImeUi.MediaEmojiTab.elementName)
|
||||
TabRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(FlorisImeSizing.smartbarHeight),
|
||||
selectedTabIndex = selectedTabIndex,
|
||||
containerColor = Color.Transparent,
|
||||
contentColor = style.foreground(),
|
||||
indicator = { tabPositions ->
|
||||
val style = rememberSnyggThemeQuery(
|
||||
elementName = FlorisImeUi.MediaEmojiTab.elementName,
|
||||
selector = SnyggSelector.FOCUS,
|
||||
)
|
||||
TabRowDefaults.PrimaryIndicator(
|
||||
Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex]),
|
||||
height = 4.dp,
|
||||
color = style.foreground(),
|
||||
)
|
||||
},
|
||||
) {
|
||||
for (category in EmojiCategoryValues) {
|
||||
if (category == EmojiCategory.RECENTLY_USED && !emojiHistoryEnabled) {
|
||||
continue
|
||||
}
|
||||
Tab(
|
||||
onClick = {
|
||||
inputFeedbackController.keyPress(TextKeyData.UNSPECIFIED)
|
||||
onCategoryChange(category)
|
||||
},
|
||||
selected = activeCategory == category,
|
||||
icon = { SnyggIcon(
|
||||
elementName = FlorisImeUi.MediaEmojiTab.elementName,
|
||||
selector = if (activeCategory == category) SnyggSelector.FOCUS else SnyggSelector.NONE,
|
||||
modifier = Modifier.size(ButtonDefaults.IconSize),
|
||||
imageVector = category.icon(),
|
||||
) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
) {
|
||||
val pagerState = rememberPagerState(
|
||||
pageCount = { calculatePageNumbers() }
|
||||
)
|
||||
|
||||
// Reset the pager to the first page when emojiHistory is enabled
|
||||
LaunchedEffect(emojiHistoryEnabled) {
|
||||
pagerState.animateScrollToPage(0)
|
||||
}
|
||||
|
||||
EmojiCategoriesTabRow(
|
||||
activeCategory = activeCategory,
|
||||
onCategoryChange = { category ->
|
||||
scope.launch { lazyListState.scrollToItem(0) }
|
||||
activeCategory = category
|
||||
scope.launch { pagerState.animateScrollToPage(categoryToPageNumber(activeCategory)) }
|
||||
},
|
||||
emojiHistoryEnabled = emojiHistoryEnabled,
|
||||
)
|
||||
HorizontalPager(pagerState, beyondViewportPageCount = 1) { page ->
|
||||
// Every page needs its own lazyGridState in order to scroll correctly
|
||||
val lazyGridState = rememberLazyGridState()
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
) {
|
||||
val emojiMapping = if (activeCategory == EmojiCategory.RECENTLY_USED) {
|
||||
// Update the lazyGridState and active category on scroll
|
||||
LaunchedEffect(pagerState) {
|
||||
snapshotFlow { pagerState.currentPage }.collect { page ->
|
||||
lazyGridState.scrollToItem(0)
|
||||
activeCategory = pageNumberToCategory(page)
|
||||
recentlyUsedVersion++
|
||||
}
|
||||
}
|
||||
|
||||
val category = pageNumberToCategory(page)
|
||||
val emojiMapping = if (category == EmojiCategory.RECENTLY_USED) {
|
||||
// Purposely using remember here to prevent recomposition, as this would cause rapid
|
||||
// emoji changes for the user when in recently used category.
|
||||
remember(recentlyUsedVersion) {
|
||||
@@ -237,63 +326,68 @@ fun EmojiPaletteView(
|
||||
EmojiMappingForView(
|
||||
pinned = emptyList(),
|
||||
recent = emptyList(),
|
||||
simple = emojiMappings[activeCategory]!!,
|
||||
simple = emojiMappings[category]!!,
|
||||
)
|
||||
}
|
||||
|
||||
val isEmojiHistoryEmpty = emojiMapping.pinned.isEmpty() && emojiMapping.recent.isEmpty()
|
||||
if (activeCategory == EmojiCategory.RECENTLY_USED && deviceLocked) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(all = 8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(R.string.emoji__history__phone_locked_message),
|
||||
)
|
||||
}
|
||||
} else if (activeCategory == EmojiCategory.RECENTLY_USED && isEmojiHistoryEmpty) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(all = 8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(R.string.emoji__history__empty_message),
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
text = stringRes(R.string.emoji__history__usage_tip),
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
} else key(emojiMapping) {
|
||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
|
||||
LazyVerticalGrid(
|
||||
when (category) {
|
||||
EmojiCategory.RECENTLY_USED if deviceLocked -> {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.florisScrollbar(lazyListState),
|
||||
columns = GridCells.Adaptive(minSize = EmojiBaseWidth),
|
||||
state = lazyListState,
|
||||
.padding(all = 8.dp),
|
||||
) {
|
||||
if (emojiMapping.pinned.isNotEmpty()) {
|
||||
header("header_pinned") {
|
||||
GridHeader(text = stringRes(R.string.emoji__history__pinned))
|
||||
Text(
|
||||
text = stringRes(R.string.emoji__history__phone_locked_message),
|
||||
)
|
||||
}
|
||||
}
|
||||
EmojiCategory.RECENTLY_USED if isEmojiHistoryEmpty -> {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(all = 8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(R.string.emoji__history__empty_message),
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
text = stringRes(R.string.emoji__history__usage_tip),
|
||||
fontStyle = FontStyle.Italic,
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> key(emojiMapping) {
|
||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
|
||||
LazyVerticalGrid(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.florisScrollbar(lazyGridState),
|
||||
columns = GridCells.Adaptive(minSize = EmojiBaseWidth),
|
||||
state = lazyGridState,
|
||||
) {
|
||||
if (emojiMapping.pinned.isNotEmpty()) {
|
||||
header("header_pinned") {
|
||||
GridHeader(text = stringRes(R.string.emoji__history__pinned))
|
||||
}
|
||||
items(emojiMapping.pinned) { emojiSet ->
|
||||
EmojiKeyWrapper(emojiSet, isPinned = true)
|
||||
}
|
||||
}
|
||||
items(emojiMapping.pinned) { emojiSet ->
|
||||
EmojiKeyWrapper(emojiSet, isPinned = true)
|
||||
if (emojiMapping.recent.isNotEmpty()) {
|
||||
header("header_recent") {
|
||||
GridHeader(text = stringRes(R.string.emoji__history__recent))
|
||||
}
|
||||
items(emojiMapping.recent) { emojiSet ->
|
||||
EmojiKeyWrapper(emojiSet, isRecent = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (emojiMapping.recent.isNotEmpty()) {
|
||||
header("header_recent") {
|
||||
GridHeader(text = stringRes(R.string.emoji__history__recent))
|
||||
}
|
||||
items(emojiMapping.recent) { emojiSet ->
|
||||
EmojiKeyWrapper(emojiSet, isRecent = true)
|
||||
}
|
||||
}
|
||||
if (emojiMapping.simple.isNotEmpty()) {
|
||||
items(emojiMapping.simple) { emojiSet ->
|
||||
EmojiKeyWrapper(emojiSet)
|
||||
if (emojiMapping.simple.isNotEmpty()) {
|
||||
items(emojiMapping.simple) { emojiSet ->
|
||||
EmojiKeyWrapper(emojiSet)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -303,59 +397,6 @@ fun EmojiPaletteView(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EmojiCategoriesTabRow(
|
||||
activeCategory: EmojiCategory,
|
||||
onCategoryChange: (EmojiCategory) -> Unit,
|
||||
emojiHistoryEnabled: Boolean,
|
||||
) {
|
||||
val inputFeedbackController = LocalInputFeedbackController.current
|
||||
val selectedTabIndex = if (emojiHistoryEnabled) {
|
||||
EmojiCategoryValues.indexOf(activeCategory)
|
||||
} else {
|
||||
EmojiCategoryValues.indexOf(activeCategory) - 1
|
||||
}
|
||||
val style = rememberSnyggThemeQuery(FlorisImeUi.MediaEmojiTab.elementName)
|
||||
TabRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(FlorisImeSizing.smartbarHeight),
|
||||
selectedTabIndex = selectedTabIndex,
|
||||
containerColor = Color.Transparent,
|
||||
contentColor = style.foreground(),
|
||||
indicator = { tabPositions ->
|
||||
val style = rememberSnyggThemeQuery(
|
||||
elementName = FlorisImeUi.MediaEmojiTab.elementName,
|
||||
selector = SnyggSelector.FOCUS,
|
||||
)
|
||||
TabRowDefaults.PrimaryIndicator(
|
||||
Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex]),
|
||||
height = 4.dp,
|
||||
color = style.foreground(),
|
||||
)
|
||||
},
|
||||
) {
|
||||
for (category in EmojiCategoryValues) {
|
||||
if (category == EmojiCategory.RECENTLY_USED && !emojiHistoryEnabled) {
|
||||
continue
|
||||
}
|
||||
Tab(
|
||||
onClick = {
|
||||
inputFeedbackController.keyPress(TextKeyData.UNSPECIFIED)
|
||||
onCategoryChange(category)
|
||||
},
|
||||
selected = activeCategory == category,
|
||||
icon = { SnyggIcon(
|
||||
elementName = FlorisImeUi.MediaEmojiTab.elementName,
|
||||
selector = if (activeCategory == category) SnyggSelector.FOCUS else SnyggSelector.NONE,
|
||||
modifier = Modifier.size(ButtonDefaults.IconSize),
|
||||
imageVector = category.icon(),
|
||||
) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EmojiKey(
|
||||
emojiSet: EmojiSet,
|
||||
@@ -497,7 +538,7 @@ private fun EmojiHistoryPopup(
|
||||
onHistoryAction: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val scope = rememberCoroutineScope()
|
||||
val emojiKeyHeight = FlorisImeSizing.smartbarHeight
|
||||
val context = LocalContext.current
|
||||
|
||||
@@ -20,7 +20,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.stream.Collectors
|
||||
import android.content.Context
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.core.Subtype
|
||||
import dev.patrickgold.florisboard.ime.editor.EditorContent
|
||||
import dev.patrickgold.florisboard.ime.nlp.EmojiSuggestionCandidate
|
||||
@@ -41,7 +41,7 @@ import io.github.reactivecircus.cache4k.Cache
|
||||
class EmojiSuggestionProvider(private val context: Context) : SuggestionProvider {
|
||||
override val providerId = "org.florisboard.nlp.providers.emoji"
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val lettersRegex = "^[A-Za-z]*$".toRegex()
|
||||
|
||||
private val cachedEmojiMappings = Cache.Builder().build<FlorisLocale, EmojiDataBySkinTone>()
|
||||
@@ -71,7 +71,7 @@ class EmojiSuggestionProvider(private val context: Context) : SuggestionProvider
|
||||
val candidates = withContext(Dispatchers.Default) {
|
||||
emojis.parallelStream()
|
||||
.filter { emoji ->
|
||||
emoji.name.contains(query, ignoreCase = true) &&
|
||||
emoji.name.contains(query, ignoreCase = true) ||
|
||||
emoji.keywords.any { it.contains(query, ignoreCase = true) }
|
||||
}
|
||||
.limit(maxCandidateCount.toLong())
|
||||
|
||||
@@ -20,7 +20,7 @@ import android.content.Context
|
||||
import android.os.SystemClock
|
||||
import android.util.LruCache
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.clipboardManager
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.ime.clipboard.provider.ClipboardItem
|
||||
@@ -43,8 +43,8 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
import org.florisboard.lib.kotlin.guardedByLock
|
||||
import org.florisboard.lib.kotlin.collectLatestIn
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.properties.Delegates
|
||||
@@ -54,7 +54,7 @@ private const val BLANK_STR_PATTERN = "^\\s*$"
|
||||
class NlpManager(context: Context) {
|
||||
private val blankStrRegex = Regex(BLANK_STR_PATTERN)
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val clipboardManager by context.clipboardManager()
|
||||
private val editorInstance by context.editorInstance()
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
@@ -93,13 +93,13 @@ class NlpManager(context: Context) {
|
||||
clipboardManager.primaryClipFlow.collectLatestIn(scope) {
|
||||
assembleCandidates()
|
||||
}
|
||||
prefs.suggestion.enabled.observeForever {
|
||||
prefs.suggestion.enabled.asFlow().collectLatestIn(scope) {
|
||||
assembleCandidates()
|
||||
}
|
||||
prefs.clipboard.suggestionEnabled.observeForever {
|
||||
prefs.clipboard.suggestionEnabled.asFlow().collectLatestIn(scope) {
|
||||
assembleCandidates()
|
||||
}
|
||||
prefs.emoji.suggestionEnabled.observeForever {
|
||||
prefs.emoji.suggestionEnabled.asFlow().collectLatestIn(scope) {
|
||||
assembleCandidates()
|
||||
}
|
||||
subtypeManager.activeSubtypeFlow.collectLatestIn(scope) { subtype ->
|
||||
@@ -317,8 +317,10 @@ class NlpManager(context: Context) {
|
||||
}*/
|
||||
val isSelection = editorInstance.activeContent.selection.isSelectionMode
|
||||
val isExpanded = list1.isNullOrEmpty() && list2.isNullOrEmpty() || isSelection
|
||||
prefs.smartbar.sharedActionsExpandWithAnimation.set(false)
|
||||
prefs.smartbar.sharedActionsExpanded.set(isExpanded)
|
||||
scope.launch {
|
||||
prefs.smartbar.sharedActionsExpandWithAnimation.set(false)
|
||||
prefs.smartbar.sharedActionsExpanded.set(isExpanded)
|
||||
}
|
||||
}
|
||||
|
||||
fun addToDebugOverlay(word: String, info: SpellingResult) {
|
||||
|
||||
@@ -26,14 +26,16 @@ import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.filled.ZoomOutMap
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.input.LocalInputFeedbackController
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.lib.compose.stringRes
|
||||
import org.florisboard.lib.compose.stringRes
|
||||
import kotlinx.coroutines.launch
|
||||
import org.florisboard.lib.snygg.ui.SnyggColumn
|
||||
import org.florisboard.lib.snygg.ui.SnyggIcon
|
||||
import org.florisboard.lib.snygg.ui.SnyggIconButton
|
||||
@@ -44,7 +46,8 @@ fun RowScope.OneHandedPanel(
|
||||
panelSide: OneHandedMode,
|
||||
weight: Float,
|
||||
) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val scope = rememberCoroutineScope()
|
||||
val inputFeedbackController = LocalInputFeedbackController.current
|
||||
|
||||
SnyggColumn(
|
||||
@@ -58,8 +61,10 @@ fun RowScope.OneHandedPanel(
|
||||
SnyggIconButton(
|
||||
FlorisImeUi.OneHandedPanelButton.elementName,
|
||||
onClick = {
|
||||
inputFeedbackController.keyPress()
|
||||
prefs.keyboard.oneHandedModeEnabled.set(false)
|
||||
scope.launch {
|
||||
inputFeedbackController.keyPress()
|
||||
prefs.keyboard.oneHandedModeEnabled.set(false)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -74,8 +79,10 @@ fun RowScope.OneHandedPanel(
|
||||
SnyggIconButton(
|
||||
FlorisImeUi.OneHandedPanelButton.elementName,
|
||||
onClick = {
|
||||
inputFeedbackController.keyPress()
|
||||
prefs.keyboard.oneHandedMode.set(panelSide)
|
||||
scope.launch {
|
||||
inputFeedbackController.keyPress()
|
||||
prefs.keyboard.oneHandedMode.set(panelSide)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
|
||||
@@ -24,12 +24,15 @@ 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
|
||||
@@ -38,6 +41,8 @@ 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,
|
||||
@@ -45,6 +50,13 @@ 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,
|
||||
|
||||
@@ -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 dev.patrickgold.florisboard.lib.compose.conditional
|
||||
import org.florisboard.lib.compose.conditional
|
||||
|
||||
private val SheetOutOfBoundsBgColorInactive = Color(0x00000000)
|
||||
private val SheetOutOfBoundsBgColorActive = Color(0x52000000)
|
||||
|
||||
@@ -20,7 +20,6 @@ import androidx.compose.foundation.gestures.awaitEachGesture
|
||||
import androidx.compose.foundation.gestures.awaitFirstDown
|
||||
import androidx.compose.foundation.gestures.waitForUpOrCancellation
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.width
|
||||
@@ -40,16 +39,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.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.nlp.ClipboardSuggestionCandidate
|
||||
import dev.patrickgold.florisboard.ime.nlp.SuggestionCandidate
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
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
|
||||
@@ -62,7 +61,7 @@ val CandidatesRowScrollbarHeight = 2.dp
|
||||
|
||||
@Composable
|
||||
fun CandidatesRow(modifier: Modifier = Modifier) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val nlpManager by context.nlpManager()
|
||||
|
||||
@@ -17,12 +17,14 @@
|
||||
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
|
||||
@@ -33,10 +35,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.snygg.SnyggPropertySet
|
||||
import org.florisboard.lib.compose.florisHorizontalScroll
|
||||
import org.florisboard.lib.snygg.SnyggSinglePropertySet
|
||||
import org.florisboard.lib.snygg.ui.rememberSnyggThemeQuery
|
||||
|
||||
@@ -57,7 +59,8 @@ fun InlineSuggestionsUi(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
val almostEmptyRect = remember { android.graphics.Rect(0, 0, 1, 1) }
|
||||
val numPopupsShowing by GlobalStateNumPopupsShowing.collectAsState()
|
||||
val isZOrderedOnTop = numPopupsShowing == 0
|
||||
|
||||
Row(
|
||||
modifier
|
||||
@@ -78,19 +81,14 @@ 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,
|
||||
)
|
||||
// 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.visibility = if (view.clipBounds.isEmpty) View.INVISIBLE else View.VISIBLE
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.absoluteOffset
|
||||
@@ -43,16 +42,19 @@ 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.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.nlp.NlpInlineAutofill
|
||||
import dev.patrickgold.florisboard.ime.smartbar.quickaction.QuickActionButton
|
||||
@@ -60,12 +62,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 dev.patrickgold.jetpref.datastore.ui.vectorResource
|
||||
import kotlinx.coroutines.launch
|
||||
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
|
||||
@@ -89,7 +91,7 @@ private val NoAnimationTween = tween<Float>(0)
|
||||
|
||||
@Composable
|
||||
fun Smartbar() {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val smartbarEnabled by prefs.smartbar.enabled.observeAsState()
|
||||
val extendedActionsPlacement by prefs.smartbar.extendedActionsPlacement.observeAsState()
|
||||
|
||||
@@ -138,10 +140,11 @@ fun Smartbar() {
|
||||
|
||||
@Composable
|
||||
private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val nlpManager by context.nlpManager()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val inlineSuggestions by NlpInlineAutofill.suggestions.collectAsState()
|
||||
LaunchedEffect(inlineSuggestions) {
|
||||
@@ -164,7 +167,9 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
if (/* was */ sharedActionsExpanded) {
|
||||
keyboardManager.activeState.isActionsOverflowVisible = false
|
||||
}
|
||||
prefs.smartbar.sharedActionsExpanded.set(!sharedActionsExpanded)
|
||||
scope.launch {
|
||||
prefs.smartbar.sharedActionsExpanded.set(!sharedActionsExpanded)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.sizeIn(maxHeight = FlorisImeSizing.smartbarHeight).aspectRatio(1f)
|
||||
) {
|
||||
@@ -182,7 +187,7 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
} else {
|
||||
Icons.AutoMirrored.Default.KeyboardArrowRight
|
||||
}
|
||||
val incognitoIcon = vectorResource(id = R.drawable.ic_incognito)
|
||||
val incognitoIcon = ImageVector.vectorResource(id = R.drawable.ic_incognito)
|
||||
val incognitoDisplayMode = prefs.keyboard.incognitoDisplayMode.observeAsState()
|
||||
val isIncognitoMode = keyboardManager.activeState.isIncognitoMode
|
||||
val icon = if (isIncognitoMode) {
|
||||
@@ -244,7 +249,9 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
if (/* was */ extendedActionsExpanded) {
|
||||
keyboardManager.activeState.isActionsOverflowVisible = false
|
||||
}
|
||||
prefs.smartbar.extendedActionsExpanded.set(!extendedActionsExpanded)
|
||||
scope.launch {
|
||||
prefs.smartbar.extendedActionsExpanded.set(!extendedActionsExpanded)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.sizeIn(maxHeight = FlorisImeSizing.smartbarHeight).aspectRatio(1f)
|
||||
) {
|
||||
@@ -304,7 +311,9 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
|
||||
SideEffect {
|
||||
if (!shouldAnimate) {
|
||||
prefs.smartbar.sharedActionsExpandWithAnimation.set(true)
|
||||
scope.launch {
|
||||
prefs.smartbar.sharedActionsExpandWithAnimation.set(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,7 +368,7 @@ private fun SmartbarMainRow(modifier: Modifier = Modifier) {
|
||||
|
||||
@Composable
|
||||
private fun SmartbarSecondaryRow(modifier: Modifier = Modifier) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val smartbarLayout by prefs.smartbar.layout.observeAsState()
|
||||
val secondaryRowStyle = rememberSnyggThemeQuery(FlorisImeUi.SmartbarExtendedActionsRow.elementName)
|
||||
val windowStyle = rememberSnyggThemeQuery(FlorisImeUi.Window.elementName)
|
||||
|
||||
@@ -25,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,6 +87,7 @@ 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
|
||||
|
||||
@@ -86,6 +86,7 @@ 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(
|
||||
),
|
||||
|
||||
@@ -25,7 +25,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
||||
import androidx.compose.foundation.lazy.grid.LazyGridItemInfo
|
||||
@@ -40,6 +39,7 @@ import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -52,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.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.text.key.KeyCode
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
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.launch
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggButton
|
||||
import org.florisboard.lib.snygg.ui.SnyggColumn
|
||||
import org.florisboard.lib.snygg.ui.SnyggIcon
|
||||
import org.florisboard.lib.snygg.ui.SnyggIconButton
|
||||
@@ -74,8 +74,9 @@ private val DragMarkerAction = QuickAction.InsertKey(TextKeyData(code = KeyCode.
|
||||
|
||||
@Composable
|
||||
fun QuickActionsEditorPanel() {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val keyboardManager by context.keyboardManager()
|
||||
|
||||
// We get the current arrangement once and do not observe on purpose
|
||||
@@ -235,7 +236,9 @@ fun QuickActionsEditorPanel() {
|
||||
dynamicActions.filter { it != NoopAction && it != DragMarkerAction },
|
||||
hiddenActions.filter { it != NoopAction && it != DragMarkerAction },
|
||||
)
|
||||
prefs.smartbar.actionArrangement.set(newActionArrangement)
|
||||
scope.launch {
|
||||
prefs.smartbar.actionArrangement.set(newActionArrangement)
|
||||
}
|
||||
if (keyboardManager.activeState.isActionsEditorVisible) {
|
||||
keyboardManager.activeState.isActionsEditorVisible = false
|
||||
}
|
||||
|
||||
@@ -30,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.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
import dev.patrickgold.florisboard.ime.theme.FlorisImeUi
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
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.compose.stringRes
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggButton
|
||||
import org.florisboard.lib.snygg.ui.SnyggText
|
||||
|
||||
@Composable
|
||||
fun QuickActionsOverflowPanel() {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.smartbar.quickaction
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@@ -28,7 +29,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.smartbar.SmartbarLayout
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKeyData
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
@@ -37,12 +38,13 @@ import org.florisboard.lib.snygg.ui.SnyggRow
|
||||
|
||||
internal val ToggleOverflowPanelAction = QuickAction.InsertKey(TextKeyData.TOGGLE_ACTIONS_OVERFLOW)
|
||||
|
||||
@SuppressLint("UnusedBoxWithConstraintsScope")
|
||||
@Composable
|
||||
fun QuickActionsRow(
|
||||
elementName: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) = with(LocalDensity.current) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.smartbar.IncognitoDisplayMode
|
||||
import dev.patrickgold.florisboard.ime.smartbar.InlineSuggestionsStyleCache
|
||||
import dev.patrickgold.florisboard.ime.smartbar.Smartbar
|
||||
@@ -49,7 +49,7 @@ fun TextInputLayout(
|
||||
val context = LocalContext.current
|
||||
val keyboardManager by context.keyboardManager()
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
|
||||
val state by keyboardManager.activeState.collectAsState()
|
||||
val evaluator by keyboardManager.activeEvaluator.collectAsState()
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package dev.patrickgold.florisboard.ime.text.gestures
|
||||
|
||||
import android.content.Context
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.nlp.WordSuggestionCandidate
|
||||
import dev.patrickgold.florisboard.ime.text.keyboard.TextKey
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
@@ -39,7 +39,7 @@ class GlideTypingManager(context: Context) : GlideTypingGesture.Listener {
|
||||
private const val MAX_SUGGESTION_COUNT = 8
|
||||
}
|
||||
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
private val nlpManager by context.nlpManager()
|
||||
private val subtypeManager by context.subtypeManager()
|
||||
|
||||
@@ -19,7 +19,7 @@ package dev.patrickgold.florisboard.ime.text.gestures
|
||||
import android.view.MotionEvent
|
||||
import android.view.VelocityTracker
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.lib.Pointer
|
||||
import dev.patrickgold.florisboard.lib.PointerMap
|
||||
import dev.patrickgold.florisboard.lib.devtools.LogTopic
|
||||
@@ -39,7 +39,7 @@ abstract class SwipeGesture {
|
||||
* @property listener The listener to report detected swipes to.
|
||||
*/
|
||||
class Detector(private val listener: Listener) {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
|
||||
var isEnabled: Boolean = true
|
||||
private var pointerMap: PointerMap<GesturePointer> = PointerMap { GesturePointer() }
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package dev.patrickgold.florisboard.ime.text.keyboard
|
||||
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.ime.keyboard.AbstractKeyData
|
||||
import dev.patrickgold.florisboard.ime.keyboard.ComputingEvaluator
|
||||
import dev.patrickgold.florisboard.ime.keyboard.Key
|
||||
@@ -246,7 +246,7 @@ class TextKey(override val data: AbstractKeyData) : Key(data) {
|
||||
else -> null
|
||||
}
|
||||
} else if (!data.isSpaceKey() || data.type == KeyType.NUMERIC) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
computedPopups.getPopupKeys(prefs.keyboard.keyHintConfiguration()).hint.let { hintData ->
|
||||
if (hintData?.isSpaceKey() == false) {
|
||||
hintedLabel = hintData.asString(isForDisplay = true)
|
||||
|
||||
@@ -56,9 +56,10 @@ import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.toSize
|
||||
import dev.patrickgold.florisboard.FlorisImeService
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.editorInstance
|
||||
import dev.patrickgold.florisboard.glideTypingManager
|
||||
import dev.patrickgold.florisboard.ime.editor.OperationUnit
|
||||
import dev.patrickgold.florisboard.ime.input.InputEventDispatcher
|
||||
import dev.patrickgold.florisboard.ime.keyboard.ComputingEvaluator
|
||||
import dev.patrickgold.florisboard.ime.keyboard.FlorisImeSizing
|
||||
@@ -78,7 +79,6 @@ import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.FlorisRect
|
||||
import dev.patrickgold.florisboard.lib.Pointer
|
||||
import dev.patrickgold.florisboard.lib.PointerMap
|
||||
import dev.patrickgold.florisboard.lib.compose.DisposableLifecycleEffect
|
||||
import dev.patrickgold.florisboard.lib.devtools.LogTopic
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogDebug
|
||||
import dev.patrickgold.florisboard.lib.observeAsTransformingState
|
||||
@@ -88,6 +88,7 @@ import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.channels.onFailure
|
||||
import kotlinx.coroutines.isActive
|
||||
import org.florisboard.lib.android.isOrientationLandscape
|
||||
import org.florisboard.lib.compose.DisposableLifecycleEffect
|
||||
import org.florisboard.lib.snygg.SnyggSelector
|
||||
import org.florisboard.lib.snygg.ui.SnyggBox
|
||||
import org.florisboard.lib.snygg.ui.SnyggIcon
|
||||
@@ -104,7 +105,7 @@ fun TextKeyboardLayout(
|
||||
evaluator: ComputingEvaluator,
|
||||
isPreview: Boolean = false,
|
||||
): Unit = with(LocalDensity.current) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val context = LocalContext.current
|
||||
val configuration = LocalConfiguration.current
|
||||
val glideTypingManager by context.glideTypingManager()
|
||||
@@ -132,7 +133,7 @@ fun TextKeyboardLayout(
|
||||
controller.onTouchEventInternal(event)
|
||||
controller.popupUiController.hide()
|
||||
event.recycle()
|
||||
} catch (e: Throwable) {
|
||||
} catch (_: Throwable) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
@@ -337,7 +338,7 @@ private fun TextKeyButton(
|
||||
key.label?.let { label ->
|
||||
var customLabel = label
|
||||
if (key.computedData.code == KeyCode.SPACE) {
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val spaceBarMode by prefs.keyboard.spaceBarMode.observeAsState()
|
||||
when (spaceBarMode) {
|
||||
SpaceBarMode.NOTHING -> return@let
|
||||
@@ -385,7 +386,7 @@ private fun TextKeyButton(
|
||||
private class TextKeyboardLayoutController(
|
||||
context: Context,
|
||||
) : SwipeGesture.Listener, GlideTypingGesture.Listener {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val editorInstance by context.editorInstance()
|
||||
private val keyboardManager by context.keyboardManager()
|
||||
|
||||
@@ -498,7 +499,7 @@ private class TextKeyboardLayoutController(
|
||||
if (pointer.hasTriggeredGestureMove && pointer.initialKey?.computedData?.code == KeyCode.DELETE) {
|
||||
val selection = editorInstance.activeContent.selection
|
||||
if (selection.isSelectionMode) {
|
||||
editorInstance.deleteBackwards()
|
||||
editorInstance.deleteBackwards(OperationUnit.CHARACTERS)
|
||||
}
|
||||
}
|
||||
onTouchCancelInternal(event, pointer)
|
||||
@@ -521,7 +522,7 @@ private class TextKeyboardLayoutController(
|
||||
prefs.gestures.deleteKeySwipeLeft.get() != SwipeAction.SELECT_WORDS_PRECISELY) {
|
||||
val selection = editorInstance.activeContent.selection
|
||||
if (selection.isSelectionMode) {
|
||||
editorInstance.deleteBackwards()
|
||||
editorInstance.deleteBackwards(OperationUnit.CHARACTERS)
|
||||
}
|
||||
}
|
||||
onTouchCancelInternal(event, pointer)
|
||||
@@ -765,10 +766,19 @@ private class TextKeyboardLayoutController(
|
||||
}
|
||||
val activeSelection = editorInstance.activeContent.selection
|
||||
if (activeSelection.isValid) {
|
||||
editorInstance.setSelection(
|
||||
(activeSelection.end + event.absUnitCountX + 1).coerceIn(0, activeSelection.end),
|
||||
activeSelection.end,
|
||||
)
|
||||
if (inputEventDispatcher.isPressed(KeyCode.SHIFT)) {
|
||||
// Forward select
|
||||
editorInstance.setSelection(
|
||||
activeSelection.start,
|
||||
(activeSelection.start - event.absUnitCountX + 1).coerceAtLeast(activeSelection.start),
|
||||
)
|
||||
} else {
|
||||
// Backward select
|
||||
editorInstance.setSelection(
|
||||
(activeSelection.end + event.absUnitCountX + 1).coerceIn(0, activeSelection.end),
|
||||
activeSelection.end,
|
||||
)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
@@ -27,13 +27,10 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.keyboardManager
|
||||
import dev.patrickgold.florisboard.lib.observeAsNonNullState
|
||||
import dev.patrickgold.florisboard.themeManager
|
||||
import dev.patrickgold.jetpref.datastore.model.observeAsState
|
||||
import org.florisboard.lib.snygg.SnyggAttributes
|
||||
import org.florisboard.lib.snygg.SnyggQueryAttributes
|
||||
import org.florisboard.lib.snygg.ui.ProvideSnyggTheme
|
||||
import org.florisboard.lib.snygg.ui.rememberSnyggTheme
|
||||
|
||||
@@ -52,10 +49,10 @@ fun FlorisImeTheme(content: @Composable () -> Unit) {
|
||||
val keyboardManager by context.keyboardManager()
|
||||
val themeManager by context.themeManager()
|
||||
|
||||
val prefs by florisPreferenceModel()
|
||||
val prefs by FlorisPreferenceStore
|
||||
val accentColor by prefs.theme.accentColor.observeAsState()
|
||||
|
||||
val activeThemeInfo by themeManager.activeThemeInfo.observeAsNonNullState()
|
||||
val activeThemeInfo by themeManager.activeThemeInfo.collectAsState()
|
||||
val activeConfig = remember(activeThemeInfo) { activeThemeInfo.config }
|
||||
val activeStyle = remember(activeThemeInfo) { activeThemeInfo.stylesheet }
|
||||
|
||||
|
||||
@@ -39,10 +39,8 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import dev.patrickgold.florisboard.R
|
||||
import dev.patrickgold.florisboard.app.florisPreferenceModel
|
||||
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
|
||||
import dev.patrickgold.florisboard.appContext
|
||||
import dev.patrickgold.florisboard.extensionManager
|
||||
import dev.patrickgold.florisboard.ime.smartbar.CachedInlineSuggestionsChipStyleSet
|
||||
@@ -52,70 +50,67 @@ import dev.patrickgold.florisboard.lib.ext.ExtensionMeta
|
||||
import dev.patrickgold.florisboard.lib.io.ZipUtils
|
||||
import dev.patrickgold.florisboard.lib.util.TimeUtils.javaLocalTime
|
||||
import dev.patrickgold.florisboard.lib.util.ViewUtils
|
||||
import java.time.LocalTime
|
||||
import java.util.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.florisboard.lib.kotlin.collectIn
|
||||
import org.florisboard.lib.kotlin.io.FsDir
|
||||
import org.florisboard.lib.kotlin.io.deleteContentsRecursively
|
||||
import org.florisboard.lib.kotlin.io.subDir
|
||||
import org.florisboard.lib.kotlin.io.subFile
|
||||
import org.florisboard.lib.snygg.SnyggStylesheet
|
||||
import org.florisboard.lib.snygg.value.SnyggStaticColorValue
|
||||
import kotlin.properties.Delegates
|
||||
import java.time.LocalTime
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Core class which manages the keyboard theme. Note, that this does not affect the UI theme of the
|
||||
* Settings Activities.
|
||||
*/
|
||||
class ThemeManager(context: Context) {
|
||||
private val prefs by florisPreferenceModel()
|
||||
private val prefs by FlorisPreferenceStore
|
||||
private val appContext by context.appContext()
|
||||
private val extensionManager by context.extensionManager()
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
|
||||
|
||||
private val _indexedThemeConfigs = MutableLiveData(mapOf<ExtensionComponentName, ThemeExtensionComponent>())
|
||||
val indexedThemeConfigs: LiveData<Map<ExtensionComponentName, ThemeExtensionComponent>> get() = _indexedThemeConfigs
|
||||
var previewThemeId: ExtensionComponentName? by Delegates.observable(null) { _, _, _ ->
|
||||
updateActiveTheme()
|
||||
}
|
||||
var previewThemeInfo: ThemeInfo? by Delegates.observable(null) { _, _, _ ->
|
||||
updateActiveTheme()
|
||||
}
|
||||
private val _indexedThemeConfigs = MutableStateFlow(mapOf<ExtensionComponentName, ThemeExtensionComponent>())
|
||||
val indexedThemeConfigs get() = _indexedThemeConfigs.asStateFlow()
|
||||
val previewThemeId = MutableStateFlow<ExtensionComponentName?>(null)
|
||||
val previewThemeInfo = MutableStateFlow<ThemeInfo?>(null)
|
||||
val wallpaperChangedCounter = MutableStateFlow(0)
|
||||
|
||||
private val cachedThemeInfos = mutableListOf<ThemeInfo>()
|
||||
private val activeThemeGuard = Mutex(locked = false)
|
||||
private val _activeThemeInfo = MutableLiveData(ThemeInfo.DEFAULT)
|
||||
val activeThemeInfo: LiveData<ThemeInfo> get() = _activeThemeInfo
|
||||
private val _activeThemeInfo = MutableStateFlow(ThemeInfo.DEFAULT)
|
||||
val activeThemeInfo get() = _activeThemeInfo.asStateFlow()
|
||||
|
||||
init {
|
||||
extensionManager.themes.observeForever { themeExtensions ->
|
||||
val map = buildMap {
|
||||
_indexedThemeConfigs.value = buildMap {
|
||||
for (themeExtension in themeExtensions) {
|
||||
for (themeComponent in themeExtension.themes) {
|
||||
put(ExtensionComponentName(themeExtension.meta.id, themeComponent.id), themeComponent)
|
||||
}
|
||||
}
|
||||
}
|
||||
_indexedThemeConfigs.postValue(map)
|
||||
}
|
||||
indexedThemeConfigs.observeForever {
|
||||
updateActiveTheme {
|
||||
cachedThemeInfos.clear()
|
||||
}
|
||||
indexedThemeConfigs.collectIn(scope) {
|
||||
updateActiveTheme { cachedThemeInfos.clear() }
|
||||
}
|
||||
prefs.theme.mode.observeForever {
|
||||
updateActiveTheme()
|
||||
}
|
||||
prefs.theme.dayThemeId.observeForever {
|
||||
updateActiveTheme()
|
||||
}
|
||||
prefs.theme.nightThemeId.observeForever {
|
||||
combine(
|
||||
prefs.theme.mode.asFlow(),
|
||||
prefs.theme.dayThemeId.asFlow(),
|
||||
prefs.theme.nightThemeId.asFlow(),
|
||||
previewThemeId,
|
||||
previewThemeInfo,
|
||||
wallpaperChangedCounter,
|
||||
) {}.collectIn(scope) {
|
||||
updateActiveTheme()
|
||||
}
|
||||
}
|
||||
@@ -124,56 +119,54 @@ class ThemeManager(context: Context) {
|
||||
* Updates the current theme ref and loads the corresponding theme, as well as notifies all
|
||||
* callback receivers about the new theme.
|
||||
*/
|
||||
fun updateActiveTheme(action: () -> Unit = { }) = scope.launch {
|
||||
activeThemeGuard.withLock {
|
||||
action()
|
||||
previewThemeInfo?.let { previewThemeInfo ->
|
||||
_activeThemeInfo.postValue(previewThemeInfo)
|
||||
return@withLock
|
||||
}
|
||||
val activeName = evaluateActiveThemeName()
|
||||
val cachedInfo = cachedThemeInfos.find { it.name == activeName }
|
||||
if (cachedInfo != null) {
|
||||
_activeThemeInfo.postValue(cachedInfo)
|
||||
return@withLock
|
||||
}
|
||||
val themeExt = extensionManager.getExtensionById(activeName.extensionId) as? ThemeExtension
|
||||
val themeExtRef = themeExt?.sourceRef
|
||||
if (themeExtRef == null) {
|
||||
return@withLock
|
||||
}
|
||||
val themeConfig = themeExt.themes.find { it.id == activeName.componentId }
|
||||
if (themeConfig == null) {
|
||||
return@withLock
|
||||
}
|
||||
// TODO: loaded dir is implemented already...
|
||||
// TODO: this leaks the loaded dir, but at least the state is not kaputt from compose viewpoint
|
||||
val loadedDir = appContext.cacheDir.subDir("loaded").subDir(UUID.randomUUID().toString())
|
||||
runCatching {
|
||||
loadedDir.mkdirs()
|
||||
loadedDir.deleteContentsRecursively()
|
||||
ZipUtils.unzip(appContext, themeExtRef, loadedDir).getOrThrow()
|
||||
flogInfo { "Loaded extension ${themeExt.meta.id} into $loadedDir" }
|
||||
val stylesheetFile = loadedDir.subFile(themeConfig.stylesheetPath())
|
||||
val stylesheetJson = stylesheetFile.readText()
|
||||
SnyggStylesheet.fromJson(stylesheetJson).getOrThrow()
|
||||
}.fold(
|
||||
onSuccess = { newStylesheet ->
|
||||
val newInfo = ThemeInfo(activeName, themeConfig, newStylesheet, loadedDir, null)
|
||||
cachedThemeInfos.add(newInfo)
|
||||
_activeThemeInfo.postValue(newInfo)
|
||||
},
|
||||
onFailure = { cause ->
|
||||
_activeThemeInfo.postValue(ThemeInfo.DEFAULT.copy(
|
||||
loadFailure = LoadFailure(themeExt.meta, themeConfig, cause)
|
||||
))
|
||||
},
|
||||
)
|
||||
suspend fun updateActiveTheme(action: () -> Unit = { }) = activeThemeGuard.withLock {
|
||||
action()
|
||||
previewThemeInfo.value?.let { previewThemeInfo ->
|
||||
_activeThemeInfo.value = previewThemeInfo
|
||||
return@withLock
|
||||
}
|
||||
val activeName = evaluateActiveThemeName()
|
||||
val cachedInfo = cachedThemeInfos.find { it.name == activeName }
|
||||
if (cachedInfo != null) {
|
||||
_activeThemeInfo.value = cachedInfo
|
||||
return@withLock
|
||||
}
|
||||
val themeExt = extensionManager.getExtensionById(activeName.extensionId) as? ThemeExtension
|
||||
val themeExtRef = themeExt?.sourceRef
|
||||
if (themeExtRef == null) {
|
||||
return@withLock
|
||||
}
|
||||
val themeConfig = themeExt.themes.find { it.id == activeName.componentId }
|
||||
if (themeConfig == null) {
|
||||
return@withLock
|
||||
}
|
||||
// TODO: loaded dir is implemented already...
|
||||
// TODO: this leaks the loaded dir, but at least the state is not kaputt from compose viewpoint
|
||||
val loadedDir = appContext.cacheDir.subDir("loaded").subDir(UUID.randomUUID().toString())
|
||||
runCatching {
|
||||
loadedDir.mkdirs()
|
||||
loadedDir.deleteContentsRecursively()
|
||||
ZipUtils.unzip(appContext, themeExtRef, loadedDir).getOrThrow()
|
||||
flogInfo { "Loaded extension ${themeExt.meta.id} into $loadedDir" }
|
||||
val stylesheetFile = loadedDir.subFile(themeConfig.stylesheetPath())
|
||||
val stylesheetJson = stylesheetFile.readText()
|
||||
SnyggStylesheet.fromJson(stylesheetJson).getOrThrow()
|
||||
}.fold(
|
||||
onSuccess = { newStylesheet ->
|
||||
val newInfo = ThemeInfo(activeName, themeConfig, newStylesheet, loadedDir, null)
|
||||
cachedThemeInfos.add(newInfo)
|
||||
_activeThemeInfo.value = newInfo
|
||||
},
|
||||
onFailure = { cause ->
|
||||
_activeThemeInfo.value = ThemeInfo.DEFAULT.copy(
|
||||
loadFailure = LoadFailure(themeExt.meta, themeConfig, cause)
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun evaluateActiveThemeName(): ExtensionComponentName {
|
||||
previewThemeId?.let { return it }
|
||||
previewThemeId.value?.let { return it }
|
||||
return when (prefs.theme.mode.get()) {
|
||||
ThemeMode.ALWAYS_DAY -> {
|
||||
prefs.theme.dayThemeId.get()
|
||||
@@ -202,10 +195,9 @@ class ThemeManager(context: Context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new inline suggestion UI bundle based on the attributes of the given [style].
|
||||
* Creates a new inline suggestion UI bundle.
|
||||
*
|
||||
* @param context The context of the parent view/controller.
|
||||
* @param style The style set which is responsible for styling the chips.
|
||||
*
|
||||
* @return A bundle containing all necessary attributes for the inline suggestion views to properly display.
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import dev.patrickgold.florisboard.lib.devtools.flogDebug
|
||||
import dev.patrickgold.florisboard.themeManager
|
||||
import kotlinx.coroutines.flow.update
|
||||
|
||||
class WallpaperChangeReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
@@ -29,7 +30,8 @@ class WallpaperChangeReceiver : BroadcastReceiver() {
|
||||
@Suppress("DEPRECATION") // We do not retrieve the wallpaper but only listen to changes
|
||||
if (intent.action == Intent.ACTION_WALLPAPER_CHANGED) {
|
||||
flogDebug { "Wallpaper changed" }
|
||||
context.themeManager().value.updateActiveTheme()
|
||||
val themeManager by context.themeManager()
|
||||
themeManager.wallpaperChangedCounter.update { it + 1 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
|
||||
package dev.patrickgold.florisboard.lib
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisallowComposableCalls
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.SnapshotMutationPolicy
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.structuralEqualityPolicy
|
||||
@@ -28,21 +30,16 @@ import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceData
|
||||
import dev.patrickgold.jetpref.datastore.model.PreferenceObserver
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
@SuppressLint("StateFlowValueCalledInComposition")
|
||||
@Composable
|
||||
inline fun <V : Any, R : Any> PreferenceData<V>.observeAsTransformingState(
|
||||
policy: SnapshotMutationPolicy<R> = structuralEqualityPolicy(),
|
||||
crossinline transform: @DisallowComposableCalls (V) -> R,
|
||||
): State<R> {
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
val state = remember(key) { mutableStateOf(transform(get()), policy) }
|
||||
DisposableEffect(this, lifecycleOwner) {
|
||||
val observer = PreferenceObserver<V> { newValue -> state.value = transform(newValue) }
|
||||
observe(lifecycleOwner, observer)
|
||||
onDispose { removeObserver(observer) }
|
||||
return asFlow().let { flow ->
|
||||
flow.map { transform(it) }.collectAsState(transform(flow.value))
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user