Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions app/src/main/java/eu/kanade/presentation/util/Permissions.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
package eu.kanade.presentation.util

import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.core.content.ContextCompat
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.compose.LocalLifecycleOwner

const val LEGACY_STORAGE_PERMISSION = Manifest.permission.WRITE_EXTERNAL_STORAGE

@Composable
fun rememberRequestPackageInstallsPermissionState(initialValue: Boolean = false): Boolean {
val context = LocalContext.current
Expand All @@ -32,3 +38,27 @@ fun rememberRequestPackageInstallsPermissionState(initialValue: Boolean = false)

return installGranted
}

@Composable
fun rememberLegacyStoragePermissionState(initialValue: Boolean = false): Boolean {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current

var storageGranted by remember { mutableStateOf(initialValue) }

DisposableEffect(lifecycleOwner.lifecycle) {
val observer = object : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
storageGranted = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ||
ContextCompat.checkSelfPermission(context, LEGACY_STORAGE_PERMISSION) ==
PackageManager.PERMISSION_GRANTED
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}

return storageGranted
}
20 changes: 19 additions & 1 deletion app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ import eu.kanade.presentation.manga.components.MangaCoverDialog
import eu.kanade.presentation.manga.components.ScanlatorFilterDialog
import eu.kanade.presentation.manga.components.SetIntervalDialog
import eu.kanade.presentation.util.AssistContentScreen
import eu.kanade.presentation.util.LEGACY_STORAGE_PERMISSION
import eu.kanade.presentation.util.Screen
import eu.kanade.presentation.util.isTabletUi
import eu.kanade.presentation.util.rememberLegacyStoragePermissionState
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.isLocalOrStub
import eu.kanade.tachiyomi.source.online.HttpSource
Expand All @@ -62,6 +64,7 @@ import tachiyomi.core.common.util.lang.withIOContext
import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.screens.LoadingScreen

class MangaScreen(
Expand Down Expand Up @@ -244,16 +247,31 @@ class MangaScreen(
val sm = rememberScreenModel { MangaCoverScreenModel(successState.manga.id) }
val manga by sm.state.collectAsState()
if (manga != null) {
val hasStoragePermission = rememberLegacyStoragePermissionState()
val getContent = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {
if (it == null) return@rememberLauncherForActivityResult
sm.editCover(context, it)
}
val requestStoragePermission =
rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
if (granted) {
sm.saveCover(context)
} else {
context.toast(MR.strings.missing_storage_permission)
}
}
MangaCoverDialog(
manga = manga!!,
snackbarHostState = sm.snackbarHostState,
isCustomCover = remember(manga) { manga!!.hasCustomCover() },
onShareClick = { sm.shareCover(context) },
onSaveClick = { sm.saveCover(context) },
onSaveClick = {
if (hasStoragePermission) {
sm.saveCover(context)
} else {
requestStoragePermission.launch(LEGACY_STORAGE_PERMISSION)
}
},
onEditClick = {
when (it) {
EditCoverAction.EDIT -> getContent.launch("image/*")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ import android.view.View
import android.view.View.LAYER_TYPE_HARDWARE
import android.view.WindowManager
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
Expand Down Expand Up @@ -57,6 +59,8 @@ import eu.kanade.presentation.reader.ReaderPageIndicator
import eu.kanade.presentation.reader.ReadingModeSelectDialog
import eu.kanade.presentation.reader.appbars.ReaderAppBars
import eu.kanade.presentation.reader.settings.ReaderSettingsDialog
import eu.kanade.presentation.util.LEGACY_STORAGE_PERMISSION
import eu.kanade.presentation.util.rememberLegacyStoragePermissionState
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.coil.TachiyomiImageDecoder
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
Expand Down Expand Up @@ -256,6 +260,15 @@ class ReaderActivity : BaseActivity() {
onChangeOrientation = viewModel::setMangaOrientationType,
)
}
val hasStoragePermission = rememberLegacyStoragePermissionState()
val requestStoragePermission =
rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
if (granted) {
viewModel.saveImage()
} else {
toast(MR.strings.missing_storage_permission)
}
}

Box(modifier = Modifier.fillMaxSize()) {
if (!state.menuVisible && showPageNumber) {
Expand Down Expand Up @@ -325,7 +338,13 @@ class ReaderActivity : BaseActivity() {
onDismissRequest = onDismissRequest,
onSetAsCover = viewModel::setAsCover,
onShare = viewModel::shareImage,
onSave = viewModel::saveImage,
onSave = {
if (hasStoragePermission) {
viewModel.saveImage()
} else {
requestStoragePermission.launch(LEGACY_STORAGE_PERMISSION)
}
},
)
}
null -> {}
Expand Down
Loading