From 382d29e303feec6ccdbae29fb50033426833d604 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Sat, 24 May 2025 01:53:53 +0700 Subject: [PATCH] feat(manga-folder): Allow open manga folder with system's File Manager (#920) * feat(manga-folder): Allow open manga folder with system's File Manager * Add error message for failed folder opening --- .../kanade/presentation/manga/MangaScreen.kt | 7 +++++ .../manga/components/MangaToolbar.kt | 11 +++++++- .../kanade/tachiyomi/ui/manga/MangaScreen.kt | 2 ++ .../tachiyomi/ui/manga/MangaScreenModel.kt | 28 +++++++++++++++++-- .../moko-resources/base/strings.xml | 2 ++ 5 files changed, 47 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt index ddd6e5b06..1d323f599 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt @@ -201,6 +201,7 @@ fun MangaScreen( // KMK --> getMangaState: @Composable (Manga) -> State, onClickSourceSettingsClicked: (() -> Unit)?, + onOpenMangaFolder: (() -> Unit)?, onRelatedMangasScreenClick: () -> Unit, onRelatedMangaClick: (Manga) -> Unit, onRelatedMangaLongClick: (Manga) -> Unit, @@ -268,6 +269,7 @@ fun MangaScreen( // KMK --> getMangaState = getMangaState, onClickSourceSettingsClicked = onClickSourceSettingsClicked, + onOpenMangaFolder = onOpenMangaFolder, onRelatedMangasScreenClick = onRelatedMangasScreenClick, onRelatedMangaClick = onRelatedMangaClick, onRelatedMangaLongClick = onRelatedMangaLongClick, @@ -328,6 +330,7 @@ fun MangaScreen( // KMK --> getMangaState = getMangaState, onClickSourceSettingsClicked = onClickSourceSettingsClicked, + onOpenMangaFolder = onOpenMangaFolder, onRelatedMangasScreenClick = onRelatedMangasScreenClick, onRelatedMangaClick = onRelatedMangaClick, onRelatedMangaLongClick = onRelatedMangaLongClick, @@ -405,6 +408,7 @@ private fun MangaScreenSmallImpl( // KMK --> getMangaState: @Composable ((Manga) -> State), onClickSourceSettingsClicked: (() -> Unit)?, + onOpenMangaFolder: (() -> Unit)?, onRelatedMangasScreenClick: () -> Unit, onRelatedMangaClick: (Manga) -> Unit, onRelatedMangaLongClick: (Manga) -> Unit, @@ -487,6 +491,7 @@ private fun MangaScreenSmallImpl( onClickEditInfo = onEditInfoClicked.takeIf { state.manga.favorite }, // KMK --> onClickSourceSettings = onClickSourceSettingsClicked, + onOpenMangaFolder = onOpenMangaFolder, onClickRelatedMangas = onRelatedMangasScreenClick.takeIf { !expandRelatedMangas && showRelatedMangasInOverflow && @@ -870,6 +875,7 @@ private fun MangaScreenLargeImpl( // KMK --> getMangaState: @Composable ((Manga) -> State), onClickSourceSettingsClicked: (() -> Unit)?, + onOpenMangaFolder: (() -> Unit)?, onRelatedMangasScreenClick: () -> Unit, onRelatedMangaClick: (Manga) -> Unit, onRelatedMangaLongClick: (Manga) -> Unit, @@ -944,6 +950,7 @@ private fun MangaScreenLargeImpl( onClickEditInfo = onEditInfoClicked.takeIf { state.manga.favorite }, // KMK --> onClickSourceSettings = onClickSourceSettingsClicked, + onOpenMangaFolder = onOpenMangaFolder, onClickRelatedMangas = onRelatedMangasScreenClick.takeIf { !expandRelatedMangas && showRelatedMangasInOverflow && diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaToolbar.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaToolbar.kt index 3690d2fb2..bd8b89c08 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaToolbar.kt @@ -52,11 +52,12 @@ fun MangaToolbar( onClickEditInfo: (() -> Unit)?, // KMK --> onClickRelatedMangas: (() -> Unit)?, + onClickSourceSettings: (() -> Unit)?, + onOpenMangaFolder: (() -> Unit)?, // KMK <-- onClickRecommend: (() -> Unit)?, onClickMerge: (() -> Unit)?, onClickMergedSettings: (() -> Unit)?, - onClickSourceSettings: (() -> Unit)?, // SY <-- // For action mode @@ -234,6 +235,14 @@ fun MangaToolbar( } // SY <-- // KMK --> + if (onOpenMangaFolder != null) { + add( + AppBar.OverflowAction( + title = stringResource(KMR.strings.action_open_folder), + onClick = onOpenMangaFolder, + ), + ) + } if (onClickSourceSettings != null) { add( AppBar.OverflowAction( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt index c135d875c..8201e2514 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt @@ -383,6 +383,8 @@ class MangaScreen( else -> {} } }.takeIf { isConfigurableSource }, + onOpenMangaFolder = { screenModel.openMangaFolder() } + .takeIf { successState.source !is StubSource }, onRelatedMangasScreenClick = { if (successState.isRelatedMangasFetched == null) { scope.launchIO { screenModel.fetchRelatedMangasFromSource(onDemand = true) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt index dad689a3f..83fbec689 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt @@ -1,6 +1,8 @@ package eu.kanade.tachiyomi.ui.manga import android.content.Context +import android.content.Intent +import android.provider.DocumentsContract import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarResult @@ -50,6 +52,7 @@ import eu.kanade.presentation.util.formattedMessage import eu.kanade.tachiyomi.data.coil.getBestColor import eu.kanade.tachiyomi.data.download.DownloadCache import eu.kanade.tachiyomi.data.download.DownloadManager +import eu.kanade.tachiyomi.data.download.DownloadProvider import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.track.EnhancedTracker import eu.kanade.tachiyomi.data.track.TrackerManager @@ -152,13 +155,12 @@ import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.domain.track.interactor.InsertTrack import tachiyomi.domain.track.model.Track import tachiyomi.i18n.MR +import tachiyomi.i18n.kmk.KMR import tachiyomi.source.local.LocalSource import tachiyomi.source.local.isLocal import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy -import kotlin.collections.filter -import kotlin.collections.forEach import kotlin.math.floor import androidx.compose.runtime.State as RuntimeState @@ -178,6 +180,7 @@ class MangaScreenModel( // KMK --> private val sourcePreferences: SourcePreferences = Injekt.get(), private val refreshTracks: RefreshTracks = Injekt.get(), + private val downloadProvider: DownloadProvider = Injekt.get(), // KMK <-- private val trackerManager: TrackerManager = Injekt.get(), private val trackChapter: TrackChapter = Injekt.get(), @@ -886,6 +889,27 @@ class MangaScreenModel( } } + /** + * Opens manga folder with the system's file manager. + */ + fun openMangaFolder() { + try { + val currentManga = manga + val currentSource = source + if (currentManga == null || currentSource == null || currentSource is StubSource) return + + val mangaDir = downloadProvider.findMangaDir(/* SY --> */ currentManga.ogTitle /* SY <-- */, currentSource) ?: return + val intent = Intent(Intent.ACTION_VIEW).apply { + setDataAndType(mangaDir.uri, DocumentsContract.Document.MIME_TYPE_DIR) + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + context.startActivity(intent) + } catch (e: Exception) { + logcat(LogPriority.ERROR, e) + context.toast(e.message ?: context.stringResource(KMR.strings.error_opening_folder)) + } + } + /** * Get user categories. * diff --git a/i18n-kmk/src/commonMain/moko-resources/base/strings.xml b/i18n-kmk/src/commonMain/moko-resources/base/strings.xml index 48524c5e5..9816d3242 100644 --- a/i18n-kmk/src/commonMain/moko-resources/base/strings.xml +++ b/i18n-kmk/src/commonMain/moko-resources/base/strings.xml @@ -14,12 +14,14 @@ Hide Show hidden categories Panorama cover + Open folder Copy first page to clipboard Copy second page to clipboard Copy combined page to clipboard Smaller tap zones + Failed to open folder Custom theme style