diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt index 60b7a4f2f..c2f23af6d 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt @@ -21,10 +21,8 @@ import android.app.Activity import android.app.Dialog import android.app.DownloadManager import android.content.DialogInterface -import android.content.pm.PackageManager import android.graphics.Color import android.net.Uri -import android.os.Build import android.os.Bundle import android.os.Environment import android.view.LayoutInflater @@ -32,7 +30,6 @@ import android.view.View import android.view.ViewGroup import android.widget.AdapterView import android.widget.ArrayAdapter -import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.StringRes import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll @@ -96,7 +93,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidViewBinding -import androidx.core.content.ContextCompat import androidx.core.text.parseAsHtml import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController @@ -170,7 +166,10 @@ import com.hippo.ehviewer.util.IntList import com.hippo.ehviewer.util.ReadableTime import com.hippo.ehviewer.util.addTextToClipboard import com.hippo.ehviewer.util.getParcelableCompat +import com.hippo.ehviewer.util.isAtLeastQ +import com.hippo.ehviewer.util.requestPermission import eu.kanade.tachiyomi.util.lang.launchIO +import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.withUIContext import kotlin.math.roundToInt import kotlinx.coroutines.CancellationException @@ -197,20 +196,6 @@ class GalleryDetailScene : BaseScene() { private var getDetailError by mutableStateOf("") private var mTorrentList: TorrentResult? = null - private var requestStoragePermissionLauncher = registerForActivityResult( - ActivityResultContracts.RequestPermission(), - ) { result: Boolean -> - if (result && composeBindingGD != null) { - val helper = TorrentListDialogHelper() - val binding = DialogTorrentListBinding.inflate(layoutInflater) - val dialog: Dialog = BaseDialogBuilder(requireActivity()) - .setTitle(R.string.torrents) - .setView(binding.root) - .setOnDismissListener(helper) - .show() - helper.setDialog(dialog, binding, composeBindingGD!!.torrentUrl) - } - } private var mArchiveFormParamOr: String? = null private var mArchiveList: List? = null private var mCurrentFunds: HomeParser.Funds? = null @@ -1113,21 +1098,20 @@ class GalleryDetailScene : BaseScene() { private fun showTorrentDialog() { val galleryDetail = composeBindingGD ?: return - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && ContextCompat.checkSelfPermission( - requireActivity(), - Manifest.permission.WRITE_EXTERNAL_STORAGE, - ) != PackageManager.PERMISSION_GRANTED - ) { - requestStoragePermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) - } else { - val helper = TorrentListDialogHelper() - val binding = DialogTorrentListBinding.inflate(layoutInflater) - val dialog: Dialog = BaseDialogBuilder(requireContext()) - .setTitle(R.string.torrents) - .setView(binding.root) - .setOnDismissListener(helper) - .show() - helper.setDialog(dialog, binding, galleryDetail.torrentUrl) + viewLifecycleOwner.lifecycleScope.launchUI { + val granted = isAtLeastQ || requireContext().requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) + if (granted) { + val helper = TorrentListDialogHelper() + val binding = DialogTorrentListBinding.inflate(layoutInflater) + val dialog: Dialog = BaseDialogBuilder(requireContext()) + .setTitle(R.string.torrents) + .setView(binding.root) + .setOnDismissListener(helper) + .show() + helper.setDialog(dialog, binding, galleryDetail.torrentUrl) + } else { + showTip(getString(R.string.permission_denied), LENGTH_SHORT) + } } } diff --git a/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index bef317d0a..1daea4954 100644 --- a/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -23,7 +23,6 @@ import android.content.ClipData import android.content.ContentValues import android.content.Intent import android.content.pm.ActivityInfo -import android.content.pm.PackageManager import android.graphics.Color import android.graphics.ColorMatrix import android.graphics.ColorMatrixColorFilter @@ -43,7 +42,6 @@ import android.view.animation.Animation import android.view.animation.AnimationUtils import android.webkit.MimeTypeMap import android.widget.Toast -import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts.CreateDocument import androidx.activity.viewModels import androidx.annotation.ChecksSdkIntAtLeast @@ -52,7 +50,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import androidx.core.content.ContextCompat import androidx.core.content.FileProvider import androidx.core.graphics.ColorUtils import androidx.core.view.WindowCompat @@ -82,7 +79,9 @@ import com.hippo.ehviewer.util.getParcelableExtraCompat import com.hippo.ehviewer.util.getValue import com.hippo.ehviewer.util.isAtLeastO import com.hippo.ehviewer.util.isAtLeastP +import com.hippo.ehviewer.util.isAtLeastQ import com.hippo.ehviewer.util.lazyMut +import com.hippo.ehviewer.util.requestPermission import com.hippo.ehviewer.util.sendTo import com.hippo.ehviewer.util.setValue import com.hippo.unifile.UniFile @@ -95,6 +94,7 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer import eu.kanade.tachiyomi.util.lang.launchIO +import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.withUIContext import eu.kanade.tachiyomi.util.system.applySystemAnimatorScale import eu.kanade.tachiyomi.util.system.isNightMode @@ -170,17 +170,6 @@ class ReaderActivity : EhActivity() { } var mGalleryProvider by lazyMut { vm::galleryProvider } private var mCurrentIndex: Int = 0 - private var mSavingPage = -1 - private var requestStoragePermissionLauncher = registerForActivityResult( - ActivityResultContracts.RequestPermission(), - ) { result -> - if (result!! && mSavingPage != -1) { - saveImage(mSavingPage) - } else { - Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show() - } - mSavingPage = -1 - } private val galleryDetailUrl: String? get() { @@ -455,78 +444,73 @@ class ReaderActivity : EhActivity() { } fun saveImage(page: Int) { - if (null == mGalleryProvider) { - return - } + mGalleryProvider ?: return - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && ContextCompat.checkSelfPermission( - this, - Manifest.permission.WRITE_EXTERNAL_STORAGE, - ) != PackageManager.PERMISSION_GRANTED - ) { - mSavingPage = page - requestStoragePermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) - return - } + lifecycleScope.launchUI { + val granted = isAtLeastQ || requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) + if (granted) { + val filename = mGalleryProvider!!.getImageFilenameWithExtension(page) + var mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension( + MimeTypeMap.getFileExtensionFromUrl(filename), + ) + if (mimeType.isNullOrEmpty()) { + mimeType = "image/jpeg" + } - val filename = mGalleryProvider!!.getImageFilenameWithExtension(page) - var mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension( - MimeTypeMap.getFileExtensionFromUrl(filename), - ) - if (mimeType.isNullOrEmpty()) { - mimeType = "image/jpeg" - } + val realPath: String + val resolver = contentResolver + val values = ContentValues() + values.put(MediaStore.MediaColumns.DISPLAY_NAME, filename) + values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis()) + values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType) + if (isAtLeastQ) { + values.put( + MediaStore.MediaColumns.RELATIVE_PATH, + Environment.DIRECTORY_PICTURES + File.separator + AppConfig.APP_DIRNAME, + ) + values.put(MediaStore.MediaColumns.IS_PENDING, 1) + realPath = Environment.DIRECTORY_PICTURES + File.separator + AppConfig.APP_DIRNAME + } else { + val path = File( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), + AppConfig.APP_DIRNAME, + ) + realPath = path.toString() + if (!FileUtils.ensureDirectory(path)) { + Toast.makeText(this@ReaderActivity, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show() + return@launchUI + } + values.put(MediaStore.MediaColumns.DATA, path.toString() + File.separator + filename) + } + val imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values) + if (imageUri == null) { + Toast.makeText(this@ReaderActivity, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show() + return@launchUI + } + if (!mGalleryProvider!!.save(page, UniFile.fromMediaUri(this@ReaderActivity, imageUri))) { + try { + resolver.delete(imageUri, null, null) + } catch (e: Exception) { + e.printStackTrace() + } - val realPath: String - val resolver = contentResolver - val values = ContentValues() - values.put(MediaStore.MediaColumns.DISPLAY_NAME, filename) - values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis()) - values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - values.put( - MediaStore.MediaColumns.RELATIVE_PATH, - Environment.DIRECTORY_PICTURES + File.separator + AppConfig.APP_DIRNAME, - ) - values.put(MediaStore.MediaColumns.IS_PENDING, 1) - realPath = Environment.DIRECTORY_PICTURES + File.separator + AppConfig.APP_DIRNAME - } else { - val path = File( - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), - AppConfig.APP_DIRNAME, - ) - realPath = path.toString() - if (!FileUtils.ensureDirectory(path)) { - Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show() - return + Toast.makeText(this@ReaderActivity, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show() + return@launchUI + } else if (isAtLeastQ) { + val contentValues = ContentValues() + contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0) + resolver.update(imageUri, contentValues, null, null) + } + + Toast.makeText( + this@ReaderActivity, + getString(R.string.image_saved, realPath + File.separator + filename), + Toast.LENGTH_SHORT, + ).show() + } else { + Toast.makeText(this@ReaderActivity, R.string.permission_denied, Toast.LENGTH_SHORT).show() } - values.put(MediaStore.MediaColumns.DATA, path.toString() + File.separator + filename) } - val imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values) - if (imageUri == null) { - Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show() - return - } - if (!mGalleryProvider!!.save(page, UniFile.fromMediaUri(this, imageUri))) { - try { - resolver.delete(imageUri, null, null) - } catch (e: Exception) { - e.printStackTrace() - } - - Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show() - return - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - val contentValues = ContentValues() - contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0) - resolver.update(imageUri, contentValues, null, null) - } - - Toast.makeText( - this, - getString(R.string.image_saved, realPath + File.separator + filename), - Toast.LENGTH_SHORT, - ).show() } fun saveImageTo(page: Int) { diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 2c91a1c95..2761ecac9 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -533,6 +533,7 @@ 每月 有新版本! 已是最新版本 + 权限被拒 设置 重试 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a8076c5aa..f11de4dc2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -597,6 +597,7 @@ Monthly New version available! Already the latest version + Permission denied Settings Default rotation type