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
This commit is contained in:
Cuong-Tran 2025-05-24 01:53:53 +07:00 committed by GitHub
parent fb9409c12f
commit 382d29e303
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 47 additions and 3 deletions

View file

@ -201,6 +201,7 @@ fun MangaScreen(
// KMK -->
getMangaState: @Composable (Manga) -> State<Manga>,
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<Manga>),
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<Manga>),
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 &&

View file

@ -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(

View file

@ -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) }

View file

@ -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.
*

View file

@ -14,12 +14,14 @@
<string name="action_hide">Hide</string>
<string name="action_show_hidden_categories">Show hidden categories</string>
<string name="action_panorama_cover">Panorama cover</string>
<string name="action_open_folder">Open folder</string>
<!-- Reader -->
<!-- Reader Actions -->
<string name="action_copy_to_clipboard_first_page">Copy first page to clipboard</string>
<string name="action_copy_to_clipboard_second_page">Copy second page to clipboard</string>
<string name="action_copy_to_clipboard_combined_page">Copy combined page to clipboard</string>
<string name="pref_viewer_nav_smaller_tap_zone">Smaller tap zones</string>
<string name="error_opening_folder">Failed to open folder</string>
<!-- Preferences -->
<!-- Appearance section -->
<string name="pref_custom_theme_style">Custom theme style</string>