This commit is contained in:
Syer10
2023-12-01 22:44:25 -05:00
parent 4afdd217b7
commit 996dca6cee
207 changed files with 1164 additions and 983 deletions

View File

@@ -20,7 +20,9 @@ import kotlinx.coroutines.flow.launchIn
import org.lighthousegames.logging.logging
import java.util.Locale
class App : Application(), DefaultLifecycleObserver {
class App :
Application(),
DefaultLifecycleObserver {
override fun onCreate() {
super<Application>.onCreate()

View File

@@ -22,7 +22,10 @@ abstract class AppComponent(
@get:AppScope
@get:Provides
val context: Context,
) : ViewModelComponent, DataComponent, DomainComponent, UiComponent {
) : ViewModelComponent,
DataComponent,
DomainComponent,
UiComponent {
abstract val appMigrations: AppMigrations
@get:AppScope

View File

@@ -208,7 +208,10 @@ class AndroidDownloadService : Service() {
val title = downloadingChapter.manga.title.chop(15)
val quotedTitle = Pattern.quote(title)
val chapter = downloadingChapter.chapter.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE), "")
val chapter = downloadingChapter.chapter.name.replaceFirst(
"$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE),
"",
)
setContentTitle("$title - $chapter".chop(30))
setContentText(

View File

@@ -23,9 +23,12 @@ import ca.gosyer.jui.i18n.MR
import dev.icerock.moko.resources.desc.desc
import java.util.concurrent.TimeUnit
class UpdateCheckWorker(private val context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) {
override suspend fun doWork(): Result {
return try {
class UpdateCheckWorker(
private val context: Context,
workerParams: WorkerParameters,
) : CoroutineWorker(context, workerParams) {
override suspend fun doWork(): Result =
try {
val update = AppComponent.getInstance(context.applicationContext)
.let {
if (it.updatePreferences.enabled().get()) {
@@ -48,7 +51,6 @@ class UpdateCheckWorker(private val context: Context, workerParams: WorkerParame
} catch (e: Exception) {
Result.failure()
}
}
companion object {
private const val TAG = "UpdateChecker"

View File

@@ -12,9 +12,11 @@ import me.tatarka.inject.annotations.Inject
actual class PreferenceStoreFactory
@Inject
constructor(private val context: Context) {
actual fun create(vararg names: String): PreferenceStore {
return StandardPreferenceStore(
constructor(
private val context: Context,
) {
actual fun create(vararg names: String): PreferenceStore =
StandardPreferenceStore(
SharedPreferencesSettings(
context.getSharedPreferences(
names.joinToString(separator = "_"),
@@ -22,5 +24,4 @@ actual class PreferenceStoreFactory
),
),
)
}
}

View File

@@ -35,6 +35,4 @@ suspend fun Source.copyTo(sink: BufferedSink) {
}
}
fun ByteArray.source(): BufferedSource {
return Buffer().write(this)
}
fun ByteArray.source(): BufferedSource = Buffer().write(this)

View File

@@ -54,7 +54,9 @@ suspend fun <T> withUIContext(block: suspend CoroutineScope.() -> T): T = withCo
suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T): T = withContext(Dispatchers.IO, block)
fun Throwable.throwIfCancellation() { if (this is CancellationException) throw this }
fun Throwable.throwIfCancellation() {
if (this is CancellationException) throw this
}
fun <T> Result<T>.throwIfCancellation(): Result<T> {
if (isFailure) {

View File

@@ -73,7 +73,10 @@ internal open class ProcessChannel<T>(
override fun offer(element: T): Boolean = inChannel.trySend(element).isSuccess
@Deprecated(
"Deprecated in the favour of 'tryReceive'. Please note that the provided replacement does not rethrow channel's close cause as 'poll' did, for the precise replacement please refer to the 'poll' documentation",
"Deprecated in the favour of 'tryReceive'. " +
"Please note that the provided replacement does not rethrow " +
"channel's close cause as 'poll' did, for the precise replacement " +
"please refer to the 'poll' documentation",
replaceWith = ReplaceWith("tryReceive().getOrNull()"),
level = DeprecationLevel.ERROR,
)
@@ -82,6 +85,7 @@ internal open class ProcessChannel<T>(
override suspend fun receive(): T = outChannel.receive()
override suspend fun send(element: T) = inChannel.send(element)
override val onReceiveCatching: SelectClause1<ChannelResult<T>>
get() = TODO("not implemented")
@@ -105,16 +109,16 @@ internal class PriorityChannelImpl<T>(
scope: CoroutineScope,
comparator: Comparator<T>,
) : ProcessChannel<T>(
// why a rendezvous channel should be the input channel?
// because we buffer and sort the messages in the co-routine
// that is where the capacity constraint is enforced
// and the buffer we keep sorted, the input channel we can't
inChannel = Channel(Channel.RENDEZVOUS),
// output channel is rendezvous channel because we may still
// get higher priority input meanwhile and we will send that
// when output consumer is ready to take it
outChannel = Channel(Channel.RENDEZVOUS),
) {
// why a rendezvous channel should be the input channel?
// because we buffer and sort the messages in the co-routine
// that is where the capacity constraint is enforced
// and the buffer we keep sorted, the input channel we can't
inChannel = Channel(Channel.RENDEZVOUS),
// output channel is rendezvous channel because we may still
// get higher priority input meanwhile and we will send that
// when output consumer is ready to take it
outChannel = Channel(Channel.RENDEZVOUS),
) {
private val buffer = PriorityQueue(comparator)
private fun PriorityQueue<T>.isNotFull() = this.size < maxCapacity

View File

@@ -13,18 +13,16 @@ package ca.gosyer.jui.core.lang
fun String.chop(
count: Int,
replacement: String = "",
): String {
return if (length > count) {
): String =
if (length > count) {
take(count - replacement.length) + replacement
} else {
this
}
}
fun String.addSuffix(char: Char): String {
return if (endsWith(char)) {
fun String.addSuffix(char: Char): String =
if (endsWith(char)) {
this
} else {
this + char
}
}

View File

@@ -22,9 +22,7 @@ class LazyPreferenceStore(
override fun getString(
key: String,
defaultValue: String,
): Preference<String> {
return lazyStore.value.getString(key, defaultValue)
}
): Preference<String> = lazyStore.value.getString(key, defaultValue)
/**
* Returns a [Long] preference for this [key].
@@ -32,9 +30,7 @@ class LazyPreferenceStore(
override fun getLong(
key: String,
defaultValue: Long,
): Preference<Long> {
return lazyStore.value.getLong(key, defaultValue)
}
): Preference<Long> = lazyStore.value.getLong(key, defaultValue)
/**
* Returns an [Int] preference for this [key].
@@ -42,9 +38,7 @@ class LazyPreferenceStore(
override fun getInt(
key: String,
defaultValue: Int,
): Preference<Int> {
return lazyStore.value.getInt(key, defaultValue)
}
): Preference<Int> = lazyStore.value.getInt(key, defaultValue)
/**
* Returns a [Float] preference for this [key].
@@ -52,9 +46,7 @@ class LazyPreferenceStore(
override fun getFloat(
key: String,
defaultValue: Float,
): Preference<Float> {
return lazyStore.value.getFloat(key, defaultValue)
}
): Preference<Float> = lazyStore.value.getFloat(key, defaultValue)
/**
* Returns a [Boolean] preference for this [key].
@@ -62,9 +54,7 @@ class LazyPreferenceStore(
override fun getBoolean(
key: String,
defaultValue: Boolean,
): Preference<Boolean> {
return lazyStore.value.getBoolean(key, defaultValue)
}
): Preference<Boolean> = lazyStore.value.getBoolean(key, defaultValue)
/**
* Returns a [Set<String>] preference for this [key].
@@ -72,9 +62,7 @@ class LazyPreferenceStore(
override fun getStringSet(
key: String,
defaultValue: Set<String>,
): Preference<Set<String>> {
return lazyStore.value.getStringSet(key, defaultValue)
}
): Preference<Set<String>> = lazyStore.value.getStringSet(key, defaultValue)
/**
* Returns preference of type [T] for this [key]. The [serializer] and [deserializer] function
@@ -85,16 +73,12 @@ class LazyPreferenceStore(
defaultValue: T,
serializer: (T) -> String,
deserializer: (String) -> T,
): Preference<T> {
return lazyStore.value.getObject(key, defaultValue, serializer, deserializer)
}
): Preference<T> = lazyStore.value.getObject(key, defaultValue, serializer, deserializer)
override fun <T> getJsonObject(
key: String,
defaultValue: T,
serializer: KSerializer<T>,
serializersModule: SerializersModule,
): Preference<T> {
return lazyStore.value.getJsonObject(key, defaultValue, serializer)
}
): Preference<T> = lazyStore.value.getJsonObject(key, defaultValue, serializer)
}

View File

@@ -91,8 +91,8 @@ interface PreferenceStore {
inline fun <reified T : Enum<T>> PreferenceStore.getEnum(
key: String,
defaultValue: T,
): Preference<T> {
return getObject(
): Preference<T> =
getObject(
key,
defaultValue,
{ it.name },
@@ -104,4 +104,3 @@ inline fun <reified T : Enum<T>> PreferenceStore.getEnum(
}
},
)
}

View File

@@ -24,20 +24,17 @@ internal class StandardPreference<T>(
/**
* Returns the key of this preference.
*/
override fun key(): String {
return key
}
override fun key(): String = key
/**
* Returns the current value of this preference.
*/
override fun get(): T {
return if (isSet()) {
override fun get(): T =
if (isSet()) {
adapter.get(key, preferences)
} else {
defaultValue
}
}
/**
* Sets a new [value] for this preference.
@@ -49,9 +46,7 @@ internal class StandardPreference<T>(
/**
* Returns whether there's an existing entry for this preference.
*/
override fun isSet(): Boolean {
return adapter.isSet(preferences.keys, key)
}
override fun isSet(): Boolean = adapter.isSet(preferences.keys, key)
/**
* Deletes the entry of this preference.
@@ -63,27 +58,22 @@ internal class StandardPreference<T>(
/**
* Returns the default value of this preference
*/
override fun defaultValue(): T {
return defaultValue
}
override fun defaultValue(): T = defaultValue
/**
* Returns a cold [Flow] of this preference to receive updates when its value changes.
*/
override fun changes(): Flow<T> {
return callbackFlow {
override fun changes(): Flow<T> =
callbackFlow {
val listener = adapter.addListener(key, preferences) {
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())
}
override fun stateIn(scope: CoroutineScope): StateFlow<T> = changes().stateIn(scope, SharingStarted.Eagerly, get())
}

View File

@@ -10,7 +10,9 @@ import com.russhwolf.settings.ObservableSettings
import kotlinx.serialization.KSerializer
import kotlinx.serialization.modules.SerializersModule
class StandardPreferenceStore(private val preferences: ObservableSettings) : PreferenceStore {
class StandardPreferenceStore(
private val preferences: ObservableSettings,
) : PreferenceStore {
/**
* Returns an [String] preference for this [key].
*/

View File

@@ -31,11 +31,12 @@ object ImageUtil {
return true
}
private fun charByteArrayOf(vararg bytes: Int): ByteArray {
return ByteArray(bytes.size) { pos -> bytes[pos].toByte() }
}
private fun charByteArrayOf(vararg bytes: Int): ByteArray = ByteArray(bytes.size) { pos -> bytes[pos].toByte() }
enum class ImageType(val mime: String, val extension: String) {
enum class ImageType(
val mime: String,
val extension: String,
) {
JPG("image/jpeg", "jpg"),
PNG("image/png", "png"),
GIF("image/gif", "gif"),

View File

@@ -16,11 +16,10 @@ actual class PreferenceStoreFactory
private val rootNode: Preferences = Preferences.userRoot()
.node("ca/gosyer/tachideskjui")
actual fun create(vararg names: String): PreferenceStore {
return StandardPreferenceStore(
actual fun create(vararg names: String): PreferenceStore =
StandardPreferenceStore(
PreferencesSettings(
rootNode.node(names.joinToString(separator = "/")),
),
)
}
}

View File

@@ -13,11 +13,10 @@ import platform.Foundation.NSUserDefaults
actual class PreferenceStoreFactory
@Inject
constructor() {
actual fun create(vararg names: String): PreferenceStore {
return StandardPreferenceStore(
actual fun create(vararg names: String): PreferenceStore =
StandardPreferenceStore(
NSUserDefaultsSettings(
NSUserDefaults.standardUserDefaults,
),
)
}
}

View File

@@ -49,7 +49,5 @@ class FlowConverterFactory : Converter.Factory {
override fun suspendResponseConverter(
typeData: TypeData,
ktorfit: Ktorfit,
): Converter.SuspendResponseConverter<HttpResponse, *>? {
return null
}
): Converter.SuspendResponseConverter<HttpResponse, *>? = null
}

View File

@@ -20,7 +20,10 @@ import me.tatarka.inject.annotations.Provides
abstract class AppComponent(
@get:Provides
val context: ContextWrapper,
) : ViewModelComponent, DataComponent, DomainComponent, UiComponent {
) : ViewModelComponent,
DataComponent,
DomainComponent,
UiComponent {
abstract val appMigrations: AppMigrations
@get:AppScope

View File

@@ -15,12 +15,12 @@ class Slf4jLogFactory : LogFactory {
override fun createKmLog(
tag: String,
className: String,
): KmLog {
return Slf4jLog(tag)
}
): KmLog = Slf4jLog(tag)
}
class Slf4jLog(tag: String) : KmLog(tag) {
class Slf4jLog(
tag: String,
) : KmLog(tag) {
private val logger: Logger = LoggerFactory.getLogger(tag)
override fun verbose(

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class ExportBackupFile
@Inject
constructor(private val backupRepository: BackupRepository) {
constructor(
private val backupRepository: BackupRepository,
) {
suspend fun await(
block: HttpRequestBuilder.() -> Unit = {},
onError: suspend (Throwable) -> Unit = {},

View File

@@ -16,7 +16,9 @@ import org.lighthousegames.logging.logging
class ImportBackupFile
@Inject
constructor(private val backupRepository: BackupRepository) {
constructor(
private val backupRepository: BackupRepository,
) {
suspend fun await(
file: Path,
block: HttpRequestBuilder.() -> Unit = {},

View File

@@ -16,7 +16,9 @@ import org.lighthousegames.logging.logging
class ValidateBackupFile
@Inject
constructor(private val backupRepository: BackupRepository) {
constructor(
private val backupRepository: BackupRepository,
) {
suspend fun await(
file: Path,
block: HttpRequestBuilder.() -> Unit = {},

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class CreateCategory
@Inject
constructor(private val categoryRepository: CategoryRepository) {
constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
name: String,
onError: suspend (Throwable) -> Unit = {},

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class DeleteCategory
@Inject
constructor(private val categoryRepository: CategoryRepository) {
constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
categoryId: Long,
onError: suspend (Throwable) -> Unit = {},

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class GetCategories
@Inject
constructor(private val categoryRepository: CategoryRepository) {
constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
dropDefault: Boolean = false,
onError: suspend (Throwable) -> Unit = {},

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class GetMangaCategories
@Inject
constructor(private val categoryRepository: CategoryRepository) {
constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class ModifyCategory
@Inject
constructor(private val categoryRepository: CategoryRepository) {
constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
categoryId: Long,
name: String,

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class ReorderCategory
@Inject
constructor(private val categoryRepository: CategoryRepository) {
constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
to: Int,
from: Int,

View File

@@ -16,7 +16,9 @@ import org.lighthousegames.logging.logging
class UpdateCategoryMeta
@Inject
constructor(private val categoryRepository: CategoryRepository) {
constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
category: Category,
example: Int = category.meta.example,

View File

@@ -17,7 +17,9 @@ import org.lighthousegames.logging.logging
class GetChapterPage
@Inject
constructor(private val chapterRepository: ChapterRepository) {
constructor(
private val chapterRepository: ChapterRepository,
) {
suspend fun await(
mangaId: Long,
index: Int,

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class BatchChapterDownload
@Inject
constructor(private val downloadRepository: DownloadRepository) {
constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(
chapterIds: List<Long>,
onError: suspend (Throwable) -> Unit = {},

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class ClearDownloadQueue
@Inject
constructor(private val downloadRepository: DownloadRepository) {
constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {

View File

@@ -16,7 +16,9 @@ import org.lighthousegames.logging.logging
class QueueChapterDownload
@Inject
constructor(private val downloadRepository: DownloadRepository) {
constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(
mangaId: Long,
index: Int,

View File

@@ -16,7 +16,9 @@ import org.lighthousegames.logging.logging
class ReorderChapterDownload
@Inject
constructor(private val downloadRepository: DownloadRepository) {
constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(
mangaId: Long,
index: Int,

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class StartDownloading
@Inject
constructor(private val downloadRepository: DownloadRepository) {
constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {

View File

@@ -16,7 +16,9 @@ import org.lighthousegames.logging.logging
class StopChapterDownload
@Inject
constructor(private val downloadRepository: DownloadRepository) {
constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(
mangaId: Long,
index: Int,

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class StopDownloading
@Inject
constructor(private val downloadRepository: DownloadRepository) {
constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {

View File

@@ -11,7 +11,9 @@ import kotlinx.serialization.Serializable
@Serializable
@Stable
enum class DownloadState(val state: Int) {
enum class DownloadState(
val state: Int,
) {
Queued(0),
Downloading(1),
Finished(2),

View File

@@ -47,6 +47,7 @@ class DownloadService
.map {
it.filter { it.mangaId == mangaId }
}
fun registerWatches(mangaIds: Set<Long>) =
downloadQueue
.map {

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class GetExtensionList
@Inject
constructor(private val extensionRepository: ExtensionRepository) {
constructor(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class InstallExtension
@Inject
constructor(private val extensionRepository: ExtensionRepository) {
constructor(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await(
extension: Extension,
onError: suspend (Throwable) -> Unit = {},

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class InstallExtensionFile
@Inject
constructor(private val extensionRepository: ExtensionRepository) {
constructor(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await(
path: Path,
onError: suspend (Throwable) -> Unit = {},

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class UninstallExtension
@Inject
constructor(private val extensionRepository: ExtensionRepository) {
constructor(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await(
extension: Extension,
onError: suspend (Throwable) -> Unit = {},

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class UpdateExtension
@Inject
constructor(private val extensionRepository: ExtensionRepository) {
constructor(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await(
extension: Extension,
onError: suspend (Throwable) -> Unit = {},

View File

@@ -10,8 +10,12 @@ import androidx.compose.ui.text.intl.Locale
import ca.gosyer.jui.core.prefs.Preference
import ca.gosyer.jui.core.prefs.PreferenceStore
class ExtensionPreferences(private val preferenceStore: PreferenceStore) {
fun languages(): Preference<Set<String>> {
return preferenceStore.getStringSet("enabled_langs", setOfNotNull("all", "en", Locale.current.language))
}
class ExtensionPreferences(
private val preferenceStore: PreferenceStore,
) {
fun languages(): Preference<Set<String>> =
preferenceStore.getStringSet(
"enabled_langs",
setOfNotNull("all", "en", Locale.current.language),
)
}

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class GetGlobalMeta
@Inject
constructor(private val globalRepository: GlobalRepository) {
constructor(
private val globalRepository: GlobalRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {

View File

@@ -16,7 +16,9 @@ import org.lighthousegames.logging.logging
class UpdateGlobalMeta
@Inject
constructor(private val globalRepository: GlobalRepository) {
constructor(
private val globalRepository: GlobalRepository,
) {
suspend fun await(
globalMeta: GlobalMeta,
example: Int = globalMeta.example,

View File

@@ -11,56 +11,52 @@ import ca.gosyer.jui.core.prefs.PreferenceStore
import ca.gosyer.jui.domain.library.model.FilterState
import ca.gosyer.jui.domain.library.model.Sort
class LibraryPreferences(private val preferenceStore: PreferenceStore) {
fun showAllCategory(): Preference<Boolean> {
return preferenceStore.getBoolean("show_all_category", false)
}
class LibraryPreferences(
private val preferenceStore: PreferenceStore,
) {
fun showAllCategory(): Preference<Boolean> = preferenceStore.getBoolean("show_all_category", false)
fun filterDownloaded(): Preference<FilterState> {
return preferenceStore.getJsonObject("filter_downloaded", FilterState.IGNORED, FilterState.serializer())
}
fun filterDownloaded(): Preference<FilterState> =
preferenceStore.getJsonObject(
"filter_downloaded",
FilterState.IGNORED,
FilterState.serializer(),
)
fun filterUnread(): Preference<FilterState> {
return preferenceStore.getJsonObject("filter_unread", FilterState.IGNORED, FilterState.serializer())
}
fun filterUnread(): Preference<FilterState> =
preferenceStore.getJsonObject(
"filter_unread",
FilterState.IGNORED,
FilterState.serializer(),
)
fun filterCompleted(): Preference<FilterState> {
return preferenceStore.getJsonObject("filter_completed", FilterState.IGNORED, FilterState.serializer())
}
fun filterCompleted(): Preference<FilterState> =
preferenceStore.getJsonObject(
"filter_completed",
FilterState.IGNORED,
FilterState.serializer(),
)
fun sortMode(): Preference<Sort> {
return preferenceStore.getJsonObject("sort_mode", Sort.ALPHABETICAL, Sort.serializer())
}
fun sortMode(): Preference<Sort> = preferenceStore.getJsonObject("sort_mode", Sort.ALPHABETICAL, Sort.serializer())
fun sortAscending(): Preference<Boolean> {
return preferenceStore.getBoolean("sort_ascending", true)
}
fun sortAscending(): Preference<Boolean> = preferenceStore.getBoolean("sort_ascending", true)
fun displayMode(): Preference<ca.gosyer.jui.domain.library.model.DisplayMode> {
return preferenceStore.getJsonObject("display_mode", ca.gosyer.jui.domain.library.model.DisplayMode.CompactGrid, ca.gosyer.jui.domain.library.model.DisplayMode.serializer())
}
fun displayMode(): Preference<ca.gosyer.jui.domain.library.model.DisplayMode> =
preferenceStore.getJsonObject(
"display_mode",
ca.gosyer.jui.domain.library.model.DisplayMode.CompactGrid,
ca.gosyer.jui.domain.library.model.DisplayMode.serializer(),
)
fun gridColumns(): Preference<Int> {
return preferenceStore.getInt("grid_columns", 0)
}
fun gridColumns(): Preference<Int> = preferenceStore.getInt("grid_columns", 0)
fun gridSize(): Preference<Int> {
return preferenceStore.getInt("grid_size", 160)
}
fun gridSize(): Preference<Int> = preferenceStore.getInt("grid_size", 160)
fun unreadBadge(): Preference<Boolean> {
return preferenceStore.getBoolean("unread_badge", true)
}
fun unreadBadge(): Preference<Boolean> = preferenceStore.getBoolean("unread_badge", true)
fun downloadBadge(): Preference<Boolean> {
return preferenceStore.getBoolean("download_badge", false)
}
fun downloadBadge(): Preference<Boolean> = preferenceStore.getBoolean("download_badge", false)
fun languageBadge(): Preference<Boolean> {
return preferenceStore.getBoolean("language_badge", false)
}
fun languageBadge(): Preference<Boolean> = preferenceStore.getBoolean("language_badge", false)
fun localBadge(): Preference<Boolean> {
return preferenceStore.getBoolean("local_badge", false)
}
fun localBadge(): Preference<Boolean> = preferenceStore.getBoolean("local_badge", false)
}

View File

@@ -9,11 +9,10 @@ package ca.gosyer.jui.domain.migration.service
import ca.gosyer.jui.core.prefs.Preference
import ca.gosyer.jui.core.prefs.PreferenceStore
class MigrationPreferences(private val preferenceStore: PreferenceStore) {
fun version(): Preference<Int> {
return preferenceStore.getInt("version", 0)
}
fun appVersion(): Preference<Int> {
return preferenceStore.getInt("app_version", 0)
}
class MigrationPreferences(
private val preferenceStore: PreferenceStore,
) {
fun version(): Preference<Int> = preferenceStore.getInt("version", 0)
fun appVersion(): Preference<Int> = preferenceStore.getInt("app_version", 0)
}

View File

@@ -11,7 +11,10 @@ import kotlinx.serialization.Serializable
@Serializable
@Stable
enum class TappingInvertMode(val shouldInvertHorizontal: Boolean = false, val shouldInvertVertical: Boolean = false) {
enum class TappingInvertMode(
val shouldInvertHorizontal: Boolean = false,
val shouldInvertVertical: Boolean = false,
) {
NONE,
HORIZONTAL(shouldInvertHorizontal = true),
VERTICAL(shouldInvertVertical = true),

View File

@@ -13,38 +13,39 @@ import ca.gosyer.jui.domain.reader.model.Direction
import ca.gosyer.jui.domain.reader.model.ImageScale
import ca.gosyer.jui.domain.reader.model.NavigationMode
class ReaderModePreferences(private val mode: String, private val preferenceStore: PreferenceStore) {
class ReaderModePreferences(
private val mode: String,
private val preferenceStore: PreferenceStore,
) {
constructor(mode: String, factory: (String) -> PreferenceStore) :
this(mode, factory(mode))
private val defaultMode by lazy { DefaultReaderMode.values().find { it.res == mode } }
fun default(): Preference<Boolean> {
return preferenceStore.getBoolean("default", defaultMode != null)
}
fun default(): Preference<Boolean> = preferenceStore.getBoolean("default", defaultMode != null)
fun continuous(): Preference<Boolean> {
return preferenceStore.getBoolean("continuous", defaultMode?.continuous ?: false)
}
fun continuous(): Preference<Boolean> = preferenceStore.getBoolean("continuous", defaultMode?.continuous ?: false)
fun direction(): Preference<Direction> {
return preferenceStore.getJsonObject("direction", defaultMode?.direction ?: Direction.Down, Direction.serializer())
}
fun direction(): Preference<Direction> =
preferenceStore.getJsonObject(
"direction",
defaultMode?.direction ?: Direction.Down,
Direction.serializer(),
)
fun padding(): Preference<Int> {
return preferenceStore.getInt("padding", defaultMode?.padding ?: 0)
}
fun padding(): Preference<Int> = preferenceStore.getInt("padding", defaultMode?.padding ?: 0)
fun imageScale(): Preference<ImageScale> {
return preferenceStore.getJsonObject("image_scale", defaultMode?.imageScale ?: ImageScale.FitScreen, ImageScale.serializer())
}
fun imageScale(): Preference<ImageScale> =
preferenceStore.getJsonObject(
"image_scale",
defaultMode?.imageScale ?: ImageScale.FitScreen,
ImageScale.serializer(),
)
fun fitSize(): Preference<Boolean> {
return preferenceStore.getBoolean("fit_size", false)
}
fun fitSize(): Preference<Boolean> = preferenceStore.getBoolean("fit_size", false)
fun maxSize(): Preference<Int> {
return preferenceStore.getInt(
fun maxSize(): Preference<Int> =
preferenceStore.getInt(
"max_size",
if (defaultMode?.continuous == true) {
if (defaultMode?.direction == Direction.Left || defaultMode?.direction == Direction.Right) {
@@ -56,9 +57,11 @@ class ReaderModePreferences(private val mode: String, private val preferenceStor
0
},
)
}
fun navigationMode(): Preference<NavigationMode> {
return preferenceStore.getJsonObject("navigation", defaultMode?.navigationMode ?: NavigationMode.LNavigation, NavigationMode.serializer())
}
fun navigationMode(): Preference<NavigationMode> =
preferenceStore.getJsonObject(
"navigation",
defaultMode?.navigationMode ?: NavigationMode.LNavigation,
NavigationMode.serializer(),
)
}

View File

@@ -12,28 +12,22 @@ import ca.gosyer.jui.domain.reader.model.DefaultReaderMode
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.serializer
class ReaderPreferences(private val preferenceStore: PreferenceStore, private val factory: (String) -> PreferenceStore) {
fun preload(): Preference<Int> {
return preferenceStore.getInt("preload", 3)
}
class ReaderPreferences(
private val preferenceStore: PreferenceStore,
private val factory: (String) -> PreferenceStore,
) {
fun preload(): Preference<Int> = preferenceStore.getInt("preload", 3)
fun threads(): Preference<Int> {
return preferenceStore.getInt("threads", 3)
}
fun threads(): Preference<Int> = preferenceStore.getInt("threads", 3)
fun modes(): Preference<List<String>> {
return preferenceStore.getJsonObject(
fun modes(): Preference<List<String>> =
preferenceStore.getJsonObject(
"modes",
DefaultReaderMode.values().map { it.res },
ListSerializer(String.serializer()),
)
}
fun mode(): Preference<String> {
return preferenceStore.getString("mode", "RTL")
}
fun mode(): Preference<String> = preferenceStore.getString("mode", "RTL")
fun getMode(mode: String): ReaderModePreferences {
return ReaderModePreferences(mode, factory)
}
fun getMode(mode: String): ReaderModePreferences = ReaderModePreferences(mode, factory)
}

View File

@@ -44,8 +44,8 @@ expect fun HttpClientConfig<HttpClientEngineConfig>.configurePlatform()
fun httpClient(
serverPreferences: ServerPreferences,
json: Json,
): Http {
return HttpClient(Engine) {
): Http =
HttpClient(Engine) {
configurePlatform()
expectSuccess = true
@@ -112,10 +112,10 @@ fun httpClient(
}
logger = object : Logger {
val log = logging("HttpClient")
override fun log(message: String) {
log.info { message }
}
}
}
}
}

View File

@@ -25,18 +25,15 @@ class ServerUrlPreference(
private val port: Preference<Int>,
private val pathPrefix: Preference<String>,
) : Preference<Url> {
override fun key(): String {
return key
}
override fun key(): String = key
override fun get(): Url {
return URLBuilder(server.get()).apply {
override fun get(): Url =
URLBuilder(server.get()).apply {
port = this@ServerUrlPreference.port.get()
if (pathPrefix.isSet()) {
pathPrefix.get().ifBlank { null }?.let { path(it) }
}
}.build()
}
override fun set(value: Url) {
server.set(value.protocol.name + "://" + value.host)
@@ -44,9 +41,7 @@ class ServerUrlPreference(
pathPrefix.set(value.encodedPath)
}
override fun isSet(): Boolean {
return server.isSet() || port.isSet() || pathPrefix.isSet()
}
override fun isSet(): Boolean = server.isSet() || port.isSet() || pathPrefix.isSet()
override fun delete() {
server.delete()
@@ -54,14 +49,13 @@ class ServerUrlPreference(
pathPrefix.delete()
}
override fun defaultValue(): Url {
return URLBuilder(server.defaultValue()).apply {
override fun defaultValue(): Url =
URLBuilder(server.defaultValue()).apply {
port = this@ServerUrlPreference.port.defaultValue()
}.build()
}
override fun changes(): Flow<Url> {
return combine(server.getAsFlow(), port.getAsFlow(), pathPrefix.getAsFlow()) { server, port, pathPrefix ->
override fun changes(): Flow<Url> =
combine(server.getAsFlow(), port.getAsFlow(), pathPrefix.getAsFlow()) { server, port, pathPrefix ->
URLBuilder(server).apply {
this.port = port
if (pathPrefix.isNotBlank()) {
@@ -69,9 +63,6 @@ class ServerUrlPreference(
}
}.build()
}.drop(1)
}
override fun stateIn(scope: CoroutineScope): StateFlow<Url> {
return changes().stateIn(scope, SharingStarted.Eagerly, get())
}
override fun stateIn(scope: CoroutineScope): StateFlow<Url> = changes().stateIn(scope, SharingStarted.Eagerly, get())
}

View File

@@ -13,51 +13,30 @@ import ca.gosyer.jui.domain.server.model.Proxy
import ca.gosyer.jui.domain.server.model.ServerUrlPreference
import io.ktor.http.Url
class ServerPreferences(private val preferenceStore: PreferenceStore) {
fun server(): Preference<String> {
return preferenceStore.getString("server_url", "http://localhost")
}
class ServerPreferences(
private val preferenceStore: PreferenceStore,
) {
fun server(): Preference<String> = preferenceStore.getString("server_url", "http://localhost")
fun port(): Preference<Int> {
return preferenceStore.getInt("server_port", 4567)
}
fun port(): Preference<Int> = preferenceStore.getInt("server_port", 4567)
fun pathPrefix(): Preference<String> {
return preferenceStore.getString("server_path_prefix", "")
}
fun pathPrefix(): Preference<String> = preferenceStore.getString("server_path_prefix", "")
fun serverUrl(): Preference<Url> {
return ServerUrlPreference("", server(), port(), pathPrefix())
}
fun serverUrl(): Preference<Url> = ServerUrlPreference("", server(), port(), pathPrefix())
fun proxy(): Preference<Proxy> {
return preferenceStore.getJsonObject("proxy", Proxy.NO_PROXY, Proxy.serializer())
}
fun proxy(): Preference<Proxy> = preferenceStore.getJsonObject("proxy", Proxy.NO_PROXY, Proxy.serializer())
fun proxyHttpHost(): Preference<String> {
return preferenceStore.getString("proxy_http_host")
}
fun proxyHttpHost(): Preference<String> = preferenceStore.getString("proxy_http_host")
fun proxyHttpPort(): Preference<Int> {
return preferenceStore.getInt("proxy_http_port")
}
fun proxyHttpPort(): Preference<Int> = preferenceStore.getInt("proxy_http_port")
fun proxySocksHost(): Preference<String> {
return preferenceStore.getString("proxy_socks_host")
}
fun proxySocksHost(): Preference<String> = preferenceStore.getString("proxy_socks_host")
fun proxySocksPort(): Preference<Int> {
return preferenceStore.getInt("proxy_socks_port")
}
fun proxySocksPort(): Preference<Int> = preferenceStore.getInt("proxy_socks_port")
fun auth(): Preference<Auth> {
return preferenceStore.getJsonObject("auth", Auth.NONE, Auth.serializer())
}
fun auth(): Preference<Auth> = preferenceStore.getJsonObject("auth", Auth.NONE, Auth.serializer())
fun authUsername(): Preference<String> {
return preferenceStore.getString("auth_username")
}
fun authPassword(): Preference<String> {
return preferenceStore.getString("auth_password")
}
fun authUsername(): Preference<String> = preferenceStore.getString("auth_username")
fun authPassword(): Preference<String> = preferenceStore.getString("auth_password")
}

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class AboutServer
@Inject
constructor(private val settingsRepository: SettingsRepository) {
constructor(
private val settingsRepository: SettingsRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class CheckUpdate
@Inject
constructor(private val settingsRepository: SettingsRepository) {
constructor(
private val settingsRepository: SettingsRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class GetFilterList
@Inject
constructor(private val sourceRepository: SourceRepository) {
constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
reset: Boolean,

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class GetLatestManga
@Inject
constructor(private val sourceRepository: SourceRepository) {
constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
page: Int,

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class GetPopularManga
@Inject
constructor(private val sourceRepository: SourceRepository) {
constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
page: Int,

View File

@@ -17,7 +17,9 @@ import org.lighthousegames.logging.logging
class GetQuickSearchManga
@Inject
constructor(private val sourceRepository: SourceRepository) {
constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
searchTerm: String?,

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class GetSearchManga
@Inject
constructor(private val sourceRepository: SourceRepository) {
constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
searchTerm: String?,

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class GetSourceList
@Inject
constructor(private val sourceRepository: SourceRepository) {
constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class GetSourceSettings
@Inject
constructor(private val sourceRepository: SourceRepository) {
constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
onError: suspend (Throwable) -> Unit = {},

View File

@@ -18,7 +18,9 @@ import org.lighthousegames.logging.logging
class SetSourceFilter
@Inject
constructor(private val sourceRepository: SourceRepository) {
constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
filterIndex: Int,
@@ -56,7 +58,8 @@ class SetSourceFilter
.catch {
onError(it)
log.warn(it) {
"Failed to set filter for ${source.displayName} with index = $filterIndex and childIndex = $childFilterIndex and value = $filter"
"Failed to set filter for ${source.displayName} with index = $filterIndex " +
"and childIndex = $childFilterIndex and value = $filter"
}
}
.collect()
@@ -71,7 +74,10 @@ class SetSourceFilter
) = asFlow(sourceId, filterIndex, childFilterIndex, filter)
.catch {
onError(it)
log.warn(it) { "Failed to set filter for $sourceId with index = $filterIndex and childIndex = $childFilterIndex and value = $filter" }
log.warn(it) {
"Failed to set filter for $sourceId with index = $filterIndex " +
"and childIndex = $childFilterIndex and value = $filter"
}
}
.collect()

View File

@@ -16,7 +16,9 @@ import org.lighthousegames.logging.logging
class SetSourceSetting
@Inject
constructor(private val sourceRepository: SourceRepository) {
constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
settingIndex: Int,

View File

@@ -11,7 +11,10 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
@Serializable
data class SourceFilterChange(val position: Int, val state: String) {
data class SourceFilterChange(
val position: Int,
val state: String,
) {
constructor(position: Int, state: Any) : this(
position,
if (state is SortFilter.Selection) {

View File

@@ -13,7 +13,9 @@ import kotlinx.serialization.Serializable
@Serializable
@SerialName("ListPreference")
@Immutable
data class ListPreference(override val props: ListProps) : SourcePreference() {
data class ListPreference(
override val props: ListProps,
) : SourcePreference() {
@Serializable
@Immutable
data class ListProps(

View File

@@ -13,7 +13,9 @@ import kotlinx.serialization.Serializable
@Serializable
@SerialName("MultiSelectListPreference")
@Immutable
data class MultiSelectListPreference(override val props: MultiSelectListProps) : SourcePreference() {
data class MultiSelectListPreference(
override val props: MultiSelectListProps,
) : SourcePreference() {
@Serializable
@Immutable
data class MultiSelectListProps(

View File

@@ -11,7 +11,10 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
@Serializable
data class SourcePreferenceChange(val position: Int, val value: String) {
data class SourcePreferenceChange(
val position: Int,
val value: String,
) {
constructor(position: Int, value: Any) : this(
position,
if (value is List<*>) {

View File

@@ -11,12 +11,15 @@ import ca.gosyer.jui.core.prefs.Preference
import ca.gosyer.jui.core.prefs.PreferenceStore
import ca.gosyer.jui.domain.library.model.DisplayMode
class CatalogPreferences(private val preferenceStore: PreferenceStore) {
fun languages(): Preference<Set<String>> {
return preferenceStore.getStringSet("enabled_langs", setOfNotNull("en", Locale.current.language))
}
class CatalogPreferences(
private val preferenceStore: PreferenceStore,
) {
fun languages(): Preference<Set<String>> = preferenceStore.getStringSet("enabled_langs", setOfNotNull("en", Locale.current.language))
fun displayMode(): Preference<DisplayMode> {
return preferenceStore.getJsonObject("display_mode", DisplayMode.CompactGrid, DisplayMode.serializer())
}
fun displayMode(): Preference<DisplayMode> =
preferenceStore.getJsonObject(
"display_mode",
DisplayMode.CompactGrid,
DisplayMode.serializer(),
)
}

View File

@@ -12,68 +12,48 @@ import ca.gosyer.jui.domain.ui.model.StartScreen
import ca.gosyer.jui.domain.ui.model.ThemeMode
import ca.gosyer.jui.domain.ui.model.WindowSettings
class UiPreferences(private val preferenceStore: PreferenceStore) {
fun themeMode(): Preference<ThemeMode> {
return preferenceStore.getJsonObject("theme_mode", ThemeMode.System, ThemeMode.serializer())
}
class UiPreferences(
private val preferenceStore: PreferenceStore,
) {
fun themeMode(): Preference<ThemeMode> = preferenceStore.getJsonObject("theme_mode", ThemeMode.System, ThemeMode.serializer())
fun lightTheme(): Preference<Int> {
return preferenceStore.getInt("theme_light", 0)
}
fun lightTheme(): Preference<Int> = preferenceStore.getInt("theme_light", 0)
fun darkTheme(): Preference<Int> {
return preferenceStore.getInt("theme_dark", 0)
}
fun darkTheme(): Preference<Int> = preferenceStore.getInt("theme_dark", 0)
fun colorPrimaryLight(): Preference<Int> {
return preferenceStore.getInt("color_primary_light", 0)
}
fun colorPrimaryLight(): Preference<Int> = preferenceStore.getInt("color_primary_light", 0)
fun colorPrimaryDark(): Preference<Int> {
return preferenceStore.getInt("color_primary_dark", 0)
}
fun colorPrimaryDark(): Preference<Int> = preferenceStore.getInt("color_primary_dark", 0)
fun colorSecondaryLight(): Preference<Int> {
return preferenceStore.getInt("color_secondary_light", 0)
}
fun colorSecondaryLight(): Preference<Int> = preferenceStore.getInt("color_secondary_light", 0)
fun colorSecondaryDark(): Preference<Int> {
return preferenceStore.getInt("color_secondary_dark", 0)
}
fun colorSecondaryDark(): Preference<Int> = preferenceStore.getInt("color_secondary_dark", 0)
fun colorTertiaryLight(): Preference<Int> {
return preferenceStore.getInt("color_tertiary_light", 0)
}
fun colorTertiaryLight(): Preference<Int> = preferenceStore.getInt("color_tertiary_light", 0)
fun colorTertiaryDark(): Preference<Int> {
return preferenceStore.getInt("color_tertiary_dark", 0)
}
fun colorTertiaryDark(): Preference<Int> = preferenceStore.getInt("color_tertiary_dark", 0)
fun startScreen(): Preference<StartScreen> {
return preferenceStore.getJsonObject("start_screen", StartScreen.Library, StartScreen.serializer())
}
fun startScreen(): Preference<StartScreen> =
preferenceStore.getJsonObject(
"start_screen",
StartScreen.Library,
StartScreen.serializer(),
)
fun confirmExit(): Preference<Boolean> {
return preferenceStore.getBoolean("confirm_exit", false)
}
fun confirmExit(): Preference<Boolean> = preferenceStore.getBoolean("confirm_exit", false)
fun language(): Preference<String> {
return preferenceStore.getString("language", "")
}
fun language(): Preference<String> = preferenceStore.getString("language", "")
fun dateFormat(): Preference<String> {
return preferenceStore.getString("date_format", "")
}
fun dateFormat(): Preference<String> = preferenceStore.getString("date_format", "")
fun window(): Preference<WindowSettings> {
return preferenceStore.getJsonObject("window", WindowSettings(), WindowSettings.serializer())
}
fun window(): Preference<WindowSettings> = preferenceStore.getJsonObject("window", WindowSettings(), WindowSettings.serializer())
fun readerWindow(): Preference<WindowSettings> {
return preferenceStore.getJsonObject("reader_window", WindowSettings(), WindowSettings.serializer())
}
fun readerWindow(): Preference<WindowSettings> =
preferenceStore.getJsonObject(
"reader_window",
WindowSettings(),
WindowSettings.serializer(),
)
fun windowDecorations(): Preference<Boolean> {
return preferenceStore.getBoolean("window_decorations", true)
}
fun windowDecorations(): Preference<Boolean> = preferenceStore.getBoolean("window_decorations", true)
}

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class GetRecentUpdates
@Inject
constructor(private val updatesRepository: UpdatesRepository) {
constructor(
private val updatesRepository: UpdatesRepository,
) {
suspend fun await(
pageNum: Int,
onError: suspend (Throwable) -> Unit = {},

View File

@@ -15,7 +15,9 @@ import org.lighthousegames.logging.logging
class UpdateCategory
@Inject
constructor(private val updatesRepository: UpdatesRepository) {
constructor(
private val updatesRepository: UpdatesRepository,
) {
suspend fun await(
categoryId: Long,
onError: suspend (Throwable) -> Unit = {},

View File

@@ -52,7 +52,10 @@ class UpdateChecker
}.flowOn(Dispatchers.IO)
sealed class Update {
data class UpdateFound(val release: GithubRelease) : Update()
data class UpdateFound(
val release: GithubRelease,
) : Update()
object NoUpdatesFound : Update()
}

View File

@@ -14,7 +14,9 @@ import org.lighthousegames.logging.logging
class UpdateLibrary
@Inject
constructor(private val updatesRepository: UpdatesRepository) {
constructor(
private val updatesRepository: UpdatesRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {

View File

@@ -157,10 +157,15 @@ class UpdatesPager
@Immutable
sealed class Updates {
@Immutable
data class Update(val manga: Manga, val chapter: Chapter) : Updates()
data class Update(
val manga: Manga,
val chapter: Chapter,
) : Updates()
@Immutable
data class Date(val date: String) : Updates() {
data class Date(
val date: String,
) : Updates() {
constructor(date: LocalDate) : this(date.toString())
}
}

View File

@@ -9,8 +9,8 @@ package ca.gosyer.jui.domain.updates.service
import ca.gosyer.jui.core.prefs.Preference
import ca.gosyer.jui.core.prefs.PreferenceStore
class UpdatePreferences(private val preferenceStore: PreferenceStore) {
fun enabled(): Preference<Boolean> {
return preferenceStore.getBoolean("enabled", true)
}
class UpdatePreferences(
private val preferenceStore: PreferenceStore,
) {
fun enabled(): Preference<Boolean> = preferenceStore.getBoolean("enabled", true)
}

View File

@@ -10,80 +10,74 @@ import ca.gosyer.jui.core.prefs.Preference
import ca.gosyer.jui.core.prefs.PreferenceStore
import ca.gosyer.jui.domain.server.service.host.ServerHostPreference
class ServerHostPreferences(private val preferenceStore: PreferenceStore) {
fun host(): Preference<Boolean> {
return preferenceStore.getBoolean("host", true)
}
class ServerHostPreferences(
private val preferenceStore: PreferenceStore,
) {
fun host(): Preference<Boolean> = preferenceStore.getBoolean("host", true)
private val ip = ServerHostPreference.IP(preferenceStore)
fun ip(): Preference<String> {
return ip.preference()
}
fun ip(): Preference<String> = ip.preference()
private val port = ServerHostPreference.Port(preferenceStore)
fun port(): Preference<Int> {
return port.preference()
}
fun port(): Preference<Int> = port.preference()
// Proxy
private val socksProxyEnabled = ServerHostPreference.SocksProxyEnabled(preferenceStore)
fun socksProxyEnabled(): Preference<Boolean> {
return socksProxyEnabled.preference()
}
fun socksProxyEnabled(): Preference<Boolean> = socksProxyEnabled.preference()
private val socksProxyHost = ServerHostPreference.SocksProxyHost(preferenceStore)
fun socksProxyHost(): Preference<String> {
return socksProxyHost.preference()
}
fun socksProxyHost(): Preference<String> = socksProxyHost.preference()
private val socksProxyPort = ServerHostPreference.SocksProxyPort(preferenceStore)
fun socksProxyPort(): Preference<Int> {
return socksProxyPort.preference()
}
fun socksProxyPort(): Preference<Int> = socksProxyPort.preference()
// Misc
private val debugLogsEnabled = ServerHostPreference.DebugLogsEnabled(preferenceStore)
fun debugLogsEnabled(): Preference<Boolean> {
return debugLogsEnabled.preference()
}
fun debugLogsEnabled(): Preference<Boolean> = debugLogsEnabled.preference()
private val systemTrayEnabled = ServerHostPreference.SystemTrayEnabled(preferenceStore)
fun systemTrayEnabled(): Preference<Boolean> {
return systemTrayEnabled.preference()
}
fun systemTrayEnabled(): Preference<Boolean> = systemTrayEnabled.preference()
// Downloader
private val downloadPath = ServerHostPreference.DownloadPath(preferenceStore)
fun downloadPath(): Preference<String> {
return downloadPath.preference()
}
fun downloadPath(): Preference<String> = downloadPath.preference()
private val downloadAsCbz = ServerHostPreference.DownloadAsCbz(preferenceStore)
fun downloadAsCbz(): Preference<Boolean> {
return downloadAsCbz.preference()
}
fun downloadAsCbz(): Preference<Boolean> = downloadAsCbz.preference()
// WebUI
private val webUIEnabled = ServerHostPreference.WebUIEnabled(preferenceStore)
fun webUIEnabled(): Preference<Boolean> {
return webUIEnabled.preference()
}
fun webUIEnabled(): Preference<Boolean> = webUIEnabled.preference()
private val openInBrowserEnabled = ServerHostPreference.OpenInBrowserEnabled(preferenceStore)
fun openInBrowserEnabled(): Preference<Boolean> {
return openInBrowserEnabled.preference()
}
fun openInBrowserEnabled(): Preference<Boolean> = openInBrowserEnabled.preference()
// Authentication
private val basicAuthEnabled = ServerHostPreference.BasicAuthEnabled(preferenceStore)
fun basicAuthEnabled(): Preference<Boolean> {
return basicAuthEnabled.preference()
}
private val basicAuthUsername = ServerHostPreference.BasicAuthUsername(preferenceStore)
fun basicAuthUsername(): Preference<String> {
return basicAuthUsername.preference()
}
private val basicAuthPassword = ServerHostPreference.BasicAuthPassword(preferenceStore)
fun basicAuthPassword(): Preference<String> {
return basicAuthPassword.preference()
}
fun properties(): Array<String> {
return listOf(
fun basicAuthEnabled(): Preference<Boolean> = basicAuthEnabled.preference()
private val basicAuthUsername = ServerHostPreference.BasicAuthUsername(preferenceStore)
fun basicAuthUsername(): Preference<String> = basicAuthUsername.preference()
private val basicAuthPassword = ServerHostPreference.BasicAuthPassword(preferenceStore)
fun basicAuthPassword(): Preference<String> = basicAuthPassword.preference()
fun properties(): Array<String> =
listOf(
ip,
port,
socksProxyEnabled,
@@ -101,5 +95,4 @@ class ServerHostPreferences(private val preferenceStore: PreferenceStore) {
).mapNotNull {
it.getProperty()
}.toTypedArray()
}
}

View File

@@ -16,6 +16,7 @@ sealed class ServerHostPreference<T : Any> {
protected abstract val defaultValue: T
protected abstract val serverValue: T
private fun validate(value: T): Boolean {
return value != serverValue
}
@@ -30,6 +31,7 @@ sealed class ServerHostPreference<T : Any> {
}
protected abstract val preferenceStore: PreferenceStore
abstract fun preference(): Preference<T>
companion object {
@@ -42,114 +44,144 @@ sealed class ServerHostPreference<T : Any> {
override val defaultValue: String,
override val serverValue: String = defaultValue,
) : ServerHostPreference<String>() {
override fun preference(): Preference<String> {
return preferenceStore.getString(propertyName, defaultValue)
}
override fun preference(): Preference<String> = preferenceStore.getString(propertyName, defaultValue)
}
sealed class IntServerHostPreference(
override val preferenceStore: PreferenceStore,
override val propertyName: String,
override val defaultValue: Int,
override val serverValue: Int = defaultValue,
) : ServerHostPreference<Int>() {
override fun preference(): Preference<Int> {
return preferenceStore.getInt(propertyName, defaultValue)
}
override fun preference(): Preference<Int> = preferenceStore.getInt(propertyName, defaultValue)
}
sealed class BooleanServerHostPreference(
override val preferenceStore: PreferenceStore,
override val propertyName: String,
override val defaultValue: Boolean,
override val serverValue: Boolean = defaultValue,
) : ServerHostPreference<Boolean>() {
override fun preference(): Preference<Boolean> {
return preferenceStore.getBoolean(propertyName, defaultValue)
}
override fun preference(): Preference<Boolean> = preferenceStore.getBoolean(propertyName, defaultValue)
}
class IP(preferenceStore: PreferenceStore) : StringServerHostPreference(
preferenceStore,
"ip",
"0.0.0.0",
)
class Port(override val preferenceStore: PreferenceStore) : IntServerHostPreference(
preferenceStore,
"port",
4567,
)
class IP(
preferenceStore: PreferenceStore,
) : StringServerHostPreference(
preferenceStore,
"ip",
"0.0.0.0",
)
class Port(
override val preferenceStore: PreferenceStore,
) : IntServerHostPreference(
preferenceStore,
"port",
4567,
)
// Proxy
class SocksProxyEnabled(preferenceStore: PreferenceStore) : BooleanServerHostPreference(
preferenceStore,
"socksProxyEnabled",
false,
)
class SocksProxyHost(preferenceStore: PreferenceStore) : StringServerHostPreference(
preferenceStore,
"socksProxyHost",
"",
)
class SocksProxyPort(override val preferenceStore: PreferenceStore) : IntServerHostPreference(
preferenceStore,
"socksProxyPort",
0,
)
class SocksProxyEnabled(
preferenceStore: PreferenceStore,
) : BooleanServerHostPreference(
preferenceStore,
"socksProxyEnabled",
false,
)
class SocksProxyHost(
preferenceStore: PreferenceStore,
) : StringServerHostPreference(
preferenceStore,
"socksProxyHost",
"",
)
class SocksProxyPort(
override val preferenceStore: PreferenceStore,
) : IntServerHostPreference(
preferenceStore,
"socksProxyPort",
0,
)
// Misc
class DebugLogsEnabled(preferenceStore: PreferenceStore) : BooleanServerHostPreference(
preferenceStore,
"debugLogsEnabled",
false,
)
class DebugLogsEnabled(
preferenceStore: PreferenceStore,
) : BooleanServerHostPreference(
preferenceStore,
"debugLogsEnabled",
false,
)
class SystemTrayEnabled(preferenceStore: PreferenceStore) : BooleanServerHostPreference(
preferenceStore,
"systemTrayEnabled",
false,
true,
)
class SystemTrayEnabled(
preferenceStore: PreferenceStore,
) : BooleanServerHostPreference(
preferenceStore,
"systemTrayEnabled",
false,
true,
)
// Downloader
class DownloadPath(preferenceStore: PreferenceStore) : StringServerHostPreference(
preferenceStore,
"downloadsPath",
"",
)
class DownloadAsCbz(preferenceStore: PreferenceStore) : BooleanServerHostPreference(
preferenceStore,
"downloadAsCbz",
false,
)
class DownloadPath(
preferenceStore: PreferenceStore,
) : StringServerHostPreference(
preferenceStore,
"downloadsPath",
"",
)
class DownloadAsCbz(
preferenceStore: PreferenceStore,
) : BooleanServerHostPreference(
preferenceStore,
"downloadAsCbz",
false,
)
// WebUI
class WebUIEnabled(preferenceStore: PreferenceStore) : BooleanServerHostPreference(
preferenceStore,
"webUIEnabled",
false,
true,
)
class WebUIEnabled(
preferenceStore: PreferenceStore,
) : BooleanServerHostPreference(
preferenceStore,
"webUIEnabled",
false,
true,
)
class OpenInBrowserEnabled(preferenceStore: PreferenceStore) : BooleanServerHostPreference(
preferenceStore,
"initialOpenInBrowserEnabled",
false,
true,
)
class OpenInBrowserEnabled(
preferenceStore: PreferenceStore,
) : BooleanServerHostPreference(
preferenceStore,
"initialOpenInBrowserEnabled",
false,
true,
)
// Authentication
class BasicAuthEnabled(preferenceStore: PreferenceStore) : BooleanServerHostPreference(
preferenceStore,
"basicAuthEnabled",
false,
)
class BasicAuthUsername(preferenceStore: PreferenceStore) : StringServerHostPreference(
preferenceStore,
"basicAuthUsername",
"",
)
class BasicAuthPassword(preferenceStore: PreferenceStore) : StringServerHostPreference(
preferenceStore,
"basicAuthPassword",
"",
)
class BasicAuthEnabled(
preferenceStore: PreferenceStore,
) : BooleanServerHostPreference(
preferenceStore,
"basicAuthEnabled",
false,
)
class BasicAuthUsername(
preferenceStore: PreferenceStore,
) : StringServerHostPreference(
preferenceStore,
"basicAuthUsername",
"",
)
class BasicAuthPassword(
preferenceStore: PreferenceStore,
) : StringServerHostPreference(
preferenceStore,
"basicAuthPassword",
"",
)
}

View File

@@ -20,7 +20,10 @@ import me.tatarka.inject.annotations.Provides
abstract class AppComponent(
@get:Provides
val context: ContextWrapper,
) : ViewModelComponent, DataComponent, DomainComponent, UiComponent {
) : ViewModelComponent,
DataComponent,
DomainComponent,
UiComponent {
abstract val appMigrations: AppMigrations
@get:AppScope

View File

@@ -4,11 +4,9 @@ package ca.gosyer.jui.ios
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Card
import androidx.compose.material.Text
@@ -33,34 +31,10 @@ import ca.gosyer.jui.uicore.vm.ContextWrapper
import ca.gosyer.jui.uicore.vm.Length
import kotlinx.cinterop.BetaInteropApi
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.autoreleasepool
import kotlinx.cinterop.cstr
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.toCValues
import kotlinx.cinterop.useContents
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.lighthousegames.logging.FixedLogLevel
import org.lighthousegames.logging.KmLog
import org.lighthousegames.logging.KmLogging
import org.lighthousegames.logging.LogFactory
import org.lighthousegames.logging.LogLevel
import org.lighthousegames.logging.LogLevelController
import org.lighthousegames.logging.Logger
import org.lighthousegames.logging.TagProvider
import platform.Foundation.NSStringFromClass
import platform.Foundation.NSThread
import platform.UIKit.UIApplication
import platform.UIKit.UIApplicationDelegateProtocol
import platform.UIKit.UIApplicationDelegateProtocolMeta
import platform.UIKit.UIApplicationMain
import platform.UIKit.UIResponder
import platform.UIKit.UIResponderMeta
import platform.UIKit.UIScreen
import platform.UIKit.UIViewController
import platform.UIKit.UIWindow
import platform.UIKit.safeAreaInsets
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds

View File

@@ -17,7 +17,9 @@ import androidx.compose.ui.platform.LocalContext
import okio.Source
import okio.source
actual class FileChooser(private val resultLauncher: ManagedActivityResultLauncher<String, Uri?>) {
actual class FileChooser(
private val resultLauncher: ManagedActivityResultLauncher<String, Uri?>,
) {
actual fun launch(extension: String) {
resultLauncher.launch(MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension))
}

View File

@@ -10,5 +10,6 @@ import ca.gosyer.jui.uicore.vm.ContextWrapper
import com.seiko.imageloader.component.decoder.BitmapFactoryDecoder
import com.seiko.imageloader.component.decoder.Decoder
actual class BitmapDecoderFactory actual constructor(contextWrapper: ContextWrapper) :
Decoder.Factory by BitmapFactoryDecoder.Factory(contextWrapper, Int.MAX_VALUE)
actual class BitmapDecoderFactory actual constructor(
contextWrapper: ContextWrapper,
) : Decoder.Factory by BitmapFactoryDecoder.Factory(contextWrapper, Int.MAX_VALUE)

View File

@@ -15,11 +15,10 @@ internal actual fun Color.toHsv(): FloatArray {
return result
}
internal actual fun hexStringToColor(hex: String): Color? {
return try {
internal actual fun hexStringToColor(hex: String): Color? =
try {
val color = android.graphics.Color.parseColor(hex)
Color(color)
} catch (e: Exception) {
null
}
}

View File

@@ -107,22 +107,20 @@ internal class SavedStateHandlesProvider(
/**
* Restore the state associated with a particular SavedStateHandle, identified by its [key]
*/
fun consumeRestoredStateForKey(key: String): Bundle? {
return savedStateRegistry::class.java
fun consumeRestoredStateForKey(key: String): Bundle? =
savedStateRegistry::class.java
.methods
.find { it.name == "consumeRestoredStateForKey" }
?.invoke(savedStateRegistry, key) as? Bundle
}
}
inline fun <reified T : ScreenModel> CreationExtras.addScreenModelKey(
screen: Screen,
tag: String?,
): CreationExtras {
return MutableCreationExtras(this).apply {
): CreationExtras =
MutableCreationExtras(this).apply {
set(
VIEW_MODEL_KEY,
"${screen.key}:${T::class.qualifiedName}:${tag ?: "default"}",
)
}
}

View File

@@ -15,7 +15,5 @@ actual object ThemeScrollbarStyle {
@Stable
@Composable
actual fun getScrollbarStyle(): ScrollbarStyle {
return defaultScrollbarStyle
}
actual fun getScrollbarStyle(): ScrollbarStyle = defaultScrollbarStyle
}

View File

@@ -11,7 +11,9 @@ import androidx.compose.runtime.remember
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.Navigator
actual class CategoriesLauncher(private val navigator: Navigator?) {
actual class CategoriesLauncher(
private val navigator: Navigator?,
) {
actual fun open() {
navigator?.push(CategoriesScreen())
}

View File

@@ -9,8 +9,8 @@ package ca.gosyer.jui.ui.main.about.components
import android.os.Build
import ca.gosyer.jui.presentation.build.BuildKonfig
actual fun getDebugInfo(): String {
return """
actual fun getDebugInfo(): String =
"""
App version: ${BuildKonfig.VERSION} (${ if (BuildKonfig.DEBUG) "Debug" else "Standard"}, ${BuildKonfig.MIGRATION_CODE})
Preview build: r${BuildKonfig.PREVIEW_BUILD}
Android version: ${Build.VERSION.RELEASE} (SDK ${Build.VERSION.SDK_INT})
@@ -20,5 +20,4 @@ actual fun getDebugInfo(): String {
Device name: ${Build.DEVICE}
Device model: ${Build.MODEL}
Device product name: ${Build.PRODUCT}
""".trimIndent()
}
""".trimIndent()

View File

@@ -12,7 +12,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
actual class ReaderLauncher(private val context: Context) {
actual class ReaderLauncher(
private val context: Context,
) {
actual fun launch(
chapterIndex: Int,
mangaId: Long,

View File

@@ -13,10 +13,10 @@ import ca.gosyer.jui.uicore.vm.ViewModel
import me.tatarka.inject.annotations.Inject
@Composable
actual fun getServerHostItems(viewModel: @Composable () -> SettingsServerHostViewModel): LazyListScope.() -> Unit {
return {}
}
actual fun getServerHostItems(viewModel: @Composable () -> SettingsServerHostViewModel): LazyListScope.() -> Unit = {}
actual class SettingsServerHostViewModel
@Inject
constructor(contextWrapper: ContextWrapper) : ViewModel(contextWrapper)
constructor(
contextWrapper: ContextWrapper,
) : ViewModel(contextWrapper)

View File

@@ -13,6 +13,4 @@ import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.bodyAsChannel
import io.ktor.utils.io.jvm.javaio.toInputStream
actual suspend fun HttpResponse.toImageBitmap(): ImageBitmap {
return BitmapFactory.decodeStream(bodyAsChannel().toInputStream()).asImageBitmap()
}
actual suspend fun HttpResponse.toImageBitmap(): ImageBitmap = BitmapFactory.decodeStream(bodyAsChannel().toInputStream()).asImageBitmap()

View File

@@ -91,9 +91,7 @@ data class ChapterDownloadItem(
_downloadState.value = ChapterDownloadState.NotDownloaded
}
fun isSelected(selectedItems: List<Long>): Boolean {
return (chapter.id in selectedItems).also { _isSelected.value = it }
}
fun isSelected(selectedItems: List<Long>): Boolean = (chapter.id in selectedItems).also { _isSelected.value = it }
}
enum class ChapterDownloadState {

View File

@@ -29,8 +29,8 @@ fun getMaterialDialogProperties(
title: String = BuildKonfig.NAME,
icon: Painter = MR.images.icon.toPainter(),
resizable: Boolean = true,
): MaterialDialogProperties {
return MaterialDialogProperties(
): MaterialDialogProperties =
MaterialDialogProperties(
dismissOnBackPress = dismissOnBackPress,
dismissOnClickOutside = dismissOnClickOutside,
securePolicy = securePolicy,
@@ -41,4 +41,3 @@ fun getMaterialDialogProperties(
windowIcon = icon,
windowIsResizable = resizable,
)
}

View File

@@ -9,4 +9,6 @@ package ca.gosyer.jui.ui.base.image
import ca.gosyer.jui.uicore.vm.ContextWrapper
import com.seiko.imageloader.component.decoder.Decoder
expect class BitmapDecoderFactory constructor(contextWrapper: ContextWrapper) : Decoder.Factory
expect class BitmapDecoderFactory constructor(
contextWrapper: ContextWrapper,
) : Decoder.Factory

View File

@@ -10,11 +10,15 @@ import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
@Stable
class StableHolder<T>(val item: T) {
class StableHolder<T>(
val item: T,
) {
operator fun component1(): T = item
}
@Immutable
class ImmutableHolder<T>(val item: T) {
class ImmutableHolder<T>(
val item: T,
) {
operator fun component1(): T = item
}

View File

@@ -75,7 +75,8 @@ enum class OverflowMode {
@Composable
fun ActionMenu(
items: ImmutableList<Action>,
numIcons: Int = 3, // includes overflow menu icon; may be overridden by NEVER_OVERFLOW
// includes overflow menu icon; may be overridden by NEVER_OVERFLOW
numIcons: Int = 3,
menuVisible: MutableState<Boolean> = remember { mutableStateOf(false) },
iconItem: @Composable (onClick: () -> Unit, name: String, icon: ImageVector, enabled: Boolean) -> Unit,
) {
@@ -99,7 +100,9 @@ fun ActionMenu(
} else {
TextButton(
onClick = when (item) {
is ActionGroup -> { { openGroup = item } }
is ActionGroup -> {
{ openGroup = item }
}
is ActionItem -> item.doAction
},
enabled = item.enabled,

View File

@@ -24,6 +24,7 @@ class DisplayController(
fun openSideMenu() {
_sideMenuVisible.value = true
}
fun closeSideMenu() {
_sideMenuVisible.value = false
}

View File

@@ -88,8 +88,10 @@ fun Toolbar(
closeIcon: ImageVector = ToolbarDefault,
modifier: Modifier = Modifier,
actions: @Composable () -> ImmutableList<Action> = { remember { persistentListOf() } },
backgroundColor: Color = MaterialTheme.colors.surface, // CustomColors.current.bars,
contentColor: Color = contentColorFor(backgroundColor), // CustomColors.current.onBars,
// CustomColors.current.bars,
backgroundColor: Color = MaterialTheme.colors.surface,
// CustomColors.current.onBars,
contentColor: Color = contentColorFor(backgroundColor),
elevation: Dp = Dp.Hairline,
searchText: String? = null,
search: ((String) -> Unit)? = null,
@@ -216,6 +218,7 @@ private fun ThinToolbar(
searchSubmit: (() -> Unit)?,
) {
var searchMode by remember { mutableStateOf(!searchText.isNullOrEmpty()) }
fun closeSearch() {
search?.invoke("")
searchSubmit?.invoke()

View File

@@ -421,16 +421,12 @@ private fun satValToCoordinates(
saturation: Float,
value: Float,
size: IntSize,
): Offset {
return Offset(saturation * size.width, ((1f - value) * size.height))
}
): Offset = Offset(saturation * size.width, ((1f - value) * size.height))
private fun hueToCoordinate(
hue: Float,
size: IntSize,
): Float {
return size.height - (hue * size.height / 360f)
}
): Float = size.height - (hue * size.height / 360f)
// Color space conversions
@@ -439,36 +435,51 @@ fun hsvToColor(
hue: Float,
saturation: Float,
value: Float,
): Color {
return Color.hsv(hue, saturation.coerceIn(0F, 1F), value.coerceIn(0F, 1F))
}
): Color = Color.hsv(hue, saturation.coerceIn(0F, 1F), value.coerceIn(0F, 1F))
internal expect fun Color.toHsv(): FloatArray
private fun hueToColor(hue: Float): Color {
return hsvToColor(hue, 1f, 1f)
}
private fun hueToColor(hue: Float): Color = hsvToColor(hue, 1f, 1f)
internal expect fun hexStringToColor(hex: String): Color?
private val presetColors = listOf(
Color(0xFFF44336), // RED 500
Color(0xFFE91E63), // PINK 500
Color(0xFFFF2C93), // LIGHT PINK 500
Color(0xFF9C27B0), // PURPLE 500
Color(0xFF673AB7), // DEEP PURPLE 500
Color(0xFF3F51B5), // INDIGO 500
Color(0xFF2196F3), // BLUE 500
Color(0xFF03A9F4), // LIGHT BLUE 500
Color(0xFF00BCD4), // CYAN 500
Color(0xFF009688), // TEAL 500
Color(0xFF4CAF50), // GREEN 500
Color(0xFF8BC34A), // LIGHT GREEN 500
Color(0xFFCDDC39), // LIME 500
Color(0xFFFFEB3B), // YELLOW 500
Color(0xFFFFC107), // AMBER 500
Color(0xFFFF9800), // ORANGE 500
Color(0xFF795548), // BROWN 500
Color(0xFF607D8B), // BLUE GREY 500
Color(0xFF9E9E9E), // GREY 500
// RED 500
Color(0xFFF44336),
// PINK 500
Color(0xFFE91E63),
// LIGHT PINK 500
Color(0xFFFF2C93),
// PURPLE 500
Color(0xFF9C27B0),
// DEEP PURPLE 500
Color(0xFF673AB7),
// INDIGO 500
Color(0xFF3F51B5),
// BLUE 500
Color(0xFF2196F3),
// LIGHT BLUE 500
Color(0xFF03A9F4),
// CYAN 500
Color(0xFF00BCD4),
// TEAL 500
Color(0xFF009688),
// GREEN 500
Color(0xFF4CAF50),
// LIGHT GREEN 500
Color(0xFF8BC34A),
// LIME 500
Color(0xFFCDDC39),
// YELLOW 500
Color(0xFFFFEB3B),
// AMBER 500
Color(0xFFFFC107),
// ORANGE 500
Color(0xFFFF9800),
// BROWN 500
Color(0xFF795548),
// BLUE GREY 500
Color(0xFF607D8B),
// GREY 500
Color(0xFF9E9E9E),
).toImmutableList()

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