Fix navigation bug
This commit is contained in:
2
.idea/deploymentTargetDropDown.xml
generated
2
.idea/deploymentTargetDropDown.xml
generated
@@ -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>
|
||||||
@@ -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()
|
||||||
|
|
||||||
SubSampledImage(
|
if (imageSource == null)
|
||||||
modifier = Modifier
|
Icon(Icons.Default.BrokenImage, contentDescription = null)
|
||||||
.fillMaxSize()
|
else
|
||||||
.run {
|
SubSampledImage(
|
||||||
if (model.isFullscreen)
|
modifier = Modifier
|
||||||
doubleClickCycleZoom(states[i], 2f)
|
.fillMaxSize()
|
||||||
else
|
.run {
|
||||||
combinedClickable(
|
if (model.isFullscreen)
|
||||||
onLongClick = {
|
doubleClickCycleZoom(state, 2f)
|
||||||
haptic.performHapticFeedback(
|
else
|
||||||
HapticFeedbackType.LongPress
|
combinedClickable(
|
||||||
)
|
onLongClick = {
|
||||||
|
|
||||||
// TODO
|
}
|
||||||
val uri = FileProvider.getUriForFile(
|
) {
|
||||||
context,
|
model.isFullscreen = true
|
||||||
"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
|
imageSource = imageSource,
|
||||||
}
|
state = state
|
||||||
},
|
)
|
||||||
imageSource = imageSource,
|
|
||||||
state = states[i]
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.progressList.any { abs(it) != 1f })
|
if (model.totalProgress != model.imageCount)
|
||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user