From 28160608ec2bfb356038bf357c8ed95089bd580e Mon Sep 17 00:00:00 2001 From: GG Bond Date: Mon, 4 Mar 2024 11:50:40 +0800 Subject: [PATCH] ReaderScreen saveImage --- .../hippo/ehviewer/ui/reader/PageActions.kt | 58 +++++++++++++++++++ .../hippo/ehviewer/ui/reader/ReaderScreen.kt | 5 +- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PageActions.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PageActions.kt index 6e2651769..5ea39c292 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PageActions.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PageActions.kt @@ -1,9 +1,13 @@ package com.hippo.ehviewer.ui.reader +import android.Manifest import android.content.ClipData +import android.content.ContentValues import android.content.Context import android.content.Intent import android.net.Uri +import android.os.Environment +import android.provider.MediaStore import android.webkit.MimeTypeMap import androidx.compose.material3.SnackbarHostState import androidx.core.content.FileProvider @@ -14,10 +18,14 @@ import com.hippo.ehviewer.client.data.GalleryInfo import com.hippo.ehviewer.gallery.PageLoader2 import com.hippo.ehviewer.util.AppConfig import com.hippo.ehviewer.util.FileUtils +import com.hippo.ehviewer.util.isAtLeastQ import com.hippo.ehviewer.util.isAtLeastT +import com.hippo.ehviewer.util.requestPermission import com.hippo.unifile.asUniFile import eu.kanade.tachiyomi.ui.reader.model.ReaderPage +import eu.kanade.tachiyomi.util.system.logcat import java.io.File +import kotlinx.datetime.Clock import splitties.systemservices.clipboardManager context(PageLoader2) @@ -67,3 +75,53 @@ suspend fun copy(page: ReaderPage) { showSnackbar(copied) } } + +context(SnackbarHostState, Context, PageLoader2) +suspend fun save(page: ReaderPage) { + val granted = isAtLeastQ || requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) + val cannotSave = getString(R.string.error_cant_save_image) + if (granted) { + val filename = getImageFilename(page.index) + val extension = FileUtils.getExtensionFromFilename(filename) + val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) ?: "image/jpeg" + val values = ContentValues() + val realPath: String + values.put(MediaStore.MediaColumns.DISPLAY_NAME, filename) + values.put(MediaStore.Images.Media.DATE_ADDED, Clock.System.now().toEpochMilliseconds()) + values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType) + if (isAtLeastQ) { + realPath = Environment.DIRECTORY_PICTURES + File.separator + AppConfig.APP_DIRNAME + values.put(MediaStore.MediaColumns.RELATIVE_PATH, realPath) + values.put(MediaStore.MediaColumns.IS_PENDING, 1) + } else { + val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + val path = File(dir, AppConfig.APP_DIRNAME) + realPath = path.toString() + if (!FileUtils.ensureDirectory(path)) { + showSnackbar(cannotSave) + return + } + values.put(MediaStore.MediaColumns.DATA, realPath + File.separator + filename) + } + val imageUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values) + if (imageUri != null) { + if (!save(page.index, imageUri.asUniFile())) { + try { + contentResolver.delete(imageUri, null, null) + } catch (e: Exception) { + e.logcat(e) + } + showSnackbar(cannotSave) + } else if (isAtLeastQ) { + val contentValues = ContentValues() + contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0) + contentResolver.update(imageUri, contentValues, null, null) + } + showSnackbar(getString(R.string.image_saved, realPath + File.separator + filename)) + } else { + showSnackbar(cannotSave) + } + } else { + showSnackbar(getString(R.string.permission_denied)) + } +} diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/ReaderScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/ReaderScreen.kt index 8345b246f..fbdb77128 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/ReaderScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/ReaderScreen.kt @@ -39,6 +39,7 @@ import eu.kanade.tachiyomi.ui.reader.ReaderAppBars import eu.kanade.tachiyomi.ui.reader.ReaderPageSheetMeta import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType import eu.kanade.tachiyomi.util.lang.launchIO +import kotlin.coroutines.resume import kotlinx.coroutines.launch @Destination @@ -103,7 +104,7 @@ fun ReaderScreen(info: BaseGalleryInfo, page: Int = -1, navigator: DestinationsN onSelectPage = { page -> launch { dialog { cont -> - fun dispose() = cont.cancel() + fun dispose() = cont.resume(Unit) val state = rememberModalBottomSheetState() ModalBottomSheet( onDismissRequest = { dispose() }, @@ -115,7 +116,7 @@ fun ReaderScreen(info: BaseGalleryInfo, page: Int = -1, navigator: DestinationsN retryOrigin = { pageLoader.retryPage(page.index, true) }, share = { launchIO { with(pageLoader) { shareImage(page, info) } } }, copy = { launchIO { with(pageLoader) { copy(page) } } }, - save = {}, + save = { launchIO { with(pageLoader) { save(page) } } }, saveTo = {}, dismiss = { launch { state.hide().also { dispose() } } }, )