From db996acfb622fc1d29bf82ed1a536ff2c7d04984 Mon Sep 17 00:00:00 2001 From: Ahmad Ansori Palembani Date: Sun, 26 May 2024 08:22:32 +0700 Subject: [PATCH] chore: Preparing for Unified Storage --- .../yokai/domain/storage/StorageManager.kt | 73 +++++++++++++++++++ .../domain/storage/StoragePreferences.kt | 12 +++ .../storage/AndroidStorageFolderProvider.kt | 23 ++++++ .../tachiyomi/core/storage/FolderProvider.kt | 10 +++ .../java/eu/kanade/tachiyomi/di/AppModule.kt | 5 ++ .../kanade/tachiyomi/di/PreferenceModule.kt | 9 +++ 6 files changed, 132 insertions(+) create mode 100644 app/src/main/java/dev/yokai/domain/storage/StorageManager.kt create mode 100644 app/src/main/java/dev/yokai/domain/storage/StoragePreferences.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/core/storage/AndroidStorageFolderProvider.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/core/storage/FolderProvider.kt diff --git a/app/src/main/java/dev/yokai/domain/storage/StorageManager.kt b/app/src/main/java/dev/yokai/domain/storage/StorageManager.kt new file mode 100644 index 0000000000..77a3c3153d --- /dev/null +++ b/app/src/main/java/dev/yokai/domain/storage/StorageManager.kt @@ -0,0 +1,73 @@ +package dev.yokai.domain.storage + +import android.content.Context +import androidx.core.net.toUri +import com.hippo.unifile.UniFile +import eu.kanade.tachiyomi.util.storage.DiskUtil +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.drop +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.flow.shareIn + +class StorageManager( + private val context: Context, + storagePreferences: StoragePreferences, +) { + private val scope = CoroutineScope(Dispatchers.IO) + + private var baseDir: UniFile? = getBaseDir(storagePreferences.baseStorageDirectory().get()) + + private val _changes: Channel = Channel(Channel.UNLIMITED) + val changes = _changes.receiveAsFlow() + .shareIn(scope, SharingStarted.Lazily, 1) + + init { + storagePreferences.baseStorageDirectory().changes() + .drop(1) + .distinctUntilChanged() + .onEach { uri -> + baseDir = getBaseDir(uri) + baseDir?.let { parent -> + parent.createDirectory(AUTOMATIC_BACKUPS_PATH) + parent.createDirectory(LOCAL_SOURCE_PATH) + parent.createDirectory(DOWNLOADS_PATH).also { + DiskUtil.createNoMediaFile(it, context) + } + } + _changes.send(Unit) + } + .launchIn(scope) + } + + private fun getBaseDir(uri: String): UniFile? { + return UniFile.fromUri(context, uri.toUri()) + .takeIf { it?.exists() == true } + } + + fun getBackupsDirectory(): UniFile? { + return baseDir?.createDirectory(BACKUPS_PATH) + } + + fun getAutomaticBackupsDirectory(): UniFile? { + return baseDir?.createDirectory(AUTOMATIC_BACKUPS_PATH) + } + + fun getDownloadsDirectory(): UniFile? { + return baseDir?.createDirectory(DOWNLOADS_PATH) + } + + fun getLocalSourceDirectory(): UniFile? { + return baseDir?.createDirectory(LOCAL_SOURCE_PATH) + } +} + +private const val BACKUPS_PATH = "autobackup" +private const val AUTOMATIC_BACKUPS_PATH = "autobackup" +private const val DOWNLOADS_PATH = "downloads" +private const val LOCAL_SOURCE_PATH = "local" diff --git a/app/src/main/java/dev/yokai/domain/storage/StoragePreferences.kt b/app/src/main/java/dev/yokai/domain/storage/StoragePreferences.kt new file mode 100644 index 0000000000..a8abb3fb69 --- /dev/null +++ b/app/src/main/java/dev/yokai/domain/storage/StoragePreferences.kt @@ -0,0 +1,12 @@ +package dev.yokai.domain.storage + +import eu.kanade.tachiyomi.core.preference.Preference +import eu.kanade.tachiyomi.core.preference.PreferenceStore +import eu.kanade.tachiyomi.core.storage.FolderProvider + +class StoragePreferences( + private val folderProvider: FolderProvider, + private val preferenceStore: PreferenceStore, +) { + fun baseStorageDirectory() = preferenceStore.getString(Preference.appStateKey("storage_dir"), folderProvider.path()) +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/core/storage/AndroidStorageFolderProvider.kt b/app/src/main/java/eu/kanade/tachiyomi/core/storage/AndroidStorageFolderProvider.kt new file mode 100644 index 0000000000..4fe299b16f --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/core/storage/AndroidStorageFolderProvider.kt @@ -0,0 +1,23 @@ +package eu.kanade.tachiyomi.core.storage + +import android.content.Context +import android.os.Environment +import androidx.core.net.toUri +import eu.kanade.tachiyomi.R +import java.io.File + +class AndroidStorageFolderProvider( + private val context: Context, +) : FolderProvider { + + override fun directory(): File { + return File( + Environment.getExternalStorageDirectory().absolutePath + File.separator + + context.getString(R.string.app_normalized_name), + ) + } + + override fun path(): String { + return directory().toUri().toString() + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/core/storage/FolderProvider.kt b/app/src/main/java/eu/kanade/tachiyomi/core/storage/FolderProvider.kt new file mode 100644 index 0000000000..7cef6948fd --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/core/storage/FolderProvider.kt @@ -0,0 +1,10 @@ +package eu.kanade.tachiyomi.core.storage + +import java.io.File + +interface FolderProvider { + + fun directory(): File + + fun path(): String +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt b/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt index c87aaa970a..d030b3e887 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt @@ -3,6 +3,8 @@ package eu.kanade.tachiyomi.di import android.app.Application import androidx.core.content.ContextCompat import dev.yokai.domain.extension.TrustExtension +import dev.yokai.domain.storage.StorageManager +import eu.kanade.tachiyomi.core.storage.AndroidStorageFolderProvider import eu.kanade.tachiyomi.data.cache.ChapterCache import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.database.DatabaseHelper @@ -59,6 +61,9 @@ class AppModule(val app: Application) : InjektModule { addSingletonFactory { TrustExtension() } + addSingletonFactory { AndroidStorageFolderProvider(app) } + addSingletonFactory { StorageManager(app, get()) } + // Asynchronously init expensive components for a faster cold start ContextCompat.getMainExecutor(app).execute { diff --git a/app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt b/app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt index 6280386d9b..4cdeb3d4ce 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt @@ -4,10 +4,12 @@ import android.app.Application import dev.yokai.domain.base.BasePreferences import dev.yokai.domain.recents.RecentsPreferences import dev.yokai.domain.source.SourcePreferences +import dev.yokai.domain.storage.StoragePreferences import dev.yokai.domain.ui.UiPreferences import dev.yokai.domain.ui.settings.ReaderPreferences import eu.kanade.tachiyomi.core.preference.AndroidPreferenceStore import eu.kanade.tachiyomi.core.preference.PreferenceStore +import eu.kanade.tachiyomi.core.storage.AndroidStorageFolderProvider import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.track.TrackPreferences import uy.kohesive.injekt.api.InjektModule @@ -37,5 +39,12 @@ class PreferenceModule(val application: Application) : InjektModule { preferenceStore = get(), ) } + + addSingletonFactory { + StoragePreferences( + folderProvider = get(), + preferenceStore = get(), + ) + } } }