mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	Convert source modules to Kotlin Multiplatform (#9172)
Use KMP in source modules Use KMP in source-api Expect LocalSource
This commit is contained in:
		@@ -48,9 +48,7 @@ import tachiyomi.data.Mangas
 | 
			
		||||
import tachiyomi.data.dateAdapter
 | 
			
		||||
import tachiyomi.data.listOfStringsAdapter
 | 
			
		||||
import tachiyomi.data.updateStrategyAdapter
 | 
			
		||||
import tachiyomi.source.local.image.AndroidLocalCoverManager
 | 
			
		||||
import tachiyomi.source.local.image.LocalCoverManager
 | 
			
		||||
import tachiyomi.source.local.io.AndroidLocalSourceFileSystem
 | 
			
		||||
import tachiyomi.source.local.io.LocalSourceFileSystem
 | 
			
		||||
import uy.kohesive.injekt.api.InjektModule
 | 
			
		||||
import uy.kohesive.injekt.api.InjektRegistrar
 | 
			
		||||
@@ -137,8 +135,8 @@ class AppModule(val app: Application) : InjektModule {
 | 
			
		||||
 | 
			
		||||
        addSingletonFactory { ImageSaver(app) }
 | 
			
		||||
 | 
			
		||||
        addSingletonFactory<LocalSourceFileSystem> { AndroidLocalSourceFileSystem(app) }
 | 
			
		||||
        addSingletonFactory<LocalCoverManager> { AndroidLocalCoverManager(app, get()) }
 | 
			
		||||
        addSingletonFactory { LocalSourceFileSystem(app) }
 | 
			
		||||
        addSingletonFactory { LocalCoverManager(app, get()) }
 | 
			
		||||
 | 
			
		||||
        // Asynchronously init expensive components for a faster cold start
 | 
			
		||||
        ContextCompat.getMainExecutor(app).execute {
 | 
			
		||||
 
 | 
			
		||||
@@ -22,3 +22,5 @@ org.gradle.caching=true
 | 
			
		||||
 | 
			
		||||
# AndroidX support
 | 
			
		||||
android.useAndroidX=true
 | 
			
		||||
 | 
			
		||||
kotlin.mpp.androidSourceSetLayoutVersion=2
 | 
			
		||||
@@ -1,7 +1,27 @@
 | 
			
		||||
plugins {
 | 
			
		||||
    id("com.android.library")
 | 
			
		||||
    kotlin("android")
 | 
			
		||||
    kotlin("multiplatform")
 | 
			
		||||
    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 {
 | 
			
		||||
@@ -11,13 +31,3 @@ android {
 | 
			
		||||
        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)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,3 @@
 | 
			
		||||
package eu.kanade.tachiyomi.source
 | 
			
		||||
 | 
			
		||||
actual typealias PreferenceScreen = androidx.preference.PreferenceScreen
 | 
			
		||||
@@ -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()
 | 
			
		||||
@@ -1,7 +1,5 @@
 | 
			
		||||
package eu.kanade.tachiyomi.source
 | 
			
		||||
 | 
			
		||||
import androidx.preference.PreferenceScreen
 | 
			
		||||
 | 
			
		||||
interface ConfigurableSource : Source {
 | 
			
		||||
 | 
			
		||||
    fun setupPreferenceScreen(screen: PreferenceScreen)
 | 
			
		||||
@@ -0,0 +1,3 @@
 | 
			
		||||
package eu.kanade.tachiyomi.source
 | 
			
		||||
 | 
			
		||||
expect class PreferenceScreen
 | 
			
		||||
@@ -3,8 +3,8 @@ package eu.kanade.tachiyomi.source
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.Page
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.SChapter
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.SManga
 | 
			
		||||
import eu.kanade.tachiyomi.util.awaitSingle
 | 
			
		||||
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...
 | 
			
		||||
@@ -0,0 +1,5 @@
 | 
			
		||||
package eu.kanade.tachiyomi.util
 | 
			
		||||
 | 
			
		||||
import rx.Observable
 | 
			
		||||
 | 
			
		||||
expect suspend fun <T> Observable<T>.awaitSingle(): T
 | 
			
		||||
@@ -1,29 +1,37 @@
 | 
			
		||||
plugins {
 | 
			
		||||
    kotlin("multiplatform")
 | 
			
		||||
    id("com.android.library")
 | 
			
		||||
    kotlin("android")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
kotlin {
 | 
			
		||||
    android()
 | 
			
		||||
    sourceSets {
 | 
			
		||||
        val commonMain by getting {
 | 
			
		||||
            dependencies {
 | 
			
		||||
                implementation(project(":source-api"))
 | 
			
		||||
                implementation(libs.unifile)
 | 
			
		||||
                implementation(libs.junrar)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        val androidMain by getting {
 | 
			
		||||
            dependencies {
 | 
			
		||||
                implementation(project(":core"))
 | 
			
		||||
                implementation(project(":core-metadata"))
 | 
			
		||||
 | 
			
		||||
                // Move ChapterRecognition to separate module?
 | 
			
		||||
                implementation(project(":domain"))
 | 
			
		||||
 | 
			
		||||
                implementation(kotlinx.bundles.serialization)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
android {
 | 
			
		||||
    namespace = "tachiyomi.source.local"
 | 
			
		||||
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
 | 
			
		||||
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
 | 
			
		||||
        consumerProguardFiles("consumer-rules.pro")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
 | 
			
		||||
    implementation(project(":source-api"))
 | 
			
		||||
    implementation(project(":core"))
 | 
			
		||||
    implementation(project(":core-metadata"))
 | 
			
		||||
 | 
			
		||||
    // Move ChapterRecognition to separate module?
 | 
			
		||||
    implementation(project(":domain"))
 | 
			
		||||
 | 
			
		||||
    implementation(kotlinx.bundles.serialization)
 | 
			
		||||
 | 
			
		||||
    implementation(libs.unifile)
 | 
			
		||||
    implementation(libs.junrar)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@ import java.util.concurrent.TimeUnit
 | 
			
		||||
import java.util.zip.ZipFile
 | 
			
		||||
import com.github.junrar.Archive as JunrarArchive
 | 
			
		||||
 | 
			
		||||
class LocalSource(
 | 
			
		||||
actual class LocalSource(
 | 
			
		||||
    private val context: Context,
 | 
			
		||||
    private val fileSystem: LocalSourceFileSystem,
 | 
			
		||||
    private val coverManager: LocalCoverManager,
 | 
			
		||||
@@ -11,12 +11,12 @@ import java.io.InputStream
 | 
			
		||||
 | 
			
		||||
private const val DEFAULT_COVER_NAME = "cover.jpg"
 | 
			
		||||
 | 
			
		||||
class AndroidLocalCoverManager(
 | 
			
		||||
actual class LocalCoverManager(
 | 
			
		||||
    private val context: Context,
 | 
			
		||||
    private val fileSystem: LocalSourceFileSystem,
 | 
			
		||||
) : LocalCoverManager {
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    override fun find(mangaUrl: String): File? {
 | 
			
		||||
    actual fun find(mangaUrl: String): File? {
 | 
			
		||||
        return fileSystem.getFilesInMangaDirectory(mangaUrl)
 | 
			
		||||
            // Get all file whose names start with 'cover'
 | 
			
		||||
            .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)
 | 
			
		||||
        if (directory == null) {
 | 
			
		||||
            inputStream.close()
 | 
			
		||||
@@ -5,31 +5,31 @@ import eu.kanade.tachiyomi.util.storage.DiskUtil
 | 
			
		||||
import tachiyomi.source.local.R
 | 
			
		||||
import java.io.File
 | 
			
		||||
 | 
			
		||||
class AndroidLocalSourceFileSystem(
 | 
			
		||||
actual class LocalSourceFileSystem(
 | 
			
		||||
    private val context: Context,
 | 
			
		||||
) : LocalSourceFileSystem {
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
            .map { File(it.absolutePath, baseFolderLocation) }
 | 
			
		||||
            .asSequence()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun getFilesInBaseDirectories(): Sequence<File> {
 | 
			
		||||
    actual fun getFilesInBaseDirectories(): Sequence<File> {
 | 
			
		||||
        return getBaseDirectories()
 | 
			
		||||
            // Get all the files inside all baseDir
 | 
			
		||||
            .flatMap { it.listFiles().orEmpty().toList() }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun getMangaDirectory(name: String): File? {
 | 
			
		||||
    actual fun getMangaDirectory(name: String): File? {
 | 
			
		||||
        return getFilesInBaseDirectories()
 | 
			
		||||
            // Get the first mangaDir or null
 | 
			
		||||
            .firstOrNull { it.isDirectory && it.name == name }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun getFilesInMangaDirectory(name: String): Sequence<File> {
 | 
			
		||||
    actual fun getFilesInMangaDirectory(name: String): Sequence<File> {
 | 
			
		||||
        return getFilesInBaseDirectories()
 | 
			
		||||
            // Filter out ones that are not related to the manga and is not a directory
 | 
			
		||||
            .filter { it.isDirectory && it.name == name }
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -4,7 +4,7 @@ import eu.kanade.tachiyomi.source.model.SManga
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.InputStream
 | 
			
		||||
 | 
			
		||||
interface LocalCoverManager {
 | 
			
		||||
expect class LocalCoverManager {
 | 
			
		||||
 | 
			
		||||
    fun find(mangaUrl: String): File?
 | 
			
		||||
 | 
			
		||||
@@ -2,7 +2,7 @@ package tachiyomi.source.local.io
 | 
			
		||||
 | 
			
		||||
import java.io.File
 | 
			
		||||
 | 
			
		||||
interface LocalSourceFileSystem {
 | 
			
		||||
expect class LocalSourceFileSystem {
 | 
			
		||||
 | 
			
		||||
    fun getBaseDirectories(): Sequence<File>
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user