Gestures
OpenWithIDDialog
This commit is contained in:
@@ -19,12 +19,16 @@
|
||||
package xyz.quaver.pupil.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
@@ -33,21 +37,18 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.BrokenImage
|
||||
import androidx.compose.material.icons.filled.Fullscreen
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import coil.annotation.ExperimentalCoilApi
|
||||
import com.google.accompanist.appcompattheme.AppCompatTheme
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.closestDI
|
||||
@@ -71,7 +72,7 @@ class ReaderActivity : ComponentActivity(), DIAware {
|
||||
|
||||
private val logger = newLogger(LoggerFactory.default)
|
||||
|
||||
@OptIn(ExperimentalCoilApi::class)
|
||||
@OptIn(ExperimentalCoilApi::class, ExperimentalFoundationApi::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -80,24 +81,20 @@ class ReaderActivity : ComponentActivity(), DIAware {
|
||||
|
||||
setContent {
|
||||
var isFABExpanded by remember { mutableStateOf(FloatingActionButtonState.COLLAPSED) }
|
||||
val isFullscreen by model.isFullscreen.observeAsState(false)
|
||||
val imageSources = remember { mutableStateListOf<ImageSource?>() }
|
||||
val imageHeights = remember { mutableStateListOf<Float?>() }
|
||||
val states = remember { mutableStateListOf<SubSampledImageState>() }
|
||||
|
||||
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) })
|
||||
|
||||
if (imageHeights.isEmpty() && model.imageList.isNotEmpty())
|
||||
imageHeights.addAll(List(model.imageList.size) { null })
|
||||
|
||||
logger.info {
|
||||
"${model.imageList.count { it == null }} nulls"
|
||||
}
|
||||
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)
|
||||
@@ -108,24 +105,27 @@ class ReaderActivity : ComponentActivity(), DIAware {
|
||||
model.error(i)
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
logger.info {
|
||||
"${imageSources.count { it == null }} nulls"
|
||||
}
|
||||
}
|
||||
|
||||
WindowInsetsControllerCompat(window, window.decorView).run {
|
||||
if (isFullscreen) {
|
||||
if (model.isFullscreen) {
|
||||
hide(WindowInsetsCompat.Type.systemBars())
|
||||
systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
} else
|
||||
show(WindowInsetsCompat.Type.systemBars())
|
||||
}
|
||||
|
||||
if (model.error)
|
||||
stringResource(R.string.reader_failed_to_find_gallery).let {
|
||||
snackbarCoroutineScope.launch {
|
||||
scaffoldState.snackbarHostState.showSnackbar(it, duration = SnackbarDuration.Indefinite)
|
||||
}
|
||||
}
|
||||
|
||||
PupilTheme {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
if (!isFullscreen)
|
||||
if (!model.isFullscreen)
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
@@ -145,14 +145,14 @@ class ReaderActivity : ComponentActivity(), DIAware {
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
if (!isFullscreen)
|
||||
if (!model.isFullscreen)
|
||||
MultipleFloatingActionButton(
|
||||
items = listOf(
|
||||
SubFabItem(
|
||||
icon = Icons.Default.Fullscreen,
|
||||
label = stringResource(id = R.string.reader_fab_fullscreen)
|
||||
) {
|
||||
model.isFullscreen.postValue(true)
|
||||
model.isFullscreen = true
|
||||
}
|
||||
),
|
||||
targetState = isFABExpanded,
|
||||
@@ -160,61 +160,76 @@ class ReaderActivity : ComponentActivity(), DIAware {
|
||||
isFABExpanded = it
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
scaffoldState = scaffoldState,
|
||||
snackbarHost = { scaffoldState.snackbarHostState }
|
||||
) {
|
||||
LazyColumn(
|
||||
Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
itemsIndexed(imageSources) { i, imageSource ->
|
||||
LaunchedEffect(states[i].canvasSize, states[i].imageSize) {
|
||||
if (imageHeights.isNotEmpty() && imageHeights[i] == null)
|
||||
states[i].canvasSize?.let { canvasSize ->
|
||||
states[i].imageSize?.let { imageSize ->
|
||||
imageHeights[i] = imageSize.height * canvasSize.width / imageSize.width
|
||||
} }
|
||||
}
|
||||
Box {
|
||||
LazyColumn(
|
||||
Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
itemsIndexed(imageSources) { i, imageSource ->
|
||||
Box(
|
||||
Modifier
|
||||
.wrapContentHeight(states[i], 500.dp)
|
||||
.fillMaxWidth()
|
||||
.border(1.dp, Color.Gray),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (imageSource == null)
|
||||
model.progressList.getOrNull(i)?.let { progress ->
|
||||
if (progress < 0f)
|
||||
Icon(Icons.Filled.BrokenImage, null)
|
||||
else
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
LinearProgressIndicator(progress)
|
||||
Text((i + 1).toString())
|
||||
}
|
||||
}
|
||||
else {
|
||||
val haptic = LocalHapticFeedback.current
|
||||
|
||||
Box(
|
||||
Modifier
|
||||
.height(
|
||||
imageHeights
|
||||
.getOrNull(i)
|
||||
?.let { with(LocalDensity.current) { it.toDp() } }
|
||||
?: 500.dp)
|
||||
.fillMaxWidth()
|
||||
.border(1.dp, Color.Gray),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (imageSource == null)
|
||||
model.progressList.getOrNull(i)?.let { progress ->
|
||||
if (progress < 0f)
|
||||
Icon(Icons.Filled.BrokenImage, null)
|
||||
else
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
LinearProgressIndicator(progress)
|
||||
Text((i + 1).toString())
|
||||
}
|
||||
SubSampledImage(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.run {
|
||||
if (model.isFullscreen)
|
||||
doubleClickCycleZoom(states[i], 2f)
|
||||
else
|
||||
combinedClickable(
|
||||
onLongClick = {
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
}
|
||||
) {
|
||||
model.isFullscreen = true
|
||||
}
|
||||
},
|
||||
imageSource = imageSource,
|
||||
state = states[i]
|
||||
)
|
||||
}
|
||||
else {
|
||||
SubSampledImage(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
imageSource = imageSource,
|
||||
state = states[i]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (model.totalProgress != model.imageCount)
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
progress = model.progressList.map { abs(it) }.sum() / model.progressList.size,
|
||||
color = colorResource(id = R.color.colorAccent)
|
||||
if (model.totalProgress != model.imageCount)
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.TopCenter),
|
||||
progress = model.progressList.map { abs(it) }
|
||||
.sum() / model.progressList.size,
|
||||
color = MaterialTheme.colors.secondary
|
||||
)
|
||||
|
||||
SnackbarHost(
|
||||
scaffoldState.snackbarHostState,
|
||||
modifier = Modifier.align(Alignment.BottomCenter)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,7 +242,7 @@ class ReaderActivity : ComponentActivity(), DIAware {
|
||||
|
||||
override fun onBackPressed() {
|
||||
when {
|
||||
model.isFullscreen.value == true -> model.isFullscreen.postValue(false)
|
||||
model.isFullscreen -> model.isFullscreen = false
|
||||
else -> super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user