Fix navigation bug

This commit is contained in:
tom5079
2021-12-18 22:07:09 +09:00
parent 9037b41b49
commit 93d68d3867
3 changed files with 50 additions and 76 deletions

View File

@@ -12,6 +12,6 @@
</deviceKey> </deviceKey>
</Target> </Target>
</targetSelectedWithDropDown> </targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2021-12-18T09:43:21.798655Z" /> <timeTargetWasSelectedWithDropDown value="2021-12-18T12:56:18.422116Z" />
</component> </component>
</project> </project>

View File

@@ -19,7 +19,6 @@
package xyz.quaver.pupil.sources.composable package xyz.quaver.pupil.sources.composable
import android.app.Application import android.app.Application
import android.content.Intent
import android.net.Uri import android.net.Uri
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.border import androidx.compose.foundation.border
@@ -35,20 +34,19 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.FileProvider
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.client.utils.*
import io.ktor.http.* import io.ktor.http.*
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.kodein.di.DIAware import org.kodein.di.DIAware
import org.kodein.di.android.closestDI import org.kodein.di.android.closestDI
import org.kodein.di.instance import org.kodein.di.instance
@@ -56,8 +54,8 @@ import xyz.quaver.graphics.subsampledimage.*
import xyz.quaver.io.FileX import xyz.quaver.io.FileX
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.db.AppDatabase import xyz.quaver.pupil.db.AppDatabase
import xyz.quaver.pupil.util.FileXImageSource
import xyz.quaver.pupil.util.NetworkCache import xyz.quaver.pupil.util.NetworkCache
import xyz.quaver.pupil.util.rememberFileXImageSource
import kotlin.math.abs import kotlin.math.abs
open class ReaderBaseViewModel(app: Application) : AndroidViewModel(app), DIAware { 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) var imageCount by mutableStateOf(0)
private var images: List<String>? = null
val imageList = mutableStateListOf<Uri?>() val imageList = mutableStateListOf<Uri?>()
val progressList = mutableStateListOf<Float>() val progressList = mutableStateListOf<Float>()
private val totalProgressMutex = Mutex()
var totalProgress by mutableStateOf(0)
private set
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
fun load(urls: List<String>, headerBuilder: HeadersBuilder.() -> Unit = { }) { fun load(urls: List<String>, headerBuilder: HeadersBuilder.() -> Unit = { }) {
viewModelScope.launch { viewModelScope.launch {
@@ -89,25 +90,36 @@ open class ReaderBaseViewModel(app: Application) : AndroidViewModel(app), DIAwar
progressList.addAll(List(imageCount) { 0f }) progressList.addAll(List(imageCount) { 0f })
imageList.addAll(List(imageCount) { null }) imageList.addAll(List(imageCount) { null })
totalProgressMutex.withLock {
totalProgress = 0
}
urls.forEachIndexed { index, url -> urls.forEachIndexed { index, url ->
when (val scheme = url.takeWhile { it != ':' }) { when (val scheme = url.takeWhile { it != ':' }) {
"http", "https" -> { "http", "https" -> {
val (channel, file) = cache.load { val (channel, file) = cache.load {
url(url) url(url)
buildHeaders(headerBuilder) headers(headerBuilder)
} }
if (channel.isClosedForReceive) { if (channel.isClosedForReceive) {
imageList[index] = Uri.fromFile(file) imageList[index] = Uri.fromFile(file)
totalProgressMutex.withLock {
totalProgress++
}
} else { } else {
channel.invokeOnClose { e -> channel.invokeOnClose { e ->
viewModelScope.launch { viewModelScope.launch {
if (e == null) { if (e == null) {
imageList[index] = Uri.fromFile(file) imageList[index] = Uri.fromFile(file)
} else { } else {
error(index) error(index)
} }
imageList[index] = Uri.fromFile(file)
totalProgressMutex.withLock {
totalProgress++
}
} }
} }
@@ -143,35 +155,13 @@ fun ReaderBase(
onToggleBookmark: () -> Unit = { } onToggleBookmark: () -> Unit = { }
) { ) {
val context = LocalContext.current val context = LocalContext.current
val haptic = LocalHapticFeedback.current
var isFABExpanded by remember { mutableStateOf(FloatingActionButtonState.COLLAPSED) } var isFABExpanded by remember { mutableStateOf(FloatingActionButtonState.COLLAPSED) }
val imageSources = remember { mutableStateListOf<ImageSource?>() }
val states = remember { mutableStateListOf<SubSampledImageState>() }
val scaffoldState = rememberScaffoldState() val scaffoldState = rememberScaffoldState()
val snackbarCoroutineScope = rememberCoroutineScope() 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) if (model.error)
stringResource(R.string.reader_failed_to_find_gallery).let { stringResource(R.string.reader_failed_to_find_gallery).let {
snackbarCoroutineScope.launch { snackbarCoroutineScope.launch {
@@ -224,15 +214,17 @@ fun ReaderBase(
Modifier.fillMaxSize(), Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(4.dp) verticalArrangement = Arrangement.spacedBy(4.dp)
) { ) {
itemsIndexed(imageSources) { i, imageSource -> itemsIndexed(model.imageList) { i, uri ->
val state = rememberSubSampledImageState(ScaleTypes.FIT_WIDTH)
Box( Box(
Modifier Modifier
.wrapContentHeight(states[i], 500.dp) .wrapContentHeight(state, 500.dp)
.fillMaxWidth() .fillMaxWidth()
.border(1.dp, Color.Gray), .border(1.dp, Color.Gray),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
if (imageSource == null) if (uri == null)
model.progressList.getOrNull(i)?.let { progress -> model.progressList.getOrNull(i)?.let { progress ->
if (progress < 0f) if (progress < 0f)
Icon(Icons.Filled.BrokenImage, null) Icon(Icons.Filled.BrokenImage, null)
@@ -245,55 +237,37 @@ fun ReaderBase(
} }
} }
else { else {
val haptic = LocalHapticFeedback.current val imageSource = kotlin.runCatching {
rememberFileXImageSource(FileX(context, uri))
}.getOrNull()
if (imageSource == null)
Icon(Icons.Default.BrokenImage, contentDescription = null)
else
SubSampledImage( SubSampledImage(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.run { .run {
if (model.isFullscreen) if (model.isFullscreen)
doubleClickCycleZoom(states[i], 2f) doubleClickCycleZoom(state, 2f)
else else
combinedClickable( combinedClickable(
onLongClick = { onLongClick = {
haptic.performHapticFeedback(
HapticFeedbackType.LongPress
)
// 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, imageSource = imageSource,
state = states[i] state = state
) )
} }
} }
} }
} }
if (model.progressList.any { abs(it) != 1f }) if (model.totalProgress != model.imageCount)
LinearProgressIndicator( LinearProgressIndicator(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()

View File

@@ -59,7 +59,7 @@ class Hitomi(app: Application) : Source(), DIAware {
override fun MainScreen(navController: NavController) { override fun MainScreen(navController: NavController) {
navController.navigate("search/hitomi.la") { navController.navigate("search/hitomi.la") {
launchSingleTop = true launchSingleTop = true
popUpTo("main") { inclusive = true } navController.popBackStack()
} }
} }