diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
index fdd365df..a3d5c4b6 100644
--- a/.idea/deploymentTargetDropDown.xml
+++ b/.idea/deploymentTargetDropDown.xml
@@ -12,6 +12,6 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/sources/composable/ReaderBase.kt b/app/src/main/java/xyz/quaver/pupil/sources/composable/ReaderBase.kt
index 4fb4a514..9bd6227a 100644
--- a/app/src/main/java/xyz/quaver/pupil/sources/composable/ReaderBase.kt
+++ b/app/src/main/java/xyz/quaver/pupil/sources/composable/ReaderBase.kt
@@ -19,7 +19,6 @@
package xyz.quaver.pupil.sources.composable
import android.app.Application
-import android.content.Intent
import android.net.Uri
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.border
@@ -35,20 +34,19 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
-import androidx.core.content.FileProvider
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import io.ktor.client.request.*
-import io.ktor.client.utils.*
import io.ktor.http.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
import org.kodein.di.DIAware
import org.kodein.di.android.closestDI
import org.kodein.di.instance
@@ -56,8 +54,8 @@ import xyz.quaver.graphics.subsampledimage.*
import xyz.quaver.io.FileX
import xyz.quaver.pupil.R
import xyz.quaver.pupil.db.AppDatabase
-import xyz.quaver.pupil.util.FileXImageSource
import xyz.quaver.pupil.util.NetworkCache
+import xyz.quaver.pupil.util.rememberFileXImageSource
import kotlin.math.abs
open class ReaderBaseViewModel(app: Application) : AndroidViewModel(app), DIAware {
@@ -78,10 +76,13 @@ open class ReaderBaseViewModel(app: Application) : AndroidViewModel(app), DIAwar
var imageCount by mutableStateOf(0)
- private var images: List? = null
val imageList = mutableStateListOf()
val progressList = mutableStateListOf()
+ private val totalProgressMutex = Mutex()
+ var totalProgress by mutableStateOf(0)
+ private set
+
@OptIn(ExperimentalCoroutinesApi::class)
fun load(urls: List, headerBuilder: HeadersBuilder.() -> Unit = { }) {
viewModelScope.launch {
@@ -89,25 +90,36 @@ open class ReaderBaseViewModel(app: Application) : AndroidViewModel(app), DIAwar
progressList.addAll(List(imageCount) { 0f })
imageList.addAll(List(imageCount) { null })
+ totalProgressMutex.withLock {
+ totalProgress = 0
+ }
urls.forEachIndexed { index, url ->
when (val scheme = url.takeWhile { it != ':' }) {
"http", "https" -> {
val (channel, file) = cache.load {
url(url)
- buildHeaders(headerBuilder)
+ headers(headerBuilder)
}
if (channel.isClosedForReceive) {
imageList[index] = Uri.fromFile(file)
+ totalProgressMutex.withLock {
+ totalProgress++
+ }
} else {
channel.invokeOnClose { e ->
viewModelScope.launch {
if (e == null) {
imageList[index] = Uri.fromFile(file)
+
} else {
error(index)
}
+ imageList[index] = Uri.fromFile(file)
+ totalProgressMutex.withLock {
+ totalProgress++
+ }
}
}
@@ -143,35 +155,13 @@ fun ReaderBase(
onToggleBookmark: () -> Unit = { }
) {
val context = LocalContext.current
+ val haptic = LocalHapticFeedback.current
var isFABExpanded by remember { mutableStateOf(FloatingActionButtonState.COLLAPSED) }
- val imageSources = remember { mutableStateListOf() }
- val states = remember { mutableStateListOf() }
val scaffoldState = rememberScaffoldState()
val snackbarCoroutineScope = rememberCoroutineScope()
- LaunchedEffect(model.imageList.count { it != null }) {
- if (imageSources.isEmpty() && model.imageList.isNotEmpty())
- imageSources.addAll(List(model.imageList.size) { null })
-
- if (states.isEmpty() && model.imageList.isNotEmpty())
- states.addAll(List(model.imageList.size) {
- SubSampledImageState(ScaleTypes.FIT_WIDTH, Bounds.FORCE_OVERLAP_OR_CENTER).apply {
- isGestureEnabled = true
- }
- })
-
- model.imageList.forEachIndexed { i, image ->
- if (imageSources[i] == null && image != null)
- imageSources[i] = kotlin.runCatching {
- FileXImageSource(FileX(context, image))
- }.onFailure {
- model.error(i)
- }.getOrNull()
- }
- }
-
if (model.error)
stringResource(R.string.reader_failed_to_find_gallery).let {
snackbarCoroutineScope.launch {
@@ -224,15 +214,17 @@ fun ReaderBase(
Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
- itemsIndexed(imageSources) { i, imageSource ->
+ itemsIndexed(model.imageList) { i, uri ->
+ val state = rememberSubSampledImageState(ScaleTypes.FIT_WIDTH)
+
Box(
Modifier
- .wrapContentHeight(states[i], 500.dp)
+ .wrapContentHeight(state, 500.dp)
.fillMaxWidth()
.border(1.dp, Color.Gray),
contentAlignment = Alignment.Center
) {
- if (imageSource == null)
+ if (uri == null)
model.progressList.getOrNull(i)?.let { progress ->
if (progress < 0f)
Icon(Icons.Filled.BrokenImage, null)
@@ -245,55 +237,37 @@ fun ReaderBase(
}
}
else {
- val haptic = LocalHapticFeedback.current
+ val imageSource = kotlin.runCatching {
+ rememberFileXImageSource(FileX(context, uri))
+ }.getOrNull()
- SubSampledImage(
- modifier = Modifier
- .fillMaxSize()
- .run {
- if (model.isFullscreen)
- doubleClickCycleZoom(states[i], 2f)
- else
- combinedClickable(
- onLongClick = {
- haptic.performHapticFeedback(
- HapticFeedbackType.LongPress
- )
+ if (imageSource == null)
+ Icon(Icons.Default.BrokenImage, contentDescription = null)
+ else
+ SubSampledImage(
+ modifier = Modifier
+ .fillMaxSize()
+ .run {
+ if (model.isFullscreen)
+ doubleClickCycleZoom(state, 2f)
+ else
+ combinedClickable(
+ onLongClick = {
- // TODO
- val uri = FileProvider.getUriForFile(
- context,
- "xyz.quaver.pupil.fileprovider",
- (imageSource as FileXImageSource).file
- )
- context.startActivity(
- Intent.createChooser(
- Intent(
- Intent.ACTION_SEND
- ).apply {
- type = "image/*"
- putExtra(
- Intent.EXTRA_STREAM,
- uri
- )
- addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
- }, "Share image"
- )
- )
+ }
+ ) {
+ model.isFullscreen = true
}
- ) {
- model.isFullscreen = true
- }
- },
- imageSource = imageSource,
- state = states[i]
- )
+ },
+ imageSource = imageSource,
+ state = state
+ )
}
}
}
}
- if (model.progressList.any { abs(it) != 1f })
+ if (model.totalProgress != model.imageCount)
LinearProgressIndicator(
modifier = Modifier
.fillMaxWidth()
diff --git a/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Hitomi.kt b/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Hitomi.kt
index e6c88ac2..432b344f 100644
--- a/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Hitomi.kt
+++ b/app/src/main/java/xyz/quaver/pupil/sources/hitomi/Hitomi.kt
@@ -59,7 +59,7 @@ class Hitomi(app: Application) : Source(), DIAware {
override fun MainScreen(navController: NavController) {
navController.navigate("search/hitomi.la") {
launchSingleTop = true
- popUpTo("main") { inclusive = true }
+ navController.popBackStack()
}
}