Convert source modules to Kotlin Multiplatform (#9172)

Use KMP in source modules 


Use KMP in source-api


Expect LocalSource
This commit is contained in:
Andreas 2023-03-05 16:16:19 +01:00 committed by GitHub
parent b41565f879
commit 1abf01c4a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 91 additions and 49 deletions

View file

@ -48,9 +48,7 @@ import tachiyomi.data.Mangas
import tachiyomi.data.dateAdapter import tachiyomi.data.dateAdapter
import tachiyomi.data.listOfStringsAdapter import tachiyomi.data.listOfStringsAdapter
import tachiyomi.data.updateStrategyAdapter import tachiyomi.data.updateStrategyAdapter
import tachiyomi.source.local.image.AndroidLocalCoverManager
import tachiyomi.source.local.image.LocalCoverManager import tachiyomi.source.local.image.LocalCoverManager
import tachiyomi.source.local.io.AndroidLocalSourceFileSystem
import tachiyomi.source.local.io.LocalSourceFileSystem import tachiyomi.source.local.io.LocalSourceFileSystem
import uy.kohesive.injekt.api.InjektModule import uy.kohesive.injekt.api.InjektModule
import uy.kohesive.injekt.api.InjektRegistrar import uy.kohesive.injekt.api.InjektRegistrar
@ -137,8 +135,8 @@ class AppModule(val app: Application) : InjektModule {
addSingletonFactory { ImageSaver(app) } addSingletonFactory { ImageSaver(app) }
addSingletonFactory<LocalSourceFileSystem> { AndroidLocalSourceFileSystem(app) } addSingletonFactory { LocalSourceFileSystem(app) }
addSingletonFactory<LocalCoverManager> { AndroidLocalCoverManager(app, get()) } addSingletonFactory { LocalCoverManager(app, get()) }
// Asynchronously init expensive components for a faster cold start // Asynchronously init expensive components for a faster cold start
ContextCompat.getMainExecutor(app).execute { ContextCompat.getMainExecutor(app).execute {

View file

@ -22,3 +22,5 @@ org.gradle.caching=true
# AndroidX support # AndroidX support
android.useAndroidX=true android.useAndroidX=true
kotlin.mpp.androidSourceSetLayoutVersion=2

View file

@ -1,7 +1,27 @@
plugins { plugins {
id("com.android.library") kotlin("multiplatform")
kotlin("android")
kotlin("plugin.serialization") kotlin("plugin.serialization")
id("com.android.library")
}
kotlin {
android()
sourceSets {
val commonMain by getting {
dependencies {
api(kotlinx.serialization.json)
api(libs.injekt.core)
api(libs.rxjava)
api(libs.jsoup)
}
}
val androidMain by getting {
dependencies {
implementation(project(":core"))
api(libs.preferencektx)
}
}
}
} }
android { android {
@ -11,13 +31,3 @@ android {
consumerProguardFile("consumer-proguard.pro") consumerProguardFile("consumer-proguard.pro")
} }
} }
dependencies {
implementation(project(":core"))
api(kotlinx.serialization.json)
api(libs.injekt.core)
api(libs.rxjava)
api(libs.preferencektx)
api(libs.jsoup)
}

View file

@ -0,0 +1,3 @@
package eu.kanade.tachiyomi.source
actual typealias PreferenceScreen = androidx.preference.PreferenceScreen

View file

@ -0,0 +1,6 @@
package eu.kanade.tachiyomi.util
import rx.Observable
import tachiyomi.core.util.lang.awaitSingle
actual suspend fun <T> Observable<T>.awaitSingle(): T = awaitSingle()

View file

@ -1,7 +1,5 @@
package eu.kanade.tachiyomi.source package eu.kanade.tachiyomi.source
import androidx.preference.PreferenceScreen
interface ConfigurableSource : Source { interface ConfigurableSource : Source {
fun setupPreferenceScreen(screen: PreferenceScreen) fun setupPreferenceScreen(screen: PreferenceScreen)

View file

@ -0,0 +1,3 @@
package eu.kanade.tachiyomi.source
expect class PreferenceScreen

View file

@ -3,8 +3,8 @@ package eu.kanade.tachiyomi.source
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.awaitSingle
import rx.Observable import rx.Observable
import tachiyomi.core.util.lang.awaitSingle
/** /**
* A basic interface for creating a source. It could be an online source, a local source, etc... * A basic interface for creating a source. It could be an online source, a local source, etc...

View file

@ -0,0 +1,5 @@
package eu.kanade.tachiyomi.util
import rx.Observable
expect suspend fun <T> Observable<T>.awaitSingle(): T

View file

@ -1,21 +1,20 @@
plugins { plugins {
kotlin("multiplatform")
id("com.android.library") id("com.android.library")
kotlin("android")
}
android {
namespace = "tachiyomi.source.local"
defaultConfig {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
} }
kotlin {
android()
sourceSets {
val commonMain by getting {
dependencies { dependencies {
implementation(project(":source-api")) implementation(project(":source-api"))
implementation(libs.unifile)
implementation(libs.junrar)
}
}
val androidMain by getting {
dependencies {
implementation(project(":core")) implementation(project(":core"))
implementation(project(":core-metadata")) implementation(project(":core-metadata"))
@ -23,7 +22,16 @@ dependencies {
implementation(project(":domain")) implementation(project(":domain"))
implementation(kotlinx.bundles.serialization) implementation(kotlinx.bundles.serialization)
}
implementation(libs.unifile) }
implementation(libs.junrar) }
}
android {
namespace = "tachiyomi.source.local"
defaultConfig {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
} }

View file

@ -40,7 +40,7 @@ import java.util.concurrent.TimeUnit
import java.util.zip.ZipFile import java.util.zip.ZipFile
import com.github.junrar.Archive as JunrarArchive import com.github.junrar.Archive as JunrarArchive
class LocalSource( actual class LocalSource(
private val context: Context, private val context: Context,
private val fileSystem: LocalSourceFileSystem, private val fileSystem: LocalSourceFileSystem,
private val coverManager: LocalCoverManager, private val coverManager: LocalCoverManager,

View file

@ -11,12 +11,12 @@ import java.io.InputStream
private const val DEFAULT_COVER_NAME = "cover.jpg" private const val DEFAULT_COVER_NAME = "cover.jpg"
class AndroidLocalCoverManager( actual class LocalCoverManager(
private val context: Context, private val context: Context,
private val fileSystem: LocalSourceFileSystem, private val fileSystem: LocalSourceFileSystem,
) : LocalCoverManager { ) {
override fun find(mangaUrl: String): File? { actual fun find(mangaUrl: String): File? {
return fileSystem.getFilesInMangaDirectory(mangaUrl) return fileSystem.getFilesInMangaDirectory(mangaUrl)
// Get all file whose names start with 'cover' // Get all file whose names start with 'cover'
.filter { it.isFile && it.nameWithoutExtension.equals("cover", ignoreCase = true) } .filter { it.isFile && it.nameWithoutExtension.equals("cover", ignoreCase = true) }
@ -26,7 +26,10 @@ class AndroidLocalCoverManager(
} }
} }
override fun update(manga: SManga, inputStream: InputStream): File? { actual fun update(
manga: SManga,
inputStream: InputStream,
): File? {
val directory = fileSystem.getMangaDirectory(manga.url) val directory = fileSystem.getMangaDirectory(manga.url)
if (directory == null) { if (directory == null) {
inputStream.close() inputStream.close()

View file

@ -5,31 +5,31 @@ import eu.kanade.tachiyomi.util.storage.DiskUtil
import tachiyomi.source.local.R import tachiyomi.source.local.R
import java.io.File import java.io.File
class AndroidLocalSourceFileSystem( actual class LocalSourceFileSystem(
private val context: Context, private val context: Context,
) : LocalSourceFileSystem { ) {
private val baseFolderLocation = "${context.getString(R.string.app_name)}${File.separator}local" private val baseFolderLocation = "${context.getString(R.string.app_name)}${File.separator}local"
override fun getBaseDirectories(): Sequence<File> { actual fun getBaseDirectories(): Sequence<File> {
return DiskUtil.getExternalStorages(context) return DiskUtil.getExternalStorages(context)
.map { File(it.absolutePath, baseFolderLocation) } .map { File(it.absolutePath, baseFolderLocation) }
.asSequence() .asSequence()
} }
override fun getFilesInBaseDirectories(): Sequence<File> { actual fun getFilesInBaseDirectories(): Sequence<File> {
return getBaseDirectories() return getBaseDirectories()
// Get all the files inside all baseDir // Get all the files inside all baseDir
.flatMap { it.listFiles().orEmpty().toList() } .flatMap { it.listFiles().orEmpty().toList() }
} }
override fun getMangaDirectory(name: String): File? { actual fun getMangaDirectory(name: String): File? {
return getFilesInBaseDirectories() return getFilesInBaseDirectories()
// Get the first mangaDir or null // Get the first mangaDir or null
.firstOrNull { it.isDirectory && it.name == name } .firstOrNull { it.isDirectory && it.name == name }
} }
override fun getFilesInMangaDirectory(name: String): Sequence<File> { actual fun getFilesInMangaDirectory(name: String): Sequence<File> {
return getFilesInBaseDirectories() return getFilesInBaseDirectories()
// Filter out ones that are not related to the manga and is not a directory // Filter out ones that are not related to the manga and is not a directory
.filter { it.isDirectory && it.name == name } .filter { it.isDirectory && it.name == name }

View file

@ -0,0 +1,6 @@
package tachiyomi.source.local
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.UnmeteredSource
expect class LocalSource : CatalogueSource, UnmeteredSource

View file

@ -4,7 +4,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
interface LocalCoverManager { expect class LocalCoverManager {
fun find(mangaUrl: String): File? fun find(mangaUrl: String): File?

View file

@ -2,7 +2,7 @@ package tachiyomi.source.local.io
import java.io.File import java.io.File
interface LocalSourceFileSystem { expect class LocalSourceFileSystem {
fun getBaseDirectories(): Sequence<File> fun getBaseDirectories(): Sequence<File>