From 14eb114ee8b22ac04cd808999e94b1cbb2d8ff6f Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 19 Nov 2023 11:50:41 -0500 Subject: [PATCH] Misc cleanup - Rebased against master - Removed unnecessary tabs for now --- app/build.gradle.kts | 2 +- .../java/eu/kanade/domain/DomainModule.kt | 8 ++ .../more/download/DownloadStatsScreen.kt | 75 +++++++++++++ .../more/download/DownloadStatsScreenModel.kt | 101 ++++++++++++++++++ .../download/components/OverAllTabContent.kt | 85 +++++++++++++++ .../more/download/data/DownloadStatManga.kt | 11 ++ .../settings/screen/SettingsDataScreen.kt | 6 ++ .../data/download/DownloadManager.kt | 25 ++++- .../tachiyomi/data/download/Downloader.kt | 19 ++++ .../tachiyomi/data/stat/DownloadStatMapper.kt | 19 ++++ .../data/stat/DownloadStatRepositoryImpl.kt | 30 ++++++ .../tachiyomi/data/download_stat.sq | 18 ++++ .../sqldelight/tachiyomi/migrations/28.sqm | 10 ++ .../interactor/AddDownloadStatOperation.kt | 13 +++ .../interactor/GetDownloadStatOperations.kt | 18 ++++ .../stat/model/DownloadStatOperation.kt | 21 ++++ .../stat/repository/DownloadStatRepository.kt | 13 +++ .../commonMain/resources/MR/base/strings.xml | 6 ++ 18 files changed, 478 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreen.kt create mode 100644 app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreenModel.kt create mode 100644 app/src/main/java/eu/kanade/presentation/more/download/components/OverAllTabContent.kt create mode 100644 app/src/main/java/eu/kanade/presentation/more/download/data/DownloadStatManga.kt create mode 100644 data/src/main/java/tachiyomi/data/stat/DownloadStatMapper.kt create mode 100644 data/src/main/java/tachiyomi/data/stat/DownloadStatRepositoryImpl.kt create mode 100644 data/src/main/sqldelight/tachiyomi/data/download_stat.sq create mode 100644 data/src/main/sqldelight/tachiyomi/migrations/28.sqm create mode 100644 domain/src/main/java/tachiyomi/domain/stat/interactor/AddDownloadStatOperation.kt create mode 100644 domain/src/main/java/tachiyomi/domain/stat/interactor/GetDownloadStatOperations.kt create mode 100644 domain/src/main/java/tachiyomi/domain/stat/model/DownloadStatOperation.kt create mode 100644 domain/src/main/java/tachiyomi/domain/stat/repository/DownloadStatRepository.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1359315450..4bba73d1de 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,7 +22,7 @@ android { defaultConfig { applicationId = "eu.kanade.tachiyomi" - versionCode = 109 + versionCode = 110 versionName = "0.14.7" buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt index 778b7645c7..d067c3eded 100644 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt @@ -29,6 +29,7 @@ import tachiyomi.data.manga.MangaRepositoryImpl import tachiyomi.data.release.ReleaseServiceImpl import tachiyomi.data.source.SourceRepositoryImpl import tachiyomi.data.source.StubSourceRepositoryImpl +import tachiyomi.data.stat.DownloadStatRepositoryImpl import tachiyomi.data.track.TrackRepositoryImpl import tachiyomi.data.updates.UpdatesRepositoryImpl import tachiyomi.domain.category.interactor.CreateCategoryWithName @@ -72,6 +73,9 @@ import tachiyomi.domain.source.interactor.GetRemoteManga import tachiyomi.domain.source.interactor.GetSourcesWithNonLibraryManga import tachiyomi.domain.source.repository.SourceRepository import tachiyomi.domain.source.repository.StubSourceRepository +import tachiyomi.domain.stat.interactor.AddDownloadStatOperation +import tachiyomi.domain.stat.interactor.GetDownloadStatOperations +import tachiyomi.domain.stat.repository.DownloadStatRepository import tachiyomi.domain.track.interactor.DeleteTrack import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.domain.track.interactor.GetTracksPerManga @@ -167,5 +171,9 @@ class DomainModule : InjektModule { addFactory { ToggleLanguage(get()) } addFactory { ToggleSource(get()) } addFactory { ToggleSourcePin(get()) } + + addSingletonFactory { DownloadStatRepositoryImpl(get()) } + addFactory { GetDownloadStatOperations(get()) } + addFactory { AddDownloadStatOperation(get()) } } } diff --git a/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreen.kt b/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreen.kt new file mode 100644 index 0000000000..ab7c6e560b --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreen.kt @@ -0,0 +1,75 @@ +package eu.kanade.presentation.more.download + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import cafe.adriel.voyager.core.model.rememberScreenModel +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.presentation.components.AppBar +import eu.kanade.presentation.more.download.components.DeletedStatsRow +import eu.kanade.presentation.more.download.components.DownloadStatOverviewSection +import eu.kanade.presentation.more.download.components.DownloadStatsRow +import eu.kanade.presentation.util.Screen +import tachiyomi.i18n.MR +import tachiyomi.presentation.core.components.material.Scaffold +import tachiyomi.presentation.core.components.material.padding +import tachiyomi.presentation.core.i18n.stringResource +import tachiyomi.presentation.core.screens.LoadingScreen + +class DownloadStatsScreen : Screen() { + + @Composable + override fun Content() { + val screenModel = rememberScreenModel { DownloadStatsScreenModel() } + val state by screenModel.state.collectAsState() + val navigator = LocalNavigator.currentOrThrow + + Scaffold( + topBar = { scrollBehavior -> + AppBar( + title = stringResource(MR.strings.label_download_stats), + navigateUp = navigator::pop, + scrollBehavior = scrollBehavior, + ) + }, + ) { contentPadding -> + when { + state.isLoading -> LoadingScreen(Modifier.padding(contentPadding)) + else -> { + OverallStats( + state = state, + contentPadding = contentPadding, + ) + } + } + } + } +} + +@Composable +private fun OverallStats( + state: DownloadStatsScreenModel.State, + contentPadding: PaddingValues, +) { + LazyColumn( + contentPadding = contentPadding, + verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small), + ) { + item { + DownloadStatOverviewSection(state.uniqueItems) + } + item { + DownloadStatsRow(state.downloadStatOperations.filter { it.size > 0 }) + } + item { + DeletedStatsRow(state.downloadStatOperations.filter { it.size < 0 }) + } + } +} diff --git a/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreenModel.kt b/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreenModel.kt new file mode 100644 index 0000000000..e82f5fedda --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/more/download/DownloadStatsScreenModel.kt @@ -0,0 +1,101 @@ +package eu.kanade.presentation.more.download + +import androidx.compose.runtime.Immutable +import cafe.adriel.voyager.core.model.StateScreenModel +import cafe.adriel.voyager.core.model.screenModelScope +import eu.kanade.presentation.more.download.data.DownloadStatManga +import eu.kanade.tachiyomi.data.download.DownloadManager +import eu.kanade.tachiyomi.data.download.DownloadProvider +import eu.kanade.tachiyomi.util.storage.DiskUtil +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.update +import tachiyomi.core.util.lang.launchIO +import tachiyomi.domain.manga.interactor.GetLibraryManga +import tachiyomi.domain.source.service.SourceManager +import tachiyomi.domain.stat.interactor.GetDownloadStatOperations +import tachiyomi.domain.stat.model.DownloadStatOperation +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import java.io.File + +class DownloadStatsScreenModel( + private val getLibraryManga: GetLibraryManga = Injekt.get(), + private val sourceManager: SourceManager = Injekt.get(), + private val downloadManager: DownloadManager = Injekt.get(), + private val downloadProvider: DownloadProvider = Injekt.get(), + private val getDownloadStatOperations: GetDownloadStatOperations = Injekt.get(), +) : StateScreenModel(State()) { + + init { + screenModelScope.launchIO { + mutableState.update { state -> + state.copy( + items = getLibraryManga.await().map { libraryManga -> + val source = sourceManager.getOrStub(libraryManga.manga.source) + val path = downloadProvider.findMangaDir( + libraryManga.manga.title, + source, + )?.filePath + DownloadStatManga( + libraryManga = libraryManga, + folderSize = if (path != null) DiskUtil.getDirectorySize(File(path)) else 0, + downloadChaptersCount = downloadManager.getDownloadCount(libraryManga.manga), + ) + }, + downloadStatOperations = getDownloadStatOperations.await(), + isLoading = false, + ) + } + } + + screenModelScope.launchIO { + getDownloadStatOperations.subscribe() + .distinctUntilChanged() + .collectLatest { operations -> + mutableState.update { state -> + val oldOperationsId = state.downloadStatOperations.map { it.id }.toHashSet() + val newOperations = operations + .mapNotNull { if (!oldOperationsId.contains(it.id)) it else null } + .groupBy { it.mangaId } + val newItems = state.items.map { item -> + if (newOperations.containsKey(item.libraryManga.id)) { + item.copy( + folderSize = item.folderSize + + newOperations[item.libraryManga.id]!! + .sumOf { it.size }, + downloadChaptersCount = item.downloadChaptersCount + + newOperations[item.libraryManga.id]!!.sumOf { it.units }.toInt(), + ) + } else { + item + } + } + state.copy( + items = newItems, + downloadStatOperations = operations, + ) + } + } + } + } + + @Immutable + data class State( + val isLoading: Boolean = true, + val items: List = emptyList(), + val downloadStatOperations: List = emptyList(), + ) { + + val uniqueItems: List by lazy { + val uniqueIds = HashSet() + val uniqueMangas = mutableListOf() + for (manga in items) { + if (uniqueIds.add(manga.libraryManga.manga.id)) { + uniqueMangas.add(manga) + } + } + uniqueMangas + } + } +} diff --git a/app/src/main/java/eu/kanade/presentation/more/download/components/OverAllTabContent.kt b/app/src/main/java/eu/kanade/presentation/more/download/components/OverAllTabContent.kt new file mode 100644 index 0000000000..6b91ec5286 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/more/download/components/OverAllTabContent.kt @@ -0,0 +1,85 @@ +package eu.kanade.presentation.more.download.components + +import android.text.format.Formatter +import androidx.compose.foundation.layout.Row +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Book +import androidx.compose.material.icons.outlined.CollectionsBookmark +import androidx.compose.material.icons.outlined.Storage +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import eu.kanade.presentation.more.download.data.DownloadStatManga +import eu.kanade.presentation.more.stats.components.StatsItem +import eu.kanade.presentation.more.stats.components.StatsOverviewItem +import eu.kanade.presentation.more.stats.components.StatsSection +import tachiyomi.domain.stat.model.DownloadStatOperation +import tachiyomi.i18n.MR +import tachiyomi.presentation.core.i18n.stringResource +import kotlin.math.abs + +@Composable +fun DownloadStatOverviewSection( + items: List, +) { + StatsSection(MR.strings.label_overview_section) { + Row { + StatsOverviewItem( + title = folderSizeText(items.sumOf { it.folderSize }), + subtitle = stringResource(MR.strings.label_size), + icon = Icons.Outlined.Storage, + ) + StatsOverviewItem( + title = items.sumOf { it.downloadChaptersCount }.toString(), + subtitle = stringResource(MR.strings.chapters), + icon = Icons.Outlined.Book, + ) + StatsOverviewItem( + title = items.filter { it.downloadChaptersCount > 0 }.size.toString(), + subtitle = stringResource(MR.strings.manga), + icon = Icons.Outlined.CollectionsBookmark, + ) + } + } +} + +@Composable +fun DownloadStatsRow( + data: List, +) { + StatsSection(MR.strings.downloaded_chapters) { + Row { + StatsItem( + data.size.toString(), + stringResource(MR.strings.label_total_chapters), + ) + StatsItem( + folderSizeText(data.sumOf { abs(it.size) }), + stringResource(MR.strings.label_size), + ) + } + } +} + +@Composable +fun DeletedStatsRow( + data: List, +) { + StatsSection(MR.strings.deleted_chapters) { + Row { + StatsItem( + abs(data.sumOf { it.units }).toString(), + stringResource(MR.strings.label_total_chapters), + ) + StatsItem( + folderSizeText(abs(data.sumOf { it.size })), + stringResource(MR.strings.label_size), + ) + } + } +} + +@Composable +private fun folderSizeText(folderSize: Long): String { + val context = LocalContext.current + return Formatter.formatFileSize(context, folderSize) +} diff --git a/app/src/main/java/eu/kanade/presentation/more/download/data/DownloadStatManga.kt b/app/src/main/java/eu/kanade/presentation/more/download/data/DownloadStatManga.kt new file mode 100644 index 0000000000..da1d5627c0 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/more/download/data/DownloadStatManga.kt @@ -0,0 +1,11 @@ +package eu.kanade.presentation.more.download.data + +import androidx.compose.runtime.Immutable +import tachiyomi.domain.library.model.LibraryManga + +@Immutable +data class DownloadStatManga( + val libraryManga: LibraryManga, + val folderSize: Long = 0, + val downloadChaptersCount: Int = 0, +) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt index f2c2c05563..74a766334d 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.presentation.more.download.DownloadStatsScreen import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget @@ -255,6 +256,7 @@ object SettingsDataScreen : SearchableSettings { val scope = rememberCoroutineScope() val context = LocalContext.current val libraryPreferences = remember { Injekt.get() } + val navigator = LocalNavigator.currentOrThrow val chapterCache = remember { Injekt.get() } var cacheReadableSizeSema by remember { mutableIntStateOf(0) } @@ -287,6 +289,10 @@ object SettingsDataScreen : SearchableSettings { pref = libraryPreferences.autoClearChapterCache(), title = stringResource(MR.strings.pref_auto_clear_chapter_cache), ), + Preference.PreferenceItem.TextPreference( + title = stringResource(MR.strings.label_download_stats), + onClick = { navigator.push(DownloadStatsScreen()) }, + ), ), ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt index 0b404165fc..8ca6ed0f31 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt @@ -4,6 +4,7 @@ import android.content.Context import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.util.storage.DiskUtil import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.drop @@ -22,9 +23,12 @@ import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.download.service.DownloadPreferences import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager +import tachiyomi.domain.stat.interactor.AddDownloadStatOperation +import tachiyomi.domain.stat.model.DownloadStatOperation import tachiyomi.i18n.MR import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import java.io.File /** * This class is used to manage chapter downloads in the application. It must be instantiated once @@ -38,6 +42,7 @@ class DownloadManager( private val getCategories: GetCategories = Injekt.get(), private val sourceManager: SourceManager = Injekt.get(), private val downloadPreferences: DownloadPreferences = Injekt.get(), + private val addDownloadStatOperation: AddDownloadStatOperation = Injekt.get(), ) { /** @@ -224,6 +229,13 @@ class DownloadManager( removeFromDownloadQueue(filteredChapters) val (mangaDir, chapterDirs) = provider.findChapterDirs(filteredChapters, manga, source) + addDownloadStatOperation.await( + DownloadStatOperation.create().copy( + mangaId = manga.id, + size = chapterDirs.sumOf { DiskUtil.getDirectorySize(File(it.filePath!!)) } * -1, + units = filteredChapters.size.toLong() * -1, + ), + ) chapterDirs.forEach { it.delete() } cache.removeChapters(filteredChapters, manga) @@ -246,7 +258,18 @@ class DownloadManager( if (removeQueued) { downloader.removeFromQueue(manga) } - provider.findMangaDir(manga.title, source)?.delete() + val mangaDir = provider.findMangaDir(manga.title, source) + val dirSize = DiskUtil.getDirectorySize(File(mangaDir?.filePath!!)) + if (dirSize > 0) { + addDownloadStatOperation.await( + DownloadStatOperation.create().copy( + mangaId = manga.id, + size = dirSize * -1, + units = cache.getDownloadCount(manga).toLong() * -1, + ), + ) + } + mangaDir.delete() cache.removeManga(manga) // Delete source directory if empty diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index f89146e3a0..c1c3684e55 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -54,6 +54,8 @@ import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.download.service.DownloadPreferences import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager +import tachiyomi.domain.stat.interactor.AddDownloadStatOperation +import tachiyomi.domain.stat.model.DownloadStatOperation import tachiyomi.i18n.MR import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -75,6 +77,7 @@ class Downloader( private val sourceManager: SourceManager = Injekt.get(), private val chapterCache: ChapterCache = Injekt.get(), private val downloadPreferences: DownloadPreferences = Injekt.get(), + private val addDownloadStatOperation: AddDownloadStatOperation = Injekt.get(), private val xml: XML = Injekt.get(), private val getCategories: GetCategories = Injekt.get(), ) { @@ -406,6 +409,22 @@ class Downloader( DiskUtil.createNoMediaFile(tmpDir, context) + addDownloadStatOperation.await( + DownloadStatOperation.create().copy( + mangaId = download.manga.id, + size = DiskUtil.getDirectorySize( + File( + provider.findChapterDir( + download.chapter.name, + download.chapter.scanlator, + download.manga.title, + download.source, + )?.filePath!!, + ), + ), + ), + ) + download.status = Download.State.DOWNLOADED } catch (error: Throwable) { if (error is CancellationException) throw error diff --git a/data/src/main/java/tachiyomi/data/stat/DownloadStatMapper.kt b/data/src/main/java/tachiyomi/data/stat/DownloadStatMapper.kt new file mode 100644 index 0000000000..36daed74fc --- /dev/null +++ b/data/src/main/java/tachiyomi/data/stat/DownloadStatMapper.kt @@ -0,0 +1,19 @@ +package tachiyomi.data.stat + +import tachiyomi.domain.stat.model.DownloadStatOperation + +object DownloadStatMapper { + fun map( + id: Long, + mangaId: Long?, + date: Long, + size: Long, + units: Long, + ): DownloadStatOperation = DownloadStatOperation( + id = id, + mangaId = mangaId, + date = date * 1000, + size = size, + units = units, + ) +} diff --git a/data/src/main/java/tachiyomi/data/stat/DownloadStatRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/stat/DownloadStatRepositoryImpl.kt new file mode 100644 index 0000000000..e4bb13c655 --- /dev/null +++ b/data/src/main/java/tachiyomi/data/stat/DownloadStatRepositoryImpl.kt @@ -0,0 +1,30 @@ +package tachiyomi.data.stat + +import kotlinx.coroutines.flow.Flow +import tachiyomi.data.DatabaseHandler +import tachiyomi.domain.stat.model.DownloadStatOperation +import tachiyomi.domain.stat.repository.DownloadStatRepository + +class DownloadStatRepositoryImpl( + private val handler: DatabaseHandler, +) : DownloadStatRepository { + + override suspend fun getStatOperations(): List { + return handler.awaitList { download_statQueries.getStatOperations(DownloadStatMapper::map) } + } + + override suspend fun getStatOperationsAsFlow(): Flow> { + return handler.subscribeToList { download_statQueries.getStatOperations(DownloadStatMapper::map) } + } + + override suspend fun insert(operation: DownloadStatOperation) { + handler.await { + download_statQueries.insert( + mangaId = operation.mangaId, + date = operation.date / 1000, + size = operation.size, + units = operation.units, + ) + } + } +} diff --git a/data/src/main/sqldelight/tachiyomi/data/download_stat.sq b/data/src/main/sqldelight/tachiyomi/data/download_stat.sq new file mode 100644 index 0000000000..e4a1774ec0 --- /dev/null +++ b/data/src/main/sqldelight/tachiyomi/data/download_stat.sq @@ -0,0 +1,18 @@ +CREATE TABLE download_stat( + _id INTEGER NOT NULL PRIMARY KEY, + manga_id INTEGER, + date INTEGER NOT NULL DEFAULT 0, + size INTEGER NOT NULL, + units INTEGER NOT NULL, + + FOREIGN KEY(manga_id) REFERENCES mangas (_id) + ON DELETE SET NULL +); + +-- Methods +getStatOperations: +SELECT * FROM download_stat; + +insert: +INSERT INTO download_stat(manga_id, date, size, units) +VALUES (:mangaId, :date, :size, :units); diff --git a/data/src/main/sqldelight/tachiyomi/migrations/28.sqm b/data/src/main/sqldelight/tachiyomi/migrations/28.sqm new file mode 100644 index 0000000000..44bb99e7a1 --- /dev/null +++ b/data/src/main/sqldelight/tachiyomi/migrations/28.sqm @@ -0,0 +1,10 @@ +CREATE TABLE download_stat( + _id INTEGER NOT NULL PRIMARY KEY, + manga_id INTEGER, + date INTEGER NOT NULL DEFAULT 0, + size INTEGER NOT NULL, + units INTEGER NOT NULL, + + FOREIGN KEY(manga_id) REFERENCES mangas (_id) + ON DELETE SET NULL +); \ No newline at end of file diff --git a/domain/src/main/java/tachiyomi/domain/stat/interactor/AddDownloadStatOperation.kt b/domain/src/main/java/tachiyomi/domain/stat/interactor/AddDownloadStatOperation.kt new file mode 100644 index 0000000000..ff07f5ec9e --- /dev/null +++ b/domain/src/main/java/tachiyomi/domain/stat/interactor/AddDownloadStatOperation.kt @@ -0,0 +1,13 @@ +package tachiyomi.domain.stat.interactor + +import tachiyomi.domain.stat.model.DownloadStatOperation +import tachiyomi.domain.stat.repository.DownloadStatRepository + +class AddDownloadStatOperation( + private val repository: DownloadStatRepository, +) { + + suspend fun await(actions: DownloadStatOperation) { + repository.insert(actions) + } +} diff --git a/domain/src/main/java/tachiyomi/domain/stat/interactor/GetDownloadStatOperations.kt b/domain/src/main/java/tachiyomi/domain/stat/interactor/GetDownloadStatOperations.kt new file mode 100644 index 0000000000..c88589a946 --- /dev/null +++ b/domain/src/main/java/tachiyomi/domain/stat/interactor/GetDownloadStatOperations.kt @@ -0,0 +1,18 @@ +package tachiyomi.domain.stat.interactor + +import kotlinx.coroutines.flow.Flow +import tachiyomi.domain.stat.model.DownloadStatOperation +import tachiyomi.domain.stat.repository.DownloadStatRepository + +class GetDownloadStatOperations( + private val repository: DownloadStatRepository, +) { + + suspend fun await(): List { + return repository.getStatOperations() + } + + suspend fun subscribe(): Flow> { + return repository.getStatOperationsAsFlow() + } +} diff --git a/domain/src/main/java/tachiyomi/domain/stat/model/DownloadStatOperation.kt b/domain/src/main/java/tachiyomi/domain/stat/model/DownloadStatOperation.kt new file mode 100644 index 0000000000..54937fba80 --- /dev/null +++ b/domain/src/main/java/tachiyomi/domain/stat/model/DownloadStatOperation.kt @@ -0,0 +1,21 @@ +package tachiyomi.domain.stat.model + +import java.util.Date + +data class DownloadStatOperation( + val id: Long, + val mangaId: Long?, + val date: Long, + val size: Long, + val units: Long, +) { + companion object { + fun create() = DownloadStatOperation( + id = -1, + mangaId = -1, + date = Date().time, + size = -1, + units = 1, + ) + } +} diff --git a/domain/src/main/java/tachiyomi/domain/stat/repository/DownloadStatRepository.kt b/domain/src/main/java/tachiyomi/domain/stat/repository/DownloadStatRepository.kt new file mode 100644 index 0000000000..d5333c5034 --- /dev/null +++ b/domain/src/main/java/tachiyomi/domain/stat/repository/DownloadStatRepository.kt @@ -0,0 +1,13 @@ +package tachiyomi.domain.stat.repository + +import kotlinx.coroutines.flow.Flow +import tachiyomi.domain.stat.model.DownloadStatOperation + +interface DownloadStatRepository { + + suspend fun getStatOperations(): List + + suspend fun getStatOperationsAsFlow(): Flow> + + suspend fun insert(operation: DownloadStatOperation) +} diff --git a/i18n/src/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/resources/MR/base/strings.xml index ba1dbef48f..78615a5142 100644 --- a/i18n/src/commonMain/resources/MR/base/strings.xml +++ b/i18n/src/commonMain/resources/MR/base/strings.xml @@ -31,6 +31,7 @@ Backup and restore Data and storage Statistics + Download statistics Migrate Extensions Extension info @@ -166,6 +167,11 @@ FAQ and Guides Not now + + Overall + Size + Deleted chapters + Loading… InternalError: Check crash logs for further information