From 5da97344937e57e21dd64d06331d20236ae1cdf2 Mon Sep 17 00:00:00 2001 From: nonproto <2092019+nonproto@users.noreply.github.com> Date: Tue, 6 Feb 2024 13:24:36 -0500 Subject: [PATCH] Switch to StorageManager class --- .../java/eu/kanade/tachiyomi/AppModule.kt | 3 + .../tachiyomi/data/backup/BackupCreator.kt | 8 +- .../tachiyomi/data/backup/BackupCreatorJob.kt | 12 +-- .../tachiyomi/data/download/DownloadCache.kt | 28 ++----- .../data/download/DownloadProvider.kt | 12 +-- .../ui/manga/MangaDetailPresenter.kt | 10 +-- .../tachiyomi/ui/reader/ReaderViewModel.kt | 14 +--- .../ui/setting/SettingsDataController.kt | 16 +++- .../domain/storage/StorageManager.kt | 84 +++++++++++++++++++ .../nekomanga/logging/DebugReportingTree.kt | 2 +- 10 files changed, 124 insertions(+), 65 deletions(-) create mode 100644 app/src/main/java/org/nekomanga/domain/storage/StorageManager.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt b/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt index 6f3bc45300..1e114bd2b9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt @@ -48,6 +48,7 @@ import org.nekomanga.domain.backup.BackupPreferences import org.nekomanga.domain.details.MangaDetailsPreferences import org.nekomanga.domain.library.LibraryPreferences import org.nekomanga.domain.reader.ReaderPreferences +import org.nekomanga.domain.storage.StorageManager import org.nekomanga.domain.storage.StoragePreferences import tachiyomi.core.preference.AndroidPreferenceStore import tachiyomi.core.preference.PreferenceStore @@ -146,6 +147,8 @@ class AppModule(val app: Application) : InjektModule { addSingletonFactory { MangaShortcutManager() } + addSingletonFactory { StorageManager(app, get()) } + // Asynchronously init expensive components for a faster cold start ContextCompat.getMainExecutor(app).execute { get() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreator.kt index f477ed4dd7..c566343cfb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreator.kt @@ -31,7 +31,7 @@ import okio.buffer import okio.gzip import okio.sink import org.nekomanga.R -import org.nekomanga.domain.storage.StoragePreferences +import org.nekomanga.domain.storage.StorageManager import org.nekomanga.logging.TimberKt import uy.kohesive.injekt.injectLazy @@ -40,6 +40,7 @@ class BackupCreator(val context: Context) { internal val databaseHelper: DatabaseHelper by injectLazy() internal val sourceManager: SourceManager by injectLazy() internal val trackManager: TrackManager by injectLazy() + internal val storageManager: StorageManager by injectLazy() private val MAX_AUTO_BACKUPS: Int = 6 @@ -75,10 +76,7 @@ class BackupCreator(val context: Context) { file = (if (isAutoBackup) { // Get dir of file and create - val dir = - UniFile.fromUri(context, uri)!!.createDirectory( - StoragePreferences.AUTOMATIC_DIR - )!! + val dir = storageManager.getAutomaticBackupsDirectory()!! // Delete older backups dir.listFiles { _, filename -> Backup.filenameRegex.matches(filename) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreatorJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreatorJob.kt index bafdc368d3..5d83d7e2f7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreatorJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreatorJob.kt @@ -17,7 +17,7 @@ import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.util.system.notificationManager import java.util.concurrent.TimeUnit -import org.nekomanga.domain.storage.StoragePreferences +import org.nekomanga.domain.storage.StorageManager import org.nekomanga.logging.TimberKt import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -49,14 +49,8 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet } private fun getAutomaticBackupLocation(): Uri { - val storagePreferences = Injekt.get() - return storagePreferences.baseStorageDirectory().get().let { - val dir = - UniFile.fromUri(context, it.toUri())!!.createDirectory( - StoragePreferences.BACKUP_DIR - )!! - dir.uri - } + val storageManager = Injekt.get() + return storageManager.getBackupDirectory()!!.uri } companion object { 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 6a8aedf91a..eb271db7bd 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 @@ -1,7 +1,6 @@ package eu.kanade.tachiyomi.data.download import android.content.Context -import androidx.core.net.toUri import androidx.core.text.isDigitsOnly import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.data.database.DatabaseHelper @@ -18,10 +17,10 @@ import java.util.concurrent.TimeUnit import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import org.nekomanga.domain.storage.StoragePreferences +import org.nekomanga.domain.storage.StorageManager +import org.nekomanga.logging.TimberKt import tachiyomi.core.util.storage.DiskUtil import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -43,7 +42,7 @@ class DownloadCache( private val provider: DownloadProvider, private val sourceManager: SourceManager, private val preferences: PreferencesHelper = Injekt.get(), - private val storagePreferences: StoragePreferences = Injekt.get(), + private val storageManager: StorageManager = Injekt.get(), ) { /** @@ -61,24 +60,11 @@ class DownloadCache( val scope = CoroutineScope(Job() + Dispatchers.IO) init { - - storagePreferences - .baseStorageDirectory() - .changes() - .drop(1) - .onEach { lastRenew = 0L } // invalidate cache + storageManager.baseDirChanges + .onEach { forceRenewCache() } // invalidate cache .launchIn(scope) } - /** Returns the downloads directory from the user's preferences. */ - private fun getDirectoryFromPreference(): UniFile { - return storagePreferences.baseStorageDirectory().get().let { - UniFile.fromUri(context, it.toUri())!!.also { uniFile -> - uniFile.createDirectory(StoragePreferences.DOWNLOADS_DIR) - } - } - } - /** * Returns true if the chapter is downloaded. * @@ -176,10 +162,12 @@ class DownloadCache( /** Renews the downloads cache. */ private fun renew() { + TimberKt.d { "Renewing cache" } val onlineSources = listOf(sourceManager.mangaDex) val sourceDirs = - getDirectoryFromPreference() + storageManager + .getDownloadsDirectory()!! .listFiles() .orEmpty() .associate { it.name to SourceDirectory(it) } 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 d47e615389..96b13df555 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 @@ -1,7 +1,6 @@ package eu.kanade.tachiyomi.data.download import android.content.Context -import androidx.core.net.toUri import androidx.core.text.isDigitsOnly import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.data.database.models.Chapter @@ -12,7 +11,7 @@ import eu.kanade.tachiyomi.source.model.isMergedChapter import eu.kanade.tachiyomi.source.online.merged.mangalife.MangaLife import eu.kanade.tachiyomi.util.lang.isUUID import org.nekomanga.R -import org.nekomanga.domain.storage.StoragePreferences +import org.nekomanga.domain.storage.StorageManager import org.nekomanga.logging.TimberKt import tachiyomi.core.util.storage.DiskUtil import tachiyomi.core.util.storage.displayablePath @@ -28,7 +27,7 @@ import uy.kohesive.injekt.api.get */ class DownloadProvider( private val context: Context, - private val storagePreferences: StoragePreferences = Injekt.get() + private val storageManager: StorageManager = Injekt.get() ) { /** Preferences helper. */ @@ -36,12 +35,7 @@ class DownloadProvider( /** The root directory for downloads. */ private val downloadsDir: UniFile? - get() = - storagePreferences.baseStorageDirectory().get().let { - UniFile.fromUri(context, it.toUri()) - ?.createDirectory(StoragePreferences.DOWNLOADS_DIR) - ?.also { dir -> DiskUtil.createNoMediaFile(dir, context) } - } + get() = storageManager.getDownloadsDirectory() /** * Returns the download directory for a manga. For internal use only. diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailPresenter.kt index 83c0cc160d..77fde48d25 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailPresenter.kt @@ -91,7 +91,7 @@ import org.nekomanga.domain.manga.Artwork import org.nekomanga.domain.manga.Stats import org.nekomanga.domain.network.message import org.nekomanga.domain.snackbar.SnackbarState -import org.nekomanga.domain.storage.StoragePreferences +import org.nekomanga.domain.storage.StorageManager import org.nekomanga.domain.track.TrackServiceItem import org.nekomanga.domain.track.toDbTrack import org.nekomanga.domain.track.toTrackItem @@ -115,7 +115,7 @@ class MangaDetailPresenter( private val trackManager: TrackManager = Injekt.get(), private val mangaUpdateCoordinator: MangaUpdateCoordinator = Injekt.get(), private val trackingCoordinator: TrackingCoordinator = Injekt.get(), - private val storagePreferences: StoragePreferences = Injekt.get(), + private val storageManager: StorageManager = Injekt.get(), ) : BaseCoroutinePresenter(), DownloadQueue.DownloadListener { private val _currentManga = MutableStateFlow(null) @@ -527,11 +527,7 @@ class MangaDetailPresenter( fun saveCover(artwork: Artwork, destDir: UniFile? = null) { presenterScope.launchIO { try { - val directory = - destDir - ?: storagePreferences - .baseStorageDirectoryAsUniFile() - .createDirectory(StoragePreferences.COVER_DIR)!! + val directory = destDir ?: storageManager.getCoverDirectory()!! val destinationUri = saveCover(directory, artwork) launchUI { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt index 77075cd95e..b95445fe03 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt @@ -76,7 +76,7 @@ import org.nekomanga.domain.chapter.ChapterItem as DomainChapterItem import org.nekomanga.domain.chapter.toSimpleChapter import org.nekomanga.domain.network.message import org.nekomanga.domain.reader.ReaderPreferences -import org.nekomanga.domain.storage.StoragePreferences +import org.nekomanga.domain.storage.StorageManager import org.nekomanga.logging.TimberKt import tachiyomi.core.util.storage.DiskUtil import uy.kohesive.injekt.Injekt @@ -95,7 +95,7 @@ class ReaderViewModel( private val securityPreferences: SecurityPreferences = Injekt.get(), private val chapterFilter: ChapterFilter = Injekt.get(), private val chapterItemFilter: ChapterItemFilter = Injekt.get(), - private val storagePreferences: StoragePreferences = Injekt.get(), + private val storageManager: StorageManager = Injekt.get(), ) : ViewModel() { private val mutableState = MutableStateFlow(State()) @@ -827,10 +827,7 @@ class ReaderViewModel( val notifier = SaveImageNotifier(context) notifier.onClear() - var directory = - storagePreferences - .baseStorageDirectoryAsUniFile() - .createDirectory(StoragePreferences.PAGES_DIR)!! + var directory = storageManager.getPagesDirectory()!! if (preferences.folderPerManga().get()) { directory = directory.createDirectory(DiskUtil.buildValidFilename(manga.title))!! @@ -867,10 +864,7 @@ class ReaderViewModel( val notifier = SaveImageNotifier(context) notifier.onClear() - var directory = - storagePreferences - .baseStorageDirectoryAsUniFile() - .createDirectory(StoragePreferences.PAGES_DIR)!! + var directory = storageManager.getPagesDirectory()!! if (preferences.folderPerManga().get()) { directory = directory.createDirectory(DiskUtil.buildValidFilename(manga.title))!! diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDataController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDataController.kt index 37e95077f6..02763df78a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDataController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDataController.kt @@ -6,6 +6,7 @@ import android.content.ActivityNotFoundException import android.content.Intent import android.net.Uri import android.os.Bundle +import android.provider.DocumentsContract import android.view.View import android.widget.Toast import androidx.appcompat.app.AlertDialog @@ -27,12 +28,14 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.nekomanga.R import org.nekomanga.domain.backup.BackupPreferences +import org.nekomanga.domain.storage.StorageManager import org.nekomanga.domain.storage.StoragePreferences import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get class SettingsDataController : SettingsController() { private val storagePreferences: StoragePreferences = Injekt.get() + private val storageManager: StorageManager = Injekt.get() private val backupPreferences: BackupPreferences = Injekt.get() /** Flags containing information of what to backup. */ @@ -183,10 +186,15 @@ class SettingsDataController : SettingsController() { try { // Use Android's built-in file creator val intent = - Intent(Intent.ACTION_CREATE_DOCUMENT) - .addCategory(Intent.CATEGORY_OPENABLE) - .setType("application/*") - .putExtra(Intent.EXTRA_TITLE, Backup.getBackupFilename()) + Intent(Intent.ACTION_CREATE_DOCUMENT).apply { + addCategory(Intent.CATEGORY_OPENABLE) + setDataAndType(storageManager.getBackupDirectory()!!.uri, "application/*") + putExtra(Intent.EXTRA_TITLE, Backup.getBackupFilename()) + putExtra( + DocumentsContract.EXTRA_INITIAL_URI, + storageManager.getBackupDirectory()!!.uri + ) + } startActivityForResult(intent, CODE_BACKUP_CREATE) } catch (e: ActivityNotFoundException) { diff --git a/app/src/main/java/org/nekomanga/domain/storage/StorageManager.kt b/app/src/main/java/org/nekomanga/domain/storage/StorageManager.kt new file mode 100644 index 0000000000..bb79f3eafa --- /dev/null +++ b/app/src/main/java/org/nekomanga/domain/storage/StorageManager.kt @@ -0,0 +1,84 @@ +package org.nekomanga.domain.storage + +import android.content.Context +import androidx.core.net.toUri +import com.hippo.unifile.UniFile +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 +import tachiyomi.core.util.storage.DiskUtil + +class StorageManager(private val context: Context, storagePreferences: StoragePreferences) { + + private val BACKUP_DIR = "backup" + private val AUTOMATIC_DIR = "automatic" + private val COVER_DIR = "covers" + private val PAGES_DIR = "pages" + private val SAVED_DIR = "saved" + private val DOWNLOADS_DIR = "downloads" + + private val scope = CoroutineScope(Dispatchers.IO) + + private var baseDir: UniFile? = getBaseDir(storagePreferences.baseStorageDirectory().get()) + + private val _changes: Channel = Channel(Channel.UNLIMITED) + val baseDirChanges = _changes.receiveAsFlow().shareIn(scope, SharingStarted.Lazily, 1) + + init { + storagePreferences + .baseStorageDirectory() + .changes() + .drop(1) + .distinctUntilChanged() + .onEach { uri -> + baseDir = getBaseDir(uri) + baseDir?.let { parent -> + parent.createDirectory(BACKUP_DIR).also { it!!.createDirectory(AUTOMATIC_DIR) } + parent.createDirectory(SAVED_DIR).also { + it!!.createDirectory(COVER_DIR) + it.createDirectory(PAGES_DIR) + } + parent.createDirectory(DOWNLOADS_DIR).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 getAutomaticBackupsDirectory(): UniFile? { + return getBackupDirectory()?.createDirectory(AUTOMATIC_DIR) + } + + fun getSavedDir(): UniFile? { + return baseDir?.createDirectory(SAVED_DIR) + } + + fun getDownloadsDirectory(): UniFile? { + return baseDir?.createDirectory(DOWNLOADS_DIR) + } + + fun getBackupDirectory(): UniFile? { + return baseDir?.createDirectory(BACKUP_DIR) + } + + fun getCoverDirectory(): UniFile? { + return getSavedDir()?.createDirectory(COVER_DIR) + } + + fun getPagesDirectory(): UniFile? { + return getSavedDir()?.createDirectory(PAGES_DIR) + } +} diff --git a/core/src/main/kotlin/org/nekomanga/logging/DebugReportingTree.kt b/core/src/main/kotlin/org/nekomanga/logging/DebugReportingTree.kt index e29fcee81a..fcefed9f1f 100644 --- a/core/src/main/kotlin/org/nekomanga/logging/DebugReportingTree.kt +++ b/core/src/main/kotlin/org/nekomanga/logging/DebugReportingTree.kt @@ -4,6 +4,6 @@ import timber.log.Timber class DebugReportingTree : Timber.DebugTree() { override fun createStackElementTag(element: StackTraceElement): String { - return "(${element.fileName}:${element.lineNumber})#${element.methodName}" + return "(${element.fileName}.${element.methodName}" } }