Skip to content

Commit

Permalink
[commonize] #496 Commonize IO and datetime part of :app:shared, ena…
Browse files Browse the repository at this point in the history
…ble ios target for some utils modules

New mpp libraries applied to `:app:shared`:
- kotlinx-datetime
- kotlinx-io

ios target is now enabled for these projects:
- ``:utils:platform`: for stdlib-level platform utils like `Uuid`, `currentTimeMillis`

Commonized modules:
- :utils:platform
- :utils:io
- :utils:coroutines

For app:
- Commonized almost all IO usages to use kotlinx-io
- Commonized all datetime usages
  • Loading branch information
Him188 committed Jul 27, 2024
1 parent b283ba9 commit 2fde710
Show file tree
Hide file tree
Showing 98 changed files with 1,083 additions and 383 deletions.
4 changes: 2 additions & 2 deletions app/android/src/main/kotlin/AndroidModules.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ import me.him188.ani.app.tools.update.AndroidUpdateInstaller
import me.him188.ani.app.tools.update.UpdateInstaller
import me.him188.ani.app.videoplayer.ExoPlayerStateFactory
import me.him188.ani.app.videoplayer.ui.state.PlayerStateFactory
import me.him188.ani.utils.io.SystemPath
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
import java.io.File

fun getAndroidModules(
torrentCacheDir: File,
torrentCacheDir: SystemPath,
coroutineScope: CoroutineScope,
) = module {
single<PermissionManager> {
Expand Down
4 changes: 3 additions & 1 deletion app/android/src/main/kotlin/AniApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import me.him188.ani.app.platform.createAppRootCoroutineScope
import me.him188.ani.app.platform.getCommonKoinModule
import me.him188.ani.app.platform.startCommonKoinModule
import me.him188.ani.app.tools.torrent.TorrentManager
import me.him188.ani.utils.io.inSystem
import me.him188.ani.utils.io.toKtPath
import org.koin.android.ext.android.getKoin
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin
Expand Down Expand Up @@ -126,7 +128,7 @@ class AniApplication : Application() {
startKoin {
androidContext(this@AniApplication)
modules(getCommonKoinModule({ this@AniApplication }, scope))
modules(getAndroidModules(torrentCaches, scope))
modules(getAndroidModules(torrentCaches.toKtPath().inSystem, scope))
}.startCommonKoinModule(scope)

getKoin().get<TorrentManager>() // start sharing, connect to DHT now
Expand Down
10 changes: 7 additions & 3 deletions app/desktop/src/main/kotlin/AniDesktop.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.io.files.Path
import me.him188.ani.app.data.repository.SettingsRepository
import me.him188.ani.app.data.source.UpdateManager
import me.him188.ani.app.data.source.media.resolver.DesktopWebVideoSourceResolver
Expand Down Expand Up @@ -95,6 +96,9 @@ import me.him188.ani.app.videoplayer.ui.VlcjVideoPlayerState
import me.him188.ani.app.videoplayer.ui.state.PlayerStateFactory
import me.him188.ani.desktop.generated.resources.Res
import me.him188.ani.desktop.generated.resources.a_round
import me.him188.ani.utils.io.inSystem
import me.him188.ani.utils.io.resolve
import me.him188.ani.utils.io.toKtPath
import me.him188.ani.utils.logging.error
import me.him188.ani.utils.logging.info
import me.him188.ani.utils.logging.logger
Expand Down Expand Up @@ -199,10 +203,10 @@ object AniDesktop {
saveDir = {
val saveDir = runBlocking {
get<SettingsRepository>().mediaCacheSettings.flow.first().saveDir
?.let(::File)
} ?: projectDirectories.torrentCacheDir
?.let(::Path)
} ?: projectDirectories.torrentCacheDir.toKtPath()
toplevelLogger.info { "TorrentManager saveDir: $saveDir" }
saveDir.resolve(it.id)
saveDir.inSystem.resolve(it.id)
},
)
}
Expand Down
3 changes: 3 additions & 0 deletions app/shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ composeCompiler {
enableStrongSkippingMode = true
}

atomicfu {
transformJvm = false // 这东西很不靠谱, 等 atomicfu 正式版了可能可以考虑下
}

kotlin {
sourceSets.commonMain.dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import androidx.datastore.dataStoreFile
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore
import me.him188.ani.app.platform.Context
import java.io.File
import me.him188.ani.utils.io.SystemPath
import me.him188.ani.utils.io.inSystem
import me.him188.ani.utils.io.toKtPath

actual val Context.preferencesStore: DataStore<Preferences> by preferencesDataStore("preferences")
actual val Context.tokenStore: DataStore<Preferences> by preferencesDataStore("tokens")
Expand All @@ -33,7 +35,7 @@ actual val Context.dataStoresImpl: PlatformDataStoreManager
internal class PlatformDataStoreManagerAndroid(
private val context: Context,
) : PlatformDataStoreManager() {
override fun resolveDataStoreFile(name: String): File {
return context.applicationContext.dataStoreFile(name)
override fun resolveDataStoreFile(name: String): SystemPath {
return context.applicationContext.dataStoreFile(name).toKtPath().inSystem
}
}
9 changes: 7 additions & 2 deletions app/shared/src/androidMain/kotlin/platform/Context.android.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ import androidx.compose.ui.platform.LocalConfiguration
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import me.him188.ani.app.platform.window.PlatformWindowMP
import me.him188.ani.utils.io.SystemPath
import me.him188.ani.utils.io.inSystem
import me.him188.ani.utils.io.toKtPath
import java.io.File


Expand Down Expand Up @@ -115,8 +118,10 @@ actual fun Context.setRequestFullScreen(window: PlatformWindowMP, fullscreen: Bo

internal actual val Context.filesImpl: ContextFiles
get() = object : ContextFiles {
override val cacheDir: File get() = this@filesImpl.cacheDir ?: File("") // can be null when previewing
override val dataDir: File get() = this@filesImpl.filesDir ?: File("") // can be null when previewing
override val cacheDir: SystemPath
get() = (this@filesImpl.cacheDir ?: File("")).toKtPath().inSystem // can be null when previewing
override val dataDir: SystemPath
get() = (this@filesImpl.filesDir ?: File("")).toKtPath().inSystem // can be null when previewing
}

// TODO: isSystemInFullscreen is written by ChatGPT, not tested
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import androidx.core.content.ContextCompat.startActivity
import androidx.core.content.FileProvider
import me.him188.ani.BuildConfig
import me.him188.ani.app.platform.ContextMP
import me.him188.ani.utils.io.SystemPath
import me.him188.ani.utils.io.toFile
import me.him188.ani.utils.logging.info
import me.him188.ani.utils.logging.logger
import me.him188.ani.utils.logging.warn
Expand All @@ -21,7 +23,7 @@ class AndroidUpdateInstaller : UpdateInstaller {
private val logger = logger<AndroidUpdateInstaller>()
}

override fun install(file: File, context: ContextMP): InstallationResult {
override fun install(file: SystemPath, context: ContextMP): InstallationResult {
logger.info { "Requesting install APK" }
if (!context.packageManager.canRequestPackageInstalls()) {
// Request permission from the user
Expand All @@ -34,7 +36,7 @@ class AndroidUpdateInstaller : UpdateInstaller {
}
} else {
kotlin.runCatching {
installApk(context, file)
installApk(context, file.toFile())
}.onFailure {
logger.warn(it) { "Failed to install update APK using installApkLegacy" }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import io.ktor.client.plugins.ClientRequestException
import io.ktor.client.plugins.ServerResponseException
import io.ktor.http.HttpStatusCode
import io.ktor.utils.io.errors.IOException
import kotlinx.coroutines.CancellationException
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.coroutines.cancellation.CancellationException

sealed interface ApiFailure {
data object Unauthorized : ApiFailure
Expand Down
39 changes: 21 additions & 18 deletions app/shared/src/commonMain/kotlin/data/models/PackedDate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ package me.him188.ani.app.data.models

import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import kotlinx.datetime.Clock
import kotlinx.datetime.LocalDate
import kotlinx.datetime.TimeZone
import kotlinx.datetime.atStartOfDayIn
import kotlinx.datetime.toLocalDateTime
import kotlinx.serialization.Serializable
import me.him188.ani.app.data.models.PackedDate.Companion.Invalid
import java.util.Calendar
import java.util.TimeZone
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.time.Duration
Expand Down Expand Up @@ -68,14 +71,15 @@ value class PackedDate @PublishedApi internal constructor(
)
}

private val UTC8 = TimeZone.of("UTC+8")

fun now(): PackedDate {
val currentTimeMillis = System.currentTimeMillis()
val timeZone = TimeZone.getTimeZone("UTC+8") // bangumi 是固定 UTC+8
val calendar = Calendar.getInstance(timeZone).apply { timeInMillis = currentTimeMillis }
val timeZone = UTC8 // bangumi 是固定 UTC+8
val calendar = Clock.System.now().toLocalDateTime(timeZone)

val year = calendar.get(Calendar.YEAR)
val month = calendar.get(Calendar.MONTH) + 1 // Calendar.MONTH is zero-based
val day = calendar.get(Calendar.DAY_OF_MONTH)
val year = calendar.year
val month = calendar.monthNumber
val day = calendar.dayOfMonth
return PackedDate(year, month, day)
}
}
Expand Down Expand Up @@ -125,15 +129,14 @@ inline val PackedDate.seasonMonth: Int
*/
operator fun PackedDate.minus(other: PackedDate): Duration {
if (this.isInvalid || other.isInvalid) return Duration.INFINITE
val thisCalendar = Calendar.getInstance().apply {
clear()
set(year, month - 1, day)
}
val otherCalendar = Calendar.getInstance().apply {
clear()
set(other.year, other.month - 1, other.day)
}
return (thisCalendar.timeInMillis - otherCalendar.timeInMillis).milliseconds

val thisDate = LocalDate(this.year, this.month, this.day)
val otherDate = LocalDate(other.year, other.month, other.day)

val thisInstant = thisDate.atStartOfDayIn(TimeZone.UTC)
val otherInstant = otherDate.atStartOfDayIn(TimeZone.UTC)

return (thisInstant.toEpochMilliseconds() - otherInstant.toEpochMilliseconds()).milliseconds
}

@Stable
Expand All @@ -144,7 +147,7 @@ inline fun PackedDate(
): PackedDate = if (year in 0..9999 && month in 1..12 && day in 1..31) {
PackedDate(DatePacker.pack(year, month, day))
} else {
PackedDate.Invalid // invalid
Invalid // invalid
}

@Suppress("NOTHING_TO_INLINE")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import me.him188.ani.datasources.bangumi.processing.toEpisodeCollectionType
import me.him188.ani.datasources.bangumi.processing.toSubjectCollectionType
import me.him188.ani.utils.coroutines.flows.runOrEmitEmptyList
import me.him188.ani.utils.coroutines.runUntilSuccess
import me.him188.ani.utils.io.toFile
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject

Expand Down Expand Up @@ -265,7 +266,7 @@ class SubjectManagerImpl(
ReplaceFileCorruptionHandler { LazyDataCacheSave.empty() },
migrations = listOf(),
produceFile = {
context.dataStores.resolveDataStoreFile("collectionsByType-${type.name}")
context.dataStores.resolveDataStoreFile("collectionsByType-${type.name}").toFile()
},
),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ import kotlinx.serialization.json.encodeToStream
import me.him188.ani.app.data.repository.MediaSourceSaves
import me.him188.ani.app.data.repository.MikanIndexes
import me.him188.ani.app.platform.Context
import java.io.File
import me.him188.ani.utils.io.SystemPath
import me.him188.ani.utils.io.toFile
import java.io.InputStream
import java.io.OutputStream

Expand All @@ -53,7 +54,7 @@ abstract class PlatformDataStoreManager {
val mikanIndexStore: DataStore<MikanIndexes>
get() = DataStoreFactory.create(
serializer = MikanIndexes.serializer().asDataStoreSerializer({ MikanIndexes.Empty }),
produceFile = { resolveDataStoreFile("mikanIndexes") },
produceFile = { resolveDataStoreFile("mikanIndexes").toFile() },
corruptionHandler = ReplaceFileCorruptionHandler {
MikanIndexes.Empty
},
Expand All @@ -63,14 +64,14 @@ abstract class PlatformDataStoreManager {
DataStoreFactory.create(
serializer = MediaSourceSaves.serializer()
.asDataStoreSerializer({ MediaSourceSaves.Default }),
produceFile = { resolveDataStoreFile("mediaSourceSaves") },
produceFile = { resolveDataStoreFile("mediaSourceSaves").toFile() },
corruptionHandler = ReplaceFileCorruptionHandler {
MediaSourceSaves.Default
},
)
}

abstract fun resolveDataStoreFile(name: String): File
abstract fun resolveDataStoreFile(name: String): SystemPath
}

fun <T> KSerializer<T>.asDataStoreSerializer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import me.him188.ani.datasources.mxdongman.MxdongmanMediaSource
import me.him188.ani.datasources.ntdm.GugufanMediaSource
import me.him188.ani.datasources.ntdm.NtdmMediaSource
import me.him188.ani.datasources.nyafun.NyafunMediaSource
import java.util.UUID
import me.him188.ani.utils.platform.Uuid

interface MediaSourceInstanceRepository : Repository {
val flow: Flow<List<MediaSourceSave>>
Expand All @@ -39,7 +39,7 @@ data class MediaSourceSaves(
val Empty = MediaSourceSaves(emptyList())
val Default: MediaSourceSaves by lazy {
fun createSave(it: String, isEnabled: Boolean) = MediaSourceSave(
instanceId = UUID.randomUUID().toString(),
instanceId = Uuid.randomString(),
mediaSourceId = it,
isEnabled = isEnabled,
config = MediaSourceConfig.Default,
Expand Down
18 changes: 12 additions & 6 deletions app/shared/src/commonMain/kotlin/data/source/UpdateManager.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package me.him188.ani.app.data.source

import me.him188.ani.app.platform.currentAniBuildConfig
import me.him188.ani.utils.io.SystemPath
import me.him188.ani.utils.io.delete
import me.him188.ani.utils.io.exists
import me.him188.ani.utils.io.inSystem
import me.him188.ani.utils.io.list
import me.him188.ani.utils.io.name
import me.him188.ani.utils.io.resolveSibling
import me.him188.ani.utils.logging.info
import me.him188.ani.utils.logging.logger
import org.koin.core.component.KoinComponent
import java.io.File

class UpdateManager(
val saveDir: File,
val saveDir: SystemPath,
) : KoinComponent {
companion object {
private val logger = logger<UpdateManager>()
Expand All @@ -16,24 +22,24 @@ class UpdateManager(
/**
* 如果此版本与 [file] 版本相同, 则删除 [file]
*/
fun deleteInstalled(file: File, currentVersion: String) {
fun deleteInstalled(file: SystemPath, currentVersion: String) {
if (file.name.contains(currentVersion)) {
file.delete()
}
}

fun deleteInstaller(file: File) {
fun deleteInstaller(file: SystemPath) {
file.delete()
file.resolveSibling(file.name + ".sha256").delete()
}

fun deleteInstalledFiles() {
if (!saveDir.exists()) return
saveDir.listFiles()?.forEach {
saveDir.list().forEach {
val version = currentAniBuildConfig.versionName
if (it.name.contains(version)) {
logger.info { "Deleting old installer file because it matches current version ${version}: $it" }
deleteInstaller(it)
deleteInstaller(it.inSystem)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package me.him188.ani.app.data.source.media

import androidx.compose.runtime.Stable
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -35,7 +36,6 @@ import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.atomic.AtomicInteger

abstract class MediaCacheManager(
val storagesIncludingDisabled: List<MediaCacheStorage>,
Expand Down Expand Up @@ -190,7 +190,7 @@ abstract class MediaCacheManager(
priority = NotifPriority.MIN
}

val visibleCount = AtomicInteger()
val visibleCount = atomic(0)
for (storage in list) {
launch {
if (System.currentTimeMillis() - startTime < 5000) {
Expand Down Expand Up @@ -260,7 +260,7 @@ abstract class MediaCacheManager(
// }.sampleWithInitial(1000)
combine(stats.downloadRate.sampleWithInitial(3000)) { downloadRate ->
// if (anyCaching) {
if (visibleCount.get() == 0) {
if (visibleCount.value == 0) {
summaryNotif.cancel()
} else {
summaryNotif.run {
Expand Down
Loading

0 comments on commit 2fde710

Please sign in to comment.