diff --git a/buildSrc/src/main/kotlin/lib-multisrc.gradle.kts b/buildSrc/src/main/kotlin/lib-multisrc.gradle.kts index 402ebebf6..16f51e489 100644 --- a/buildSrc/src/main/kotlin/lib-multisrc.gradle.kts +++ b/buildSrc/src/main/kotlin/lib-multisrc.gradle.kts @@ -38,6 +38,7 @@ kotlinter { dependencies { compileOnly(versionCatalogs.named("libs").findBundle("common").get()) + implementation(project(":utils")) } tasks { diff --git a/common.gradle b/common.gradle index f916f509a..95c5cf6d3 100644 --- a/common.gradle +++ b/common.gradle @@ -103,6 +103,7 @@ dependencies { if (theme != null) implementation(theme) // Overrides core launcher icons implementation(project(":core")) compileOnly(libs.bundles.common) + implementation(project(":utils")) } tasks.register("writeManifestFile") { diff --git a/settings.gradle.kts b/settings.gradle.kts index 18932239c..ab0ee69b0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,5 @@ include(":core") +include(":utils") // Load all modules under /lib File(rootDir, "lib").eachDir { include("lib:${it.name}") } diff --git a/utils/build.gradle.kts b/utils/build.gradle.kts new file mode 100644 index 000000000..81dd240c5 --- /dev/null +++ b/utils/build.gradle.kts @@ -0,0 +1,33 @@ +plugins { + id("com.android.library") + kotlin("android") +} + +android { + compileSdk = AndroidConfig.compileSdk + + defaultConfig { + minSdk = AndroidConfig.minSdk + } + + namespace = "keiyoushi.utils" + + sourceSets { + named("main") { + java.setSrcDirs(listOf("src")) + } + } + + buildFeatures { + resValues = false + shaders = false + } + + kotlinOptions { + freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi" + } +} + +dependencies { + compileOnly(versionCatalogs.named("libs").findBundle("common").get()) +} diff --git a/utils/src/keiyoushi/utils/Collections.kt b/utils/src/keiyoushi/utils/Collections.kt new file mode 100644 index 000000000..4c1ba7c46 --- /dev/null +++ b/utils/src/keiyoushi/utils/Collections.kt @@ -0,0 +1,13 @@ +package keiyoushi.utils + +/** + * Returns the first element that is an instances of specified type parameter T. + * + * @throws [NoSuchElementException] if no such element is found. + */ +inline fun Iterable<*>.firstInstance(): T = first { it is T } as T + +/** + * Returns the first element that is an instances of specified type parameter T, or `null` if element was not found. + */ +inline fun Iterable<*>.firstInstanceOrNull(): T? = firstOrNull { it is T } as? T diff --git a/utils/src/keiyoushi/utils/Date.kt b/utils/src/keiyoushi/utils/Date.kt new file mode 100644 index 000000000..b395d4869 --- /dev/null +++ b/utils/src/keiyoushi/utils/Date.kt @@ -0,0 +1,15 @@ +package keiyoushi.utils + +import java.text.ParseException +import java.text.SimpleDateFormat + +@Suppress("NOTHING_TO_INLINE") +inline fun SimpleDateFormat.tryParse(date: String?): Long { + date ?: return 0L + + return try { + parse(date)?.time ?: 0L + } catch (_: ParseException) { + 0L + } +} diff --git a/utils/src/keiyoushi/utils/Json.kt b/utils/src/keiyoushi/utils/Json.kt new file mode 100644 index 000000000..cc0a98644 --- /dev/null +++ b/utils/src/keiyoushi/utils/Json.kt @@ -0,0 +1,21 @@ +package keiyoushi.utils + +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.decodeFromStream +import okhttp3.Response +import uy.kohesive.injekt.injectLazy + +val jsonInstance: Json by injectLazy() + +/** + * Parses and serializes the String as the type . + */ +inline fun String.parseAs(json: Json = jsonInstance): T = + json.decodeFromString(this) + +/** + * Parse and serialize the response body as the type . + */ +inline fun Response.parseAs(json: Json = jsonInstance): T = + json.decodeFromStream(body.byteStream()) diff --git a/utils/src/keiyoushi/utils/Preferences.kt b/utils/src/keiyoushi/utils/Preferences.kt new file mode 100644 index 000000000..e2d6686de --- /dev/null +++ b/utils/src/keiyoushi/utils/Preferences.kt @@ -0,0 +1,29 @@ +package keiyoushi.utils + +import android.app.Application +import android.content.SharedPreferences +import eu.kanade.tachiyomi.source.online.HttpSource +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +/** + * Returns the [SharedPreferences] associated with current source id + */ +inline fun HttpSource.getPreferences( + migration: SharedPreferences.() -> Unit = { }, +): SharedPreferences = getPreferences(id).also(migration) + +/** + * Lazily returns the [SharedPreferences] associated with current source id + */ +inline fun HttpSource.getPreferencesLazy( + crossinline migration: SharedPreferences.() -> Unit = { } +) = lazy { getPreferences(migration) } + +/** + * Returns the [SharedPreferences] associated with passed source id + */ +@Suppress("NOTHING_TO_INLINE") +inline fun getPreferences(sourceId: Long): SharedPreferences = + Injekt.get().getSharedPreferences("source_$sourceId", 0x0000) +