Start working on moving classes to a shared Jvm module

This commit is contained in:
Syer10
2022-02-01 19:33:40 -05:00
parent 9a27313936
commit 26d7e70ae0
80 changed files with 647 additions and 175 deletions

View File

@@ -11,4 +11,5 @@ object Config {
const val previewCommit = "b714abddae9f13e91bc53c5daac54aeae564cd2a"
val desktopJvmTarget = JavaVersion.VERSION_16
val androidJvmTarget = JavaVersion.VERSION_11
}

View File

@@ -14,7 +14,13 @@ repositories {
}
kotlin {
android()
android {
compilations {
all {
kotlinOptions.jvmTarget = Config.androidJvmTarget.toString()
}
}
}
jvm("desktop") {
compilations {
all {

View File

@@ -0,0 +1,100 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.core.prefs
import com.russhwolf.settings.ObservableSettings
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.stateIn
internal class AndroidPreference<T>(
private val preferences: ObservableSettings,
private val key: String,
private val defaultValue: T,
private val adapter: Adapter<T>
) : Preference<T> {
interface Adapter<T> {
fun get(key: String, preferences: ObservableSettings): T
fun set(key: String, value: T, editor: ObservableSettings)
fun isSet(keys: Set<String>, key: String): Boolean = key in keys
fun keyListener(key: String) = key
}
/**
* Returns the key of this preference.
*/
override fun key(): String {
return key
}
/**
* Returns the current value of this preference.
*/
override fun get(): T {
return if (isSet()) {
adapter.get(key, preferences)
} else {
defaultValue
}
}
/**
* Sets a new [value] for this preference.
*/
override fun set(value: T) {
adapter.set(key, value, preferences)
}
/**
* Returns whether there's an existing entry for this preference.
*/
override fun isSet(): Boolean {
return adapter.isSet(preferences.keys, key)
}
/**
* Deletes the entry of this preference.
*/
override fun delete() {
preferences.remove(key)
}
/**
* Returns the default value of this preference
*/
override fun defaultValue(): T {
return defaultValue
}
/**
* Returns a cold [Flow] of this preference to receive updates when its value changes.
*/
override fun changes(): Flow<T> {
return callbackFlow {
val listener = preferences.addListener(adapter.keyListener(key)) {
trySend(get())
}
awaitClose { listener.deactivate() }
}
}
/**
* Returns a hot [StateFlow] of this preference bound to the given [scope], allowing to read the
* current value and receive preference updates.
*/
override fun stateIn(scope: CoroutineScope): StateFlow<T> {
return changes().stateIn(scope, SharingStarted.Eagerly, get())
}
}

View File

@@ -0,0 +1,128 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.core.prefs
import com.russhwolf.settings.ObservableSettings
import com.russhwolf.settings.serialization.decodeValue
import com.russhwolf.settings.serialization.encodeValue
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.SetSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.modules.EmptySerializersModule
import kotlinx.serialization.modules.SerializersModule
internal object StringAdapter : AndroidPreference.Adapter<String> {
override fun get(key: String, preferences: ObservableSettings): String {
return preferences.getString(key) // Not called unless key is present.
}
override fun set(key: String, value: String, editor: ObservableSettings) {
editor.putString(key, value)
}
}
internal object LongAdapter : AndroidPreference.Adapter<Long> {
override fun get(key: String, preferences: ObservableSettings): Long {
return preferences.getLong(key, 0)
}
override fun set(key: String, value: Long, editor: ObservableSettings) {
editor.putLong(key, value)
}
}
internal object IntAdapter : AndroidPreference.Adapter<Int> {
override fun get(key: String, preferences: ObservableSettings): Int {
return preferences.getInt(key, 0)
}
override fun set(key: String, value: Int, editor: ObservableSettings) {
editor.putInt(key, value)
}
}
internal object FloatAdapter : AndroidPreference.Adapter<Float> {
override fun get(key: String, preferences: ObservableSettings): Float {
return preferences.getFloat(key, 0f)
}
override fun set(key: String, value: Float, editor: ObservableSettings) {
editor.putFloat(key, value)
}
}
internal object BooleanAdapter : AndroidPreference.Adapter<Boolean> {
override fun get(key: String, preferences: ObservableSettings): Boolean {
return preferences.getBoolean(key, false)
}
override fun set(key: String, value: Boolean, editor: ObservableSettings) {
editor.putBoolean(key, value)
}
}
internal object StringSetAdapter : AndroidPreference.Adapter<Set<String>> {
override fun get(key: String, preferences: ObservableSettings): Set<String> {
return preferences.decodeValue(SetSerializer(String.serializer()), key, emptySet()) // Not called unless key is present.
}
override fun set(key: String, value: Set<String>, editor: ObservableSettings) {
editor.encodeValue(SetSerializer(String.serializer()), key, value)
}
/**
* Encoding a string set makes a list of keys and a size key, such as key.size and key.0-size
*/
override fun isSet(keys: Set<String>, key: String): Boolean {
return keys.contains("$key.size")
}
/**
* Watching the regular key doesnt produce updates for a string set for some reason
* TODO make better, doesnt produce updates when you add something and remove something
*/
override fun keyListener(key: String): String {
return "$key.size"
}
}
internal class ObjectAdapter<T>(
private val serializer: (T) -> String,
private val deserializer: (String) -> T
) : AndroidPreference.Adapter<T> {
override fun get(key: String, preferences: ObservableSettings): T {
return deserializer(preferences.getString(key)) // Not called unless key is present.
}
override fun set(key: String, value: T, editor: ObservableSettings) {
editor.putString(key, serializer(value))
}
}
internal class JsonObjectAdapter<T>(
private val defaultValue: T,
private val serializer: KSerializer<T>,
private val serializersModule: SerializersModule = EmptySerializersModule
) : AndroidPreference.Adapter<T> {
override fun get(key: String, preferences: ObservableSettings): T {
return preferences.decodeValue(serializer, key, defaultValue, serializersModule) // Not called unless key is present.
}
override fun set(key: String, value: T, editor: ObservableSettings) {
editor.encodeValue(serializer, key, value, serializersModule)
}
/**
* Encoding a structure makes keys start with the [key] and adds extensions for values,
* for a pair it would be like [key].first [key].second.
*/
override fun isSet(keys: Set<String>, key: String): Boolean {
return keys.any { it.startsWith(key) }
}
}

View File

@@ -0,0 +1,83 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.core.prefs
import com.russhwolf.settings.ObservableSettings
import kotlinx.serialization.KSerializer
import kotlinx.serialization.modules.SerializersModule
class AndroidPreferenceStore(private val preferences: ObservableSettings) : PreferenceStore {
/**
* Returns an [String] preference for this [key].
*/
override fun getString(key: String, defaultValue: String): Preference<String> {
return AndroidPreference(preferences, key, defaultValue, StringAdapter)
}
/**
* Returns a [Long] preference for this [key].
*/
override fun getLong(key: String, defaultValue: Long): Preference<Long> {
return AndroidPreference(preferences, key, defaultValue, LongAdapter)
}
/**
* Returns an [Int] preference for this [key].
*/
override fun getInt(key: String, defaultValue: Int): Preference<Int> {
return AndroidPreference(preferences, key, defaultValue, IntAdapter)
}
/**
* Returns a [Float] preference for this [key].
*/
override fun getFloat(key: String, defaultValue: Float): Preference<Float> {
return AndroidPreference(preferences, key, defaultValue, FloatAdapter)
}
/**
* Returns a [Boolean] preference for this [key].
*/
override fun getBoolean(key: String, defaultValue: Boolean): Preference<Boolean> {
return AndroidPreference(preferences, key, defaultValue, BooleanAdapter)
}
/**
* Returns a [Set<String>] preference for this [key].
*/
override fun getStringSet(key: String, defaultValue: Set<String>): Preference<Set<String>> {
return AndroidPreference(preferences, key, defaultValue, StringSetAdapter)
}
/**
* Returns preference of type [T] for this [key]. The [serializer] and [deserializer] function
* must be provided.
*/
override fun <T> getObject(
key: String,
defaultValue: T,
serializer: (T) -> String,
deserializer: (String) -> T
): Preference<T> {
val adapter = ObjectAdapter(serializer, deserializer)
return AndroidPreference(preferences, key, defaultValue, adapter)
}
/**
* Returns preference of type [T] for this [key]. The [serializer] must be provided.
*/
override fun <T> getJsonObject(
key: String,
defaultValue: T,
serializer: KSerializer<T>,
serializersModule: SerializersModule
): Preference<T> {
val adapter = JsonObjectAdapter(defaultValue, serializer, serializersModule)
return AndroidPreference(preferences, key, defaultValue, adapter)
}
}

View File

@@ -0,0 +1,21 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.core.prefs
import android.content.Context
import com.russhwolf.settings.AndroidSettings
import me.tatarka.inject.annotations.Inject
actual class PreferenceStoreFactory @Inject constructor(private val context: Context) {
actual fun create(vararg names: String): PreferenceStore {
return AndroidPreferenceStore(
AndroidSettings(
context.getSharedPreferences(names.joinToString(separator = "/"), Context.MODE_PRIVATE)
)
)
}
}

View File

@@ -7,9 +7,10 @@
package ca.gosyer.core.prefs
import com.russhwolf.settings.JvmPreferencesSettings
import me.tatarka.inject.annotations.Inject
import java.util.prefs.Preferences
actual class PreferenceStoreFactory {
actual class PreferenceStoreFactory @Inject constructor() {
private val rootNode: Preferences = Preferences.userRoot()
.node("ca/gosyer/tachideskjui")

View File

@@ -1,53 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.core.io
import ca.gosyer.core.util.decodeBase64
import okio.Buffer
import okio.Source
import okio.Timeout
class DataUriStringSource(private val data: String) : Source {
private val timeout = Timeout()
private val headers = data.substringBefore(",")
private var pos = headers.length + 1
private val decoder: (Buffer, String) -> Long = if ("base64" in headers) {
{ sink, bytes ->
val decoded = bytes.decodeBase64()
sink.write(decoded)
decoded.size.toLong()
}
} else {
{ sink, bytes ->
val decoded = bytes.toByteArray()
sink.write(decoded)
decoded.size.toLong()
}
}
override fun read(sink: Buffer, byteCount: Long): Long {
if (pos >= data.length) return -1
val charsToRead = minOf(data.length - pos, byteCount.toInt())
val nextChars = data.substring(pos, pos + charsToRead)
pos += charsToRead
return decoder(sink, nextChars)
}
override fun timeout(): Timeout {
return timeout
}
override fun close() {
}
}

View File

@@ -8,16 +8,19 @@ package ca.gosyer.core.io
import ca.gosyer.core.lang.withIOContext
import okio.BufferedSink
import okio.FileSystem
import okio.Path
import okio.Source
import okio.buffer
import okio.sink
import okio.use
import java.nio.file.Path
suspend fun Source.saveTo(path: Path) {
withIOContext {
use { source ->
path.sink().buffer().use { it.writeAll(source) }
FileSystem.SYSTEM
.sink(path)
.buffer()
.use { it.writeAll(source) }
}
}
}

View File

@@ -6,6 +6,6 @@
package ca.gosyer.core.prefs
expect class PreferenceStoreFactory() {
expect class PreferenceStoreFactory {
fun create(vararg names: String): PreferenceStore
}

View File

@@ -1,14 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.core.util
import okio.ByteString.Companion.decodeBase64
import okio.ByteString.Companion.encode
fun String.decodeBase64() = decodeBase64()!!
fun String.md5() = encode().md5().hex()

View File

@@ -8,7 +8,13 @@ plugins {
}
kotlin {
android()
android {
compilations {
all {
kotlinOptions.jvmTarget = Config.androidJvmTarget.toString()
}
}
}
jvm("desktop") {
compilations {
all {

View File

@@ -0,0 +1,96 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.data
import android.content.Context
import ca.gosyer.core.di.AppScope
import ca.gosyer.core.prefs.PreferenceStoreFactory
import ca.gosyer.data.catalog.CatalogPreferences
import ca.gosyer.data.download.DownloadService
import ca.gosyer.data.extension.ExtensionPreferences
import ca.gosyer.data.library.LibraryPreferences
import ca.gosyer.data.library.LibraryUpdateService
import ca.gosyer.data.migration.MigrationPreferences
import ca.gosyer.data.migration.Migrations
import ca.gosyer.data.reader.ReaderPreferences
import ca.gosyer.data.server.Http
import ca.gosyer.data.server.HttpProvider
import ca.gosyer.data.server.ServerPreferences
import ca.gosyer.data.ui.UiPreferences
import ca.gosyer.data.update.UpdateChecker
import ca.gosyer.data.update.UpdatePreferences
import me.tatarka.inject.annotations.Component
import me.tatarka.inject.annotations.Provides
@AppScope
@Component
actual abstract class DataComponent(
@get:AppScope
@get:Provides
val context: Context
) {
protected abstract val preferenceFactory: PreferenceStoreFactory
protected abstract val httpProvider: HttpProvider
abstract val downloadService: DownloadService
abstract val libraryUpdateService: LibraryUpdateService
abstract val migrations: Migrations
abstract val updateChecker: UpdateChecker
@get:AppScope
@get:Provides
val serverPreferences: ServerPreferences
get() = ServerPreferences(preferenceFactory.create("server"))
@get:AppScope
@get:Provides
val extensionPreferences: ExtensionPreferences
get() = ExtensionPreferences(preferenceFactory.create("extension"))
@get:AppScope
@get:Provides
val catalogPreferences: CatalogPreferences
get() = CatalogPreferences(preferenceFactory.create("catalog"))
@get:AppScope
@get:Provides
val libraryPreferences: LibraryPreferences
get() = LibraryPreferences(preferenceFactory.create("library"))
@get:AppScope
@get:Provides
val readerPreferences: ReaderPreferences
get() = ReaderPreferences(preferenceFactory.create("reader")) { name ->
preferenceFactory.create("reader", name)
}
@get:AppScope
@get:Provides
val uiPreferences: UiPreferences
get() = UiPreferences(preferenceFactory.create("ui"))
@get:AppScope
@get:Provides
val migrationPreferences: MigrationPreferences
get() = MigrationPreferences(preferenceFactory.create("migration"))
@get:AppScope
@get:Provides
val updatePreferences: UpdatePreferences
get() = UpdatePreferences(preferenceFactory.create("update"))
@get:AppScope
@get:Provides
val http: Http
get() = httpProvider.get(serverPreferences)
companion object
}

View File

@@ -0,0 +1,100 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.data
import ca.gosyer.core.di.AppScope
import ca.gosyer.core.prefs.PreferenceStoreFactory
import ca.gosyer.data.catalog.CatalogPreferences
import ca.gosyer.data.download.DownloadService
import ca.gosyer.data.extension.ExtensionPreferences
import ca.gosyer.data.library.LibraryPreferences
import ca.gosyer.data.library.LibraryUpdateService
import ca.gosyer.data.migration.MigrationPreferences
import ca.gosyer.data.migration.Migrations
import ca.gosyer.data.reader.ReaderPreferences
import ca.gosyer.data.server.Http
import ca.gosyer.data.server.HttpProvider
import ca.gosyer.data.server.ServerHostPreferences
import ca.gosyer.data.server.ServerPreferences
import ca.gosyer.data.server.ServerService
import ca.gosyer.data.ui.UiPreferences
import ca.gosyer.data.update.UpdateChecker
import ca.gosyer.data.update.UpdatePreferences
import me.tatarka.inject.annotations.Component
import me.tatarka.inject.annotations.Provides
@AppScope
@Component
actual abstract class DataComponent {
protected abstract val preferenceFactory: PreferenceStoreFactory
protected abstract val httpProvider: HttpProvider
abstract val downloadService: DownloadService
abstract val libraryUpdateService: LibraryUpdateService
abstract val migrations: Migrations
abstract val updateChecker: UpdateChecker
abstract val serverService: ServerService
@get:AppScope
@get:Provides
val serverHostPreferences: ServerHostPreferences
get() = ServerHostPreferences(preferenceFactory.create("host"))
@get:AppScope
@get:Provides
val serverPreferences: ServerPreferences
get() = ServerPreferences(preferenceFactory.create("server"))
@get:AppScope
@get:Provides
val extensionPreferences: ExtensionPreferences
get() = ExtensionPreferences(preferenceFactory.create("extension"))
@get:AppScope
@get:Provides
val catalogPreferences: CatalogPreferences
get() = CatalogPreferences(preferenceFactory.create("catalog"))
@get:AppScope
@get:Provides
val libraryPreferences: LibraryPreferences
get() = LibraryPreferences(preferenceFactory.create("library"))
@get:AppScope
@get:Provides
val readerPreferences: ReaderPreferences
get() = ReaderPreferences(preferenceFactory.create("reader")) { name ->
preferenceFactory.create("reader", name)
}
@get:AppScope
@get:Provides
val uiPreferences: UiPreferences
get() = UiPreferences(preferenceFactory.create("ui"))
@get:AppScope
@get:Provides
val migrationPreferences: MigrationPreferences
get() = MigrationPreferences(preferenceFactory.create("migration"))
@get:AppScope
@get:Provides
val updatePreferences: UpdatePreferences
get() = UpdatePreferences(preferenceFactory.create("update"))
@get:AppScope
@get:Provides
val http: Http
get() = httpProvider.get(serverPreferences)
companion object
}

View File

@@ -6,94 +6,4 @@
package ca.gosyer.data
import ca.gosyer.core.di.AppScope
import ca.gosyer.core.prefs.PreferenceStoreFactory
import ca.gosyer.data.catalog.CatalogPreferences
import ca.gosyer.data.download.DownloadService
import ca.gosyer.data.extension.ExtensionPreferences
import ca.gosyer.data.library.LibraryPreferences
import ca.gosyer.data.library.LibraryUpdateService
import ca.gosyer.data.migration.MigrationPreferences
import ca.gosyer.data.migration.Migrations
import ca.gosyer.data.reader.ReaderPreferences
import ca.gosyer.data.server.Http
import ca.gosyer.data.server.HttpProvider
import ca.gosyer.data.server.ServerHostPreferences
import ca.gosyer.data.server.ServerPreferences
import ca.gosyer.data.server.ServerService
import ca.gosyer.data.ui.UiPreferences
import ca.gosyer.data.update.UpdateChecker
import ca.gosyer.data.update.UpdatePreferences
import me.tatarka.inject.annotations.Component
import me.tatarka.inject.annotations.Provides
@AppScope
@Component
abstract class DataComponent {
private val preferenceFactory = PreferenceStoreFactory()
protected abstract val httpProvider: HttpProvider
abstract val serverService: ServerService
abstract val downloadService: DownloadService
abstract val libraryUpdateService: LibraryUpdateService
abstract val migrations: Migrations
abstract val updateChecker: UpdateChecker
@get:AppScope
@get:Provides
val serverPreferences: ServerPreferences
get() = ServerPreferences(preferenceFactory.create("server"))
@get:AppScope
@get:Provides
val serverHostPreferences: ServerHostPreferences
get() = ServerHostPreferences(preferenceFactory.create("host"))
@get:AppScope
@get:Provides
val extensionPreferences: ExtensionPreferences
get() = ExtensionPreferences(preferenceFactory.create("extension"))
@get:AppScope
@get:Provides
val catalogPreferences: CatalogPreferences
get() = CatalogPreferences(preferenceFactory.create("catalog"))
@get:AppScope
@get:Provides
val libraryPreferences: LibraryPreferences
get() = LibraryPreferences(preferenceFactory.create("library"))
@get:AppScope
@get:Provides
val readerPreferences: ReaderPreferences
get() = ReaderPreferences(preferenceFactory.create("reader")) { name ->
preferenceFactory.create("reader", name)
}
@get:AppScope
@get:Provides
val uiPreferences: UiPreferences
get() = UiPreferences(preferenceFactory.create("ui"))
@get:AppScope
@get:Provides
val migrationPreferences: MigrationPreferences
get() = MigrationPreferences(preferenceFactory.create("migration"))
@get:AppScope
@get:Provides
val updatePreferences: UpdatePreferences
get() = UpdatePreferences(preferenceFactory.create("update"))
@get:AppScope
@get:Provides
val http: Http
get() = httpProvider.get(serverPreferences)
companion object
}
expect abstract class DataComponent

View File

@@ -8,7 +8,13 @@ plugins {
}
kotlin {
android()
android {
compilations {
all {
kotlinOptions.jvmTarget = Config.androidJvmTarget.toString()
}
}
}
jvm("desktop") {
compilations {
all {

View File

@@ -10,7 +10,13 @@ plugins {
}
kotlin {
android()
android {
compilations {
all {
kotlinOptions.jvmTarget = Config.androidJvmTarget.toString()
}
}
}
jvm("desktop") {
compilations {
all {

View File

@@ -13,13 +13,15 @@ import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.get
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.jvm.javaio.copyTo
import okio.FileSystem
import okio.Path
import okio.buffer
import org.jetbrains.skia.Image
import java.io.ByteArrayOutputStream
import java.nio.file.Path
import kotlin.io.path.readBytes
fun imageFromFile(file: Path): ImageBitmap {
return Image.makeFromEncoded(file.readBytes()).toComposeImageBitmap()
return Image.makeFromEncoded(FileSystem.SYSTEM.source(file).buffer().readByteArray())
.toComposeImageBitmap()
}
suspend fun imageFromUrl(client: Http, url: String, block: HttpRequestBuilder.() -> Unit): ImageBitmap {

View File

@@ -7,7 +7,13 @@ plugins {
}
kotlin {
android()
android {
compilations {
all {
kotlinOptions.jvmTarget = Config.androidJvmTarget.toString()
}
}
}
jvm("desktop") {
compilations {
all {

View File

@@ -0,0 +1,41 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.uicore.resources
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import dev.icerock.moko.resources.PluralsResource
import dev.icerock.moko.resources.StringResource
import dev.icerock.moko.resources.desc.Plural
import dev.icerock.moko.resources.desc.PluralFormatted
import dev.icerock.moko.resources.desc.Resource
import dev.icerock.moko.resources.desc.ResourceFormatted
import dev.icerock.moko.resources.desc.StringDesc
@Composable
actual fun stringResource(resource: StringResource): String {
val context = LocalContext.current
return StringDesc.Resource(resource).toString(context)
}
@Composable
actual fun stringResource(resource: StringResource, vararg args: Any): String {
val context = LocalContext.current
return StringDesc.ResourceFormatted(resource, *args).toString(context)
}
@Composable
actual fun stringResource(resource: PluralsResource, quantity: Int): String {
val context = LocalContext.current
return StringDesc.Plural(resource, quantity).toString(context)
}
@Composable
actual fun stringResource(resource: PluralsResource, quantity: Int, vararg args: Any): String {
val context = LocalContext.current
return StringDesc.PluralFormatted(resource, quantity, *args).toString(context)
}

View File

@@ -0,0 +1,23 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.uicore.resources
import androidx.compose.runtime.Composable
import dev.icerock.moko.resources.PluralsResource
import dev.icerock.moko.resources.StringResource
@Composable
expect fun stringResource(resource: StringResource): String
@Composable
expect fun stringResource(resource: StringResource, vararg args: Any): String
@Composable
expect fun stringResource(resource: PluralsResource, quantity: Int): String
@Composable
expect fun stringResource(resource: PluralsResource, quantity: Int, vararg args: Any): String

View File

@@ -16,17 +16,17 @@ import dev.icerock.moko.resources.desc.ResourceFormatted
import dev.icerock.moko.resources.desc.StringDesc
@Composable
fun stringResource(resource: StringResource): String =
actual fun stringResource(resource: StringResource): String =
StringDesc.Resource(resource).localized()
@Composable
fun stringResource(resource: StringResource, vararg args: Any): String =
actual fun stringResource(resource: StringResource, vararg args: Any): String =
StringDesc.ResourceFormatted(resource, *args).localized()
@Composable
fun stringResource(resource: PluralsResource, quantity: Int): String =
actual fun stringResource(resource: PluralsResource, quantity: Int): String =
StringDesc.Plural(resource, quantity).localized()
@Composable
fun stringResource(resource: PluralsResource, quantity: Int, vararg args: Any): String =
actual fun stringResource(resource: PluralsResource, quantity: Int, vararg args: Any): String =
StringDesc.PluralFormatted(resource, quantity, *args).localized()