Gestures
OpenWithIDDialog
This commit is contained in:
@@ -43,7 +43,6 @@ data class SearchResultEvent(val type: Type, val itemID: String, val payload: Pa
|
||||
abstract class Source {
|
||||
abstract val name: String
|
||||
abstract val iconResID: Int
|
||||
abstract val preferenceID: Int
|
||||
abstract val availableSortMode: List<String>
|
||||
|
||||
abstract suspend fun search(query: String, range: IntRange, sortMode: Int): Pair<Channel<ItemInfo>, Int>
|
||||
|
||||
@@ -36,8 +36,6 @@ class History(override val di: DI) : Source(), DIAware {
|
||||
get() = "history"
|
||||
override val iconResID: Int
|
||||
get() = 0 //TODO
|
||||
override val preferenceID: Int
|
||||
get() = 0 //TODO
|
||||
override val availableSortMode: List<String> = emptyList()
|
||||
|
||||
private val history = direct.database().historyDao()
|
||||
|
||||
@@ -122,7 +122,6 @@ class Hitomi(app: Application) : Source(), DIAware {
|
||||
|
||||
override val name: String = "hitomi.la"
|
||||
override val iconResID: Int = R.drawable.hitomi
|
||||
override val preferenceID: Int = R.xml.hitomi_preferences
|
||||
override val availableSortMode: List<String> = app.resources.getStringArray(R.array.hitomi_sort_mode).toList()
|
||||
|
||||
var cachedQuery: String? = null
|
||||
|
||||
@@ -153,7 +153,6 @@ class Hiyobi_io(app: Application): Source(), DIAware {
|
||||
|
||||
override val name = "hiyobi.io"
|
||||
override val iconResID = R.drawable.hitomi
|
||||
override val preferenceID = 0
|
||||
override val availableSortMode = emptyList<String>()
|
||||
|
||||
private val client: HttpClient by instance()
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Pupil, Hitomi.la viewer for Android
|
||||
* Copyright (C) 2019 tom5079
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.quaver.pupil.types
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Tag(val area: String?, val tag: String, val isNegative: Boolean = false) {
|
||||
companion object {
|
||||
fun parse(tag: String) : Tag {
|
||||
if (tag.firstOrNull() == '-') {
|
||||
tag.substring(1).split(Regex(":"), 2).let {
|
||||
return when(it.size) {
|
||||
2 -> Tag(it[0], it[1], true)
|
||||
else -> Tag(null, tag, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
tag.split(Regex(":"), 2).let {
|
||||
return when(it.size) {
|
||||
2 -> Tag(it[0], it[1])
|
||||
else -> Tag(null, tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return (if (isNegative) "-" else "") + when(area) {
|
||||
null -> tag
|
||||
else -> "$area:$tag"
|
||||
}
|
||||
}
|
||||
|
||||
fun toQuery(): String {
|
||||
return toString().replace(' ', '_')
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is Tag)
|
||||
return false
|
||||
|
||||
if (other.area == area && other.tag == tag)
|
||||
return true
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun hashCode() = toString().hashCode()
|
||||
}
|
||||
|
||||
class Tags(val tags: MutableSet<Tag> = mutableSetOf()) : MutableSet<Tag> by tags {
|
||||
|
||||
companion object {
|
||||
fun parse(tags: String) : Tags {
|
||||
return Tags(
|
||||
tags.split(' ').mapNotNull {
|
||||
if (it.isNotEmpty())
|
||||
Tag.parse(it)
|
||||
else
|
||||
null
|
||||
}.toMutableSet()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun contains(element: String): Boolean {
|
||||
tags.forEach {
|
||||
if (it.toString() == element)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fun add(element: String): Boolean {
|
||||
return tags.add(Tag.parse(element))
|
||||
}
|
||||
|
||||
fun remove(element: String) {
|
||||
tags.filter { it.toString() == element }.forEach {
|
||||
tags.remove(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeByArea(area: String, isNegative: Boolean? = null) {
|
||||
tags.filter { it.area == area && (if(isNegative == null) true else (it.isNegative == isNegative)) }.forEach {
|
||||
tags.remove(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return tags.joinToString(" ") { it.toString() }
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,6 @@ import org.kodein.log.newLogger
|
||||
import xyz.quaver.pupil.*
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.sources.SearchResultEvent
|
||||
import xyz.quaver.pupil.types.*
|
||||
import xyz.quaver.pupil.ui.composable.FloatingActionButtonState
|
||||
import xyz.quaver.pupil.ui.composable.FloatingSearchBar
|
||||
import xyz.quaver.pupil.ui.composable.MultipleFloatingActionButton
|
||||
@@ -66,6 +65,7 @@ import xyz.quaver.pupil.ui.composable.SubFabItem
|
||||
import xyz.quaver.pupil.ui.dialog.SourceSelectDialog
|
||||
import xyz.quaver.pupil.ui.theme.PupilTheme
|
||||
import xyz.quaver.pupil.ui.composable.ProgressCard
|
||||
import xyz.quaver.pupil.ui.dialog.OpenWithItemIDDialog
|
||||
import xyz.quaver.pupil.ui.viewmodel.MainViewModel
|
||||
import xyz.quaver.pupil.util.*
|
||||
import kotlin.math.*
|
||||
@@ -108,21 +108,51 @@ class MainActivity : ComponentActivity(), DIAware {
|
||||
}
|
||||
}
|
||||
|
||||
var openSourceSelectDialog by remember { mutableStateOf(false) }
|
||||
val onSearchResultEvent: (SearchResultEvent) -> Unit = { event ->
|
||||
when (event.type) {
|
||||
SearchResultEvent.Type.OPEN_READER -> {
|
||||
startActivity(
|
||||
Intent(
|
||||
this@MainActivity,
|
||||
ReaderActivity::class.java
|
||||
).apply {
|
||||
putExtra("source", model.source.name)
|
||||
putExtra("id", event.itemID)
|
||||
putExtra("payload", event.payload)
|
||||
})
|
||||
}
|
||||
else -> TODO("")
|
||||
}
|
||||
}
|
||||
|
||||
var sourceSelectDialog by remember { mutableStateOf(false) }
|
||||
var openWithItemIDDialog by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(navigationIconProgress) {
|
||||
navigationIcon.progress = navigationIconProgress
|
||||
}
|
||||
|
||||
if (openSourceSelectDialog)
|
||||
if (sourceSelectDialog)
|
||||
SourceSelectDialog(
|
||||
currentSource = model.source.name,
|
||||
onDismissRequest = { openSourceSelectDialog = false }
|
||||
onDismissRequest = { sourceSelectDialog = false }
|
||||
) { source ->
|
||||
openSourceSelectDialog = false
|
||||
sourceSelectDialog = false
|
||||
model.setSourceAndReset(source.name)
|
||||
}
|
||||
|
||||
if (openWithItemIDDialog)
|
||||
OpenWithItemIDDialog {
|
||||
openWithItemIDDialog = false
|
||||
|
||||
it?.let {
|
||||
onSearchResultEvent(SearchResultEvent(
|
||||
SearchResultEvent.Type.OPEN_READER,
|
||||
it
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
floatingActionButton = {
|
||||
MultipleFloatingActionButton(
|
||||
@@ -142,7 +172,9 @@ class MainActivity : ComponentActivity(), DIAware {
|
||||
SubFabItem(
|
||||
painterResource(R.drawable.numeric),
|
||||
stringResource(R.string.main_open_gallery_by_id)
|
||||
),
|
||||
) {
|
||||
openWithItemIDDialog = true
|
||||
}
|
||||
),
|
||||
visible = isFabVisible,
|
||||
targetState = isFabExpanded,
|
||||
@@ -178,22 +210,7 @@ class MainActivity : ComponentActivity(), DIAware {
|
||||
ProgressCard(
|
||||
progress = 0.5f
|
||||
) {
|
||||
model.source.SearchResult(itemInfo = itemInfo) { event ->
|
||||
when (event.type) {
|
||||
SearchResultEvent.Type.OPEN_READER -> {
|
||||
startActivity(
|
||||
Intent(
|
||||
this@MainActivity,
|
||||
ReaderActivity::class.java
|
||||
).apply {
|
||||
putExtra("source", model.source.name)
|
||||
putExtra("id", event.itemID)
|
||||
putExtra("payload", event.payload)
|
||||
})
|
||||
}
|
||||
else -> TODO("")
|
||||
}
|
||||
}
|
||||
model.source.SearchResult(itemInfo = itemInfo, onEvent = onSearchResultEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,7 +241,7 @@ class MainActivity : ComponentActivity(), DIAware {
|
||||
painterResource(model.source.iconResID),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp).clickable {
|
||||
openSourceSelectDialog = true
|
||||
sourceSelectDialog = true
|
||||
}
|
||||
)
|
||||
Icon(
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Pupil, Hitomi.la viewer for Android
|
||||
* Copyright (C) 2021 tom5079
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.quaver.pupil.ui.dialog
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import xyz.quaver.pupil.R
|
||||
|
||||
@Composable
|
||||
fun OpenWithItemIDDialog(onDismissRequest: (String?) -> Unit = { }) {
|
||||
var itemID by remember { mutableStateOf("") }
|
||||
|
||||
Dialog(onDismissRequest = { onDismissRequest(null) }) {
|
||||
Card(
|
||||
elevation = 8.dp,
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.main_open_gallery_by_id),
|
||||
style = MaterialTheme.typography.h6
|
||||
)
|
||||
TextField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = itemID,
|
||||
onValueChange = {
|
||||
itemID = it
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Go),
|
||||
keyboardActions = KeyboardActions(
|
||||
onGo = { onDismissRequest(itemID) }
|
||||
)
|
||||
)
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = { onDismissRequest(itemID) }
|
||||
) {
|
||||
Text(stringResource(android.R.string.ok))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,8 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
private val DarkColorPalette = darkColors(
|
||||
primary = LightBlue300,
|
||||
primaryVariant = LightBlue700,
|
||||
secondary = Pink600,
|
||||
onSecondary = Color.White
|
||||
)
|
||||
|
||||
@@ -53,13 +53,16 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
||||
|
||||
private val logger = newLogger(LoggerFactory.default)
|
||||
|
||||
val isFullscreen = MutableLiveData(false)
|
||||
var isFullscreen by mutableStateOf(false)
|
||||
|
||||
private val database: AppDatabase by instance()
|
||||
|
||||
private val historyDao = database.historyDao()
|
||||
private val bookmarkDao = database.bookmarkDao()
|
||||
|
||||
var error by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
var source by mutableStateOf<Source?>(null)
|
||||
private set
|
||||
var itemID by mutableStateOf<String?>(null)
|
||||
@@ -121,14 +124,20 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
||||
viewModelScope.launch {
|
||||
if (title == null)
|
||||
title = withContext(Dispatchers.IO) {
|
||||
source.info(itemID)
|
||||
}.title
|
||||
kotlin.runCatching {
|
||||
source.info(itemID)
|
||||
}.getOrNull()
|
||||
}?.title
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
source.images(itemID)
|
||||
}.let { images ->
|
||||
kotlin.runCatching {
|
||||
source.images(itemID)
|
||||
}.onFailure {
|
||||
error = true
|
||||
}.getOrNull()
|
||||
}?.let { images ->
|
||||
this@ReaderViewModel.images = images
|
||||
|
||||
imageCount = images.size
|
||||
|
||||
@@ -149,7 +149,7 @@ fun View.show() {
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
class FileXImageSource(file: FileX): ImageSource {
|
||||
class FileXImageSource(val file: FileX): ImageSource {
|
||||
private val decoder = newBitmapRegionDecoder(file.inputStream()!!)
|
||||
|
||||
override val imageSize by lazy { Size(decoder.width.toFloat(), decoder.height.toFloat()) }
|
||||
|
||||
@@ -150,7 +150,7 @@ fun checkUpdate(context: Context, force: Boolean = false) {
|
||||
Preferences["update_download_id"] = it
|
||||
}
|
||||
}
|
||||
setNegativeButton(if (force) android.R.string.cancel else R.string.ignore_update) { _, _ ->
|
||||
setNegativeButton(if (force) android.R.string.cancel else R.string.ignore) { _, _ ->
|
||||
if (!force)
|
||||
preferences.edit()
|
||||
.putLong("ignore_update_until", System.currentTimeMillis() + 604800000)
|
||||
|
||||
Reference in New Issue
Block a user