diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt index cfcf1fe856..efeb9e4163 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt @@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.download import android.app.Application import android.content.Context import android.net.Uri -import androidx.core.net.toUri import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.source.Source @@ -19,6 +18,7 @@ import kotlinx.coroutines.ensureActive import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart @@ -64,7 +64,7 @@ class DownloadCache( private val provider: DownloadProvider = Injekt.get(), private val sourceManager: SourceManager = Injekt.get(), private val extensionManager: ExtensionManager = Injekt.get(), - private val storagePreferences: StoragePreferences = Injekt.get(), + storagePreferences: StoragePreferences = Injekt.get(), ) { private val scope = CoroutineScope(Dispatchers.IO) @@ -95,16 +95,9 @@ class DownloadCache( get() = File(context.cacheDir, "dl_index_cache") private val rootDownloadsDirLock = Mutex() - private var rootDownloadsDir = RootDirectory(getDirectoryFromPreference()) + private var rootDownloadsDir = RootDirectory(provider.downloadsDir) init { - storagePreferences.baseStorageDirectory().changes() - .onEach { - rootDownloadsDir = RootDirectory(getDirectoryFromPreference()) - invalidateCache() - } - .launchIn(scope) - // Attempt to read cache file scope.launch { rootDownloadsDirLock.withLock { @@ -119,6 +112,14 @@ class DownloadCache( } } } + + storagePreferences.baseStorageDirectory().changes() + .drop(1) + .onEach { + rootDownloadsDir = RootDirectory(provider.downloadsDir) + invalidateCache() + } + .launchIn(scope) } /** @@ -293,17 +294,6 @@ class DownloadCache( renewalJob?.cancel() } - /** - * Returns the downloads directory from the user's preferences. - */ - private fun getDirectoryFromPreference(): UniFile { - return storagePreferences.baseStorageDirectory().get().let { - UniFile.fromUri(context, it.toUri()).also { - it?.createDirectory(StoragePreferences.DOWNLOADS_DIR) - } - } - } - /** * Renews the downloads cache. */ @@ -335,7 +325,7 @@ class DownloadCache( val sourceMap = sources.associate { provider.getSourceDirName(it).lowercase() to it.id } rootDownloadsDirLock.withLock { - val sourceDirs = rootDownloadsDir.dir.listFiles().orEmpty() + val sourceDirs = rootDownloadsDir.dir?.listFiles().orEmpty() .filter { it.isDirectory && !it.name.isNullOrBlank() } .mapNotNull { dir -> val sourceId = sourceMap[dir.name!!.lowercase()] @@ -348,12 +338,12 @@ class DownloadCache( sourceDirs.values .map { sourceDir -> async { - sourceDir.mangaDirs = sourceDir.dir.listFiles().orEmpty() + sourceDir.mangaDirs = sourceDir.dir?.listFiles().orEmpty() .filter { it.isDirectory && !it.name.isNullOrBlank() } .associate { it.name!! to MangaDirectory(it) } sourceDir.mangaDirs.values.forEach { mangaDir -> - val chapterDirs = mangaDir.dir.listFiles().orEmpty() + val chapterDirs = mangaDir.dir?.listFiles().orEmpty() .mapNotNull { when { // Ignore incomplete downloads @@ -430,7 +420,7 @@ class DownloadCache( @Serializable private class RootDirectory( @Serializable(with = UniFileAsStringSerializer::class) - val dir: UniFile, + val dir: UniFile?, var sourceDirs: Map = mapOf(), ) @@ -440,7 +430,7 @@ private class RootDirectory( @Serializable private class SourceDirectory( @Serializable(with = UniFileAsStringSerializer::class) - val dir: UniFile, + val dir: UniFile?, var mangaDirs: Map = mapOf(), ) @@ -450,17 +440,26 @@ private class SourceDirectory( @Serializable private class MangaDirectory( @Serializable(with = UniFileAsStringSerializer::class) - val dir: UniFile, + val dir: UniFile?, var chapterDirs: MutableSet = mutableSetOf(), ) -private object UniFileAsStringSerializer : KSerializer { +private object UniFileAsStringSerializer : KSerializer { override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("UniFile", PrimitiveKind.STRING) - override fun serialize(encoder: Encoder, value: UniFile) { - return encoder.encodeString(value.uri.toString()) + override fun serialize(encoder: Encoder, value: UniFile?) { + return if (value == null) { + encoder.encodeNull() + } else { + encoder.encodeString(value.uri.toString()) + } } - override fun deserialize(decoder: Decoder): UniFile { - return UniFile.fromUri(Injekt.get(), Uri.parse(decoder.decodeString())) + + override fun deserialize(decoder: Decoder): UniFile? { + return if (decoder.decodeNotNullMark()) { + UniFile.fromUri(Injekt.get(), Uri.parse(decoder.decodeString())) + } else { + decoder.decodeNull() + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt index 2f40ab78c0..59ed6b2f88 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt @@ -5,6 +5,10 @@ import androidx.core.net.toUri import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.util.storage.DiskUtil +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import logcat.LogPriority import tachiyomi.core.i18n.stringResource import tachiyomi.core.util.system.logcat @@ -23,17 +27,27 @@ import uy.kohesive.injekt.api.get */ class DownloadProvider( private val context: Context, - private val storagePreferences: StoragePreferences = Injekt.get(), + storagePreferences: StoragePreferences = Injekt.get(), ) { - private val downloadsDir: UniFile? - get() = storagePreferences.baseStorageDirectory().get().let { - UniFile.fromUri(context, it.toUri()) - ?.createDirectory(StoragePreferences.DOWNLOADS_DIR) - ?.also { dir -> - DiskUtil.createNoMediaFile(dir, context) - } - } + private val scope = CoroutineScope(Dispatchers.IO) + + private var _downloadsDir: UniFile? = + storagePreferences.baseStorageDirectory().get().let(::getDownloadsLocation) + val downloadsDir: UniFile? + get() = _downloadsDir + + init { + storagePreferences.baseStorageDirectory().changes() + .onEach { _downloadsDir = getDownloadsLocation(it) } + .launchIn(scope) + } + + private fun getDownloadsLocation(dir: String): UniFile? { + return UniFile.fromUri(context, dir.toUri()) + ?.createDirectory(StoragePreferences.DOWNLOADS_DIR) + ?.also { DiskUtil.createNoMediaFile(it, context) } + } /** * Returns the download directory for a manga. For internal use only.