init projects
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
package ru.shadowsparky.data
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.Context
|
||||
import org.koin.core.annotation.Factory
|
||||
import ru.shadowsparky.domain.Clip
|
||||
import ru.shadowsparky.domain.ClipboardManager
|
||||
|
||||
@Factory
|
||||
class AndroidClipboardManager(private val context: Context) : ClipboardManager {
|
||||
private val service by lazy {
|
||||
context.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
|
||||
}
|
||||
|
||||
override suspend fun setText(clip: Clip) {
|
||||
service.setPrimaryClip(ClipData.newPlainText(clip.title, clip.text))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package ru.shadowsparky.data
|
||||
|
||||
import org.koin.core.annotation.Single
|
||||
import ru.shadowsparky.domain.Log
|
||||
|
||||
private typealias Impl = android.util.Log
|
||||
|
||||
@Single
|
||||
class AndroidLog : Log {
|
||||
override fun d(tag: String, msg: String?, e: Throwable?) {
|
||||
Impl.d(tag, msg, e)
|
||||
}
|
||||
|
||||
override fun e(tag: String, e: Throwable?, msg: String?) {
|
||||
Impl.e(tag, msg, e)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package ru.shadowsparky.data
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import com.russhwolf.settings.Settings
|
||||
import com.russhwolf.settings.SharedPreferencesSettings
|
||||
import org.koin.core.annotation.Single
|
||||
|
||||
@Single
|
||||
class AndroidSettingsFactory(private val app: Application) : SettingsFactory {
|
||||
override fun create(name: String): Settings {
|
||||
val prefs = app.getSharedPreferences(name, Context.MODE_PRIVATE)
|
||||
return SharedPreferencesSettings(prefs)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package ru.shadowsparky
|
||||
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.koin.core.Koin
|
||||
import org.koin.core.annotation.ComponentScan
|
||||
import org.koin.core.annotation.Configuration
|
||||
import org.koin.core.annotation.Module
|
||||
import org.koin.core.annotation.Single
|
||||
import org.koin.core.component.KoinComponent
|
||||
import ru.shadowsparky.data.SettingsFactory
|
||||
import ru.shadowsparky.data.SettingsKeyValueStorage
|
||||
import ru.shadowsparky.domain.DispatcherProvider
|
||||
import ru.shadowsparky.domain.KeyValueStorage
|
||||
|
||||
val koin: Koin get() = object : KoinComponent {}.getKoin()
|
||||
|
||||
@Module
|
||||
@ComponentScan
|
||||
@Configuration
|
||||
class CommonModule {
|
||||
|
||||
@Single
|
||||
fun provideJson(): Json {
|
||||
return Json {
|
||||
ignoreUnknownKeys = true
|
||||
encodeDefaults = true
|
||||
}
|
||||
}
|
||||
|
||||
@Single
|
||||
fun provideSettings(
|
||||
dispatcherProvider: DispatcherProvider,
|
||||
settingsFactory: SettingsFactory
|
||||
): KeyValueStorage {
|
||||
return SettingsKeyValueStorage(dispatcherProvider, "app_settings", settingsFactory)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package ru.shadowsparky.data
|
||||
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import org.koin.core.annotation.Singleton
|
||||
import ru.shadowsparky.domain.DispatcherProvider
|
||||
|
||||
@Singleton
|
||||
class DispatcherProviderImpl : DispatcherProvider {
|
||||
override val io: CoroutineDispatcher = Dispatchers.Default
|
||||
override val main: CoroutineDispatcher = Dispatchers.Main
|
||||
override val unconfined: CoroutineDispatcher = Dispatchers.Unconfined
|
||||
override val default: CoroutineDispatcher = Dispatchers.Default
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package ru.shadowsparky.data
|
||||
|
||||
import com.russhwolf.settings.ObservableSettings
|
||||
import com.russhwolf.settings.Settings
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import ru.shadowsparky.domain.DispatcherProvider
|
||||
import ru.shadowsparky.domain.KeyValueStorage
|
||||
|
||||
private typealias NotifyCallback = ((String?) -> Unit)?
|
||||
|
||||
class SettingsKeyValueStorage(
|
||||
private val dispatcherProvider: DispatcherProvider,
|
||||
private val name: String,
|
||||
private val settingsFactory: SettingsFactory
|
||||
) : KeyValueStorage {
|
||||
private val impl: BaseImpl by lazy {
|
||||
val settings = settingsFactory.create(name)
|
||||
if (settings is ObservableSettings) {
|
||||
DefaultImpl(settings)
|
||||
} else {
|
||||
FallbackImpl(settings)
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(key: String): Flow<String?> {
|
||||
return impl.get(key)
|
||||
}
|
||||
|
||||
override suspend fun remove(key: String) = withContext(dispatcherProvider.io) {
|
||||
impl.remove(key)
|
||||
}
|
||||
|
||||
override suspend fun put(key: String, value: String) = withContext(dispatcherProvider.io) {
|
||||
impl.put(key, value)
|
||||
}
|
||||
|
||||
private abstract class BaseImpl(private val settings: Settings) {
|
||||
|
||||
open fun remove(key: String) {
|
||||
settings.remove(key)
|
||||
}
|
||||
|
||||
open fun put(key: String, value: String) {
|
||||
settings.putString(key, value)
|
||||
}
|
||||
|
||||
abstract fun get(key: String): Flow<String?>
|
||||
}
|
||||
|
||||
private class DefaultImpl(private val settings: ObservableSettings) : BaseImpl(settings) {
|
||||
override fun get(key: String): Flow<String?> {
|
||||
return callbackFlow {
|
||||
val listener: (String?) -> Unit = {
|
||||
launch { send(it) }
|
||||
}
|
||||
send(settings.getStringOrNull(key))
|
||||
val disposable = settings.addStringOrNullListener(key, listener)
|
||||
awaitClose { disposable.deactivate() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FallbackImpl(private val settings: Settings) : BaseImpl(settings) {
|
||||
private val listenerMap = hashMapOf<String, MutableList<NotifyCallback>>()
|
||||
|
||||
override fun get(key: String): Flow<String?> = callbackFlow {
|
||||
val listener: (String?) -> Unit = {
|
||||
launch { send(it) }
|
||||
}
|
||||
send(settings.getStringOrNull(key))
|
||||
put(key, listener)
|
||||
awaitClose {
|
||||
remove(key, listener)
|
||||
}
|
||||
}
|
||||
|
||||
private fun put(key: String, listener: NotifyCallback) {
|
||||
val list = listenerMap[key] ?: mutableListOf()
|
||||
list.add(listener)
|
||||
listenerMap[key] = list
|
||||
}
|
||||
|
||||
private fun remove(key: String, listener: NotifyCallback) {
|
||||
val list = listenerMap[key] ?: return
|
||||
val index = list.indexOf(listener)
|
||||
if (index >= 0) {
|
||||
list.removeAt(index)
|
||||
}
|
||||
listenerMap[key] = list
|
||||
}
|
||||
|
||||
override fun remove(key: String) {
|
||||
super.remove(key)
|
||||
listenerMap[key]?.forEach {
|
||||
it?.invoke(null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun put(key: String, value: String) {
|
||||
super.put(key, value)
|
||||
listenerMap[key]?.forEach {
|
||||
it?.invoke(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.shadowsparky.data
|
||||
|
||||
import com.russhwolf.settings.Settings
|
||||
|
||||
interface SettingsFactory {
|
||||
fun create(name: String): Settings
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package ru.shadowsparky.domain
|
||||
|
||||
interface AppConfig {
|
||||
fun isDebug(): Boolean
|
||||
fun isBackend(): Boolean = false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.shadowsparky.domain
|
||||
|
||||
interface ClipboardManager {
|
||||
suspend fun setText(clip: Clip)
|
||||
}
|
||||
|
||||
data class Clip(val title: String, val text: String)
|
||||
@@ -0,0 +1,10 @@
|
||||
package ru.shadowsparky.domain
|
||||
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
|
||||
interface DispatcherProvider {
|
||||
val io: CoroutineDispatcher
|
||||
val main: CoroutineDispatcher
|
||||
val unconfined: CoroutineDispatcher
|
||||
val default: CoroutineDispatcher
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package ru.shadowsparky.domain
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
interface KeyValueStorage {
|
||||
fun get(key: String): Flow<String?>
|
||||
fun getBoolean(key: String): Flow<Boolean?> = get(key).map { it?.toBoolean() }
|
||||
fun getInt(key: String): Flow<Int?> = get(key).map { it?.toInt() }
|
||||
|
||||
suspend fun remove(key: String)
|
||||
suspend fun put(key: String, value: String)
|
||||
suspend fun putBoolean(key: String, value: Boolean) = put(key, value.toString())
|
||||
suspend fun putInt(key: String, value: Int) = put(key, value.toString())
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package ru.shadowsparky.domain
|
||||
|
||||
interface Log {
|
||||
fun d(tag: String, msg: String? = null, e: Throwable? = null)
|
||||
fun e(tag: String, e: Throwable? = null, msg: String? = null)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package ru.shadowsparky.data
|
||||
|
||||
import org.koin.core.annotation.Single
|
||||
import ru.shadowsparky.domain.Log
|
||||
|
||||
@Single
|
||||
class DesktopLog : Log {
|
||||
|
||||
override fun d(tag: String, msg: String?, e: Throwable?) {
|
||||
println("$tag $msg")
|
||||
e?.printStackTrace()
|
||||
}
|
||||
|
||||
override fun e(tag: String, e: Throwable?, msg: String?) {
|
||||
println("$tag $msg")
|
||||
e?.printStackTrace()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package ru.shadowsparky.data
|
||||
|
||||
import com.russhwolf.settings.PreferencesSettings
|
||||
import com.russhwolf.settings.Settings
|
||||
import org.koin.core.annotation.Single
|
||||
import java.util.prefs.Preferences
|
||||
|
||||
@Single
|
||||
class DesktopSettingsFactory : SettingsFactory {
|
||||
override fun create(name: String): Settings {
|
||||
return PreferencesSettings(Preferences.userRoot().node(name))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package ru.shadowsparky.data
|
||||
|
||||
import org.koin.core.annotation.Single
|
||||
import ru.shadowsparky.domain.Log
|
||||
|
||||
@Single
|
||||
class WasmLog : Log {
|
||||
override fun d(tag: String, msg: String?, e: Throwable?) {
|
||||
println("$tag $msg")
|
||||
e?.printStackTrace()
|
||||
}
|
||||
|
||||
override fun e(tag: String, e: Throwable?, msg: String?) {
|
||||
println("$tag $msg")
|
||||
e?.printStackTrace()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package ru.shadowsparky.data
|
||||
|
||||
import com.russhwolf.settings.Settings
|
||||
import com.russhwolf.settings.StorageSettings
|
||||
import org.koin.core.annotation.Single
|
||||
|
||||
@Single
|
||||
class WasmSettingsFactory : SettingsFactory {
|
||||
override fun create(name: String): Settings {
|
||||
return StorageSettings()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user