Crash fixes (#38)
* Updated animation specifications for UI controls and overlays to use a 200ms tween duration. * Implemented fallback file picker and improved CSS rule merging efficiency to fix OOM.
This commit is contained in:
parent
77a09c545f
commit
3a31e4fc7b
7 changed files with 133 additions and 58 deletions
|
|
@ -214,11 +214,31 @@ fun HomeScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val fallbackFilePickerLauncher = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.GetMultipleContents()
|
||||||
|
) { uris ->
|
||||||
|
if (isContextualModeActive) {
|
||||||
|
viewModel.clearContextualAction()
|
||||||
|
}
|
||||||
|
uris.forEach { uri ->
|
||||||
|
viewModel.onFileSelected(uri, isFromRecent = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val onSelectFileClick = {
|
val onSelectFileClick = {
|
||||||
if (isContextualModeActive) {
|
if (isContextualModeActive) {
|
||||||
viewModel.clearContextualAction()
|
viewModel.clearContextualAction()
|
||||||
}
|
}
|
||||||
pickFileLauncher.launch(arrayOf("*/*"))
|
try {
|
||||||
|
pickFileLauncher.launch(arrayOf("*/*"))
|
||||||
|
} catch (_: android.content.ActivityNotFoundException) {
|
||||||
|
Timber.w("OpenDocument picker failed. Falling back to GetMultipleContents.")
|
||||||
|
try {
|
||||||
|
fallbackFilePickerLauncher.launch("*/*")
|
||||||
|
} catch (_: android.content.ActivityNotFoundException) {
|
||||||
|
viewModel.showBanner("No file manager found. Please install a file manager app.", isError = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,7 @@ import com.aryan.reader.data.RecentFileItem
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
@ -156,7 +157,14 @@ fun LibraryScreen(
|
||||||
viewModel.setSyncedFolder(it)
|
viewModel.setSyncedFolder(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val onSelectSyncFolderClick = { pickFolderLauncher.launch(null) }
|
|
||||||
|
val onSelectSyncFolderClick = {
|
||||||
|
try {
|
||||||
|
pickFolderLauncher.launch(null)
|
||||||
|
} catch (_: android.content.ActivityNotFoundException) {
|
||||||
|
viewModel.showBanner("Your device doesn't support folder selection. You can still import files individually.", isError = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val pickFileLauncher = rememberFilePickerLauncher { uris ->
|
val pickFileLauncher = rememberFilePickerLauncher { uris ->
|
||||||
if (isContextualModeActive) {
|
if (isContextualModeActive) {
|
||||||
|
|
@ -167,11 +175,31 @@ fun LibraryScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val fallbackFilePickerLauncher = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.GetMultipleContents()
|
||||||
|
) { uris ->
|
||||||
|
if (isContextualModeActive) {
|
||||||
|
viewModel.clearContextualAction()
|
||||||
|
}
|
||||||
|
uris.forEach { uri ->
|
||||||
|
viewModel.onFileSelected(uri, isFromRecent = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val onSelectFileClick = {
|
val onSelectFileClick = {
|
||||||
if (isContextualModeActive) {
|
if (isContextualModeActive) {
|
||||||
viewModel.clearContextualAction()
|
viewModel.clearContextualAction()
|
||||||
}
|
}
|
||||||
pickFileLauncher.launch(arrayOf("*/*"))
|
try {
|
||||||
|
pickFileLauncher.launch(arrayOf("*/*"))
|
||||||
|
} catch (_: android.content.ActivityNotFoundException) {
|
||||||
|
Timber.w("OpenDocument picker failed. Falling back to GetMultipleContents.")
|
||||||
|
try {
|
||||||
|
fallbackFilePickerLauncher.launch("*/*")
|
||||||
|
} catch (_: android.content.ActivityNotFoundException) {
|
||||||
|
viewModel.showBanner("No file manager found. Please install a file manager app.", isError = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(pagerState) {
|
LaunchedEffect(pagerState) {
|
||||||
|
|
|
||||||
|
|
@ -156,8 +156,8 @@ fun EpubReaderTopBar(
|
||||||
) {
|
) {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = isVisible,
|
visible = isVisible,
|
||||||
enter = slideInVertically { -it } + fadeIn(),
|
enter = slideInVertically(animationSpec = tween(200)) { -it } + fadeIn(animationSpec = tween(200)),
|
||||||
exit = slideOutVertically { -it } + fadeOut(),
|
exit = slideOutVertically(animationSpec = tween(200)) { -it } + fadeOut(animationSpec = tween(200)),
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
|
|
@ -347,8 +347,8 @@ fun EpubReaderBottomBar(
|
||||||
) {
|
) {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = isVisible,
|
visible = isVisible,
|
||||||
enter = slideInVertically { it } + fadeIn(),
|
enter = slideInVertically(animationSpec = tween(200)) { it } + fadeIn(animationSpec = tween(200)),
|
||||||
exit = slideOutVertically { it } + fadeOut(),
|
exit = slideOutVertically(animationSpec = tween(200)) { it } + fadeOut(animationSpec = tween(200)),
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
|
|
@ -456,11 +456,10 @@ fun EpubReaderPageSlider(
|
||||||
) {
|
) {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = isVisible,
|
visible = isVisible,
|
||||||
enter = slideInVertically { fullHeight -> fullHeight } + fadeIn(),
|
enter = slideInVertically(animationSpec = tween(200)) { fullHeight -> fullHeight } + fadeIn(animationSpec = tween(200)),
|
||||||
exit = slideOutVertically { fullHeight -> fullHeight } + fadeOut()
|
exit = slideOutVertically(animationSpec = tween(200)) { fullHeight -> fullHeight } + fadeOut(animationSpec = tween(200))
|
||||||
) {
|
) {
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
// Dismiss area
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
@ -470,14 +469,6 @@ fun EpubReaderPageSlider(
|
||||||
) { onClose() }
|
) { onClose() }
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fast scrub overlay
|
|
||||||
// Note: In the refactor, we rely on the parent or this logic to determine "isFastScrubbing".
|
|
||||||
// Since `isFastScrubbing` was state in the parent, we'll implement a local check or just show it if `isVisible`.
|
|
||||||
// Ideally, the parent handles the "Scrubbing Animation" separately, but let's bundle it here for simplicity.
|
|
||||||
// For now, we only show the static overlay logic.
|
|
||||||
// If we want the big center indicator, we can render it based on interaction state here.
|
|
||||||
|
|
||||||
// Top back button
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onClose,
|
onClick = onClose,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
|
||||||
|
|
@ -2667,8 +2667,8 @@ fun EpubReaderHost(
|
||||||
// Page Info Bar (Vertical)
|
// Page Info Bar (Vertical)
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = renderMode == RenderMode.VERTICAL_SCROLL && !showBars,
|
visible = renderMode == RenderMode.VERTICAL_SCROLL && !showBars,
|
||||||
enter = fadeIn(),
|
enter = fadeIn(animationSpec = tween(200)),
|
||||||
exit = fadeOut(),
|
exit = fadeOut(animationSpec = tween(200)),
|
||||||
modifier = Modifier.align(Alignment.BottomCenter)
|
modifier = Modifier.align(Alignment.BottomCenter)
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
|
|
@ -2710,8 +2710,8 @@ fun EpubReaderHost(
|
||||||
// Page Info Bar (Paginated)
|
// Page Info Bar (Paginated)
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = renderMode == RenderMode.PAGINATED && paginator != null && !showBars && paginatedPagerState.pageCount > 0,
|
visible = renderMode == RenderMode.PAGINATED && paginator != null && !showBars && paginatedPagerState.pageCount > 0,
|
||||||
enter = fadeIn(),
|
enter = fadeIn(animationSpec = tween(200)),
|
||||||
exit = fadeOut(),
|
exit = fadeOut(animationSpec = tween(200)),
|
||||||
modifier = Modifier.align(Alignment.BottomCenter)
|
modifier = Modifier.align(Alignment.BottomCenter)
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
|
|
@ -2922,8 +2922,8 @@ fun EpubReaderHost(
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = isAutoScrollControlsVisible,
|
visible = isAutoScrollControlsVisible,
|
||||||
enter = slideInVertically { it } + fadeIn(),
|
enter = slideInVertically(animationSpec = tween(200)) { it } + fadeIn(animationSpec = tween(200)),
|
||||||
exit = slideOutVertically { it } + fadeOut(),
|
exit = slideOutVertically(animationSpec = tween(200)) { it } + fadeOut(animationSpec = tween(200)),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(BiasAlignment(alignmentBias, 1f))
|
.align(BiasAlignment(alignmentBias, 1f))
|
||||||
.padding(bottom = autoScrollPadding)
|
.padding(bottom = autoScrollPadding)
|
||||||
|
|
|
||||||
|
|
@ -53,29 +53,52 @@ class LocatorConverter(
|
||||||
try {
|
try {
|
||||||
val chapter = book.chapters.getOrNull(chapterIndex) ?: return@withContext null
|
val chapter = book.chapters.getOrNull(chapterIndex) ?: return@withContext null
|
||||||
|
|
||||||
// 1. Parse CSS from the book
|
val mergedByTag = mutableMapOf<String, MutableList<CssRule>>()
|
||||||
var parsingCssRules = OptimizedCssRules()
|
val mergedByClass = mutableMapOf<String, MutableList<CssRule>>()
|
||||||
|
val mergedById = mutableMapOf<String, MutableList<CssRule>>()
|
||||||
|
val mergedOtherComplex = mutableListOf<CssRule>()
|
||||||
|
|
||||||
val density = Density(context)
|
val density = Density(context)
|
||||||
val displayMetrics = context.resources.displayMetrics
|
val displayMetrics = context.resources.displayMetrics
|
||||||
val constraints = Constraints(maxWidth = displayMetrics.widthPixels, maxHeight = displayMetrics.heightPixels)
|
val constraints = Constraints(maxWidth = displayMetrics.widthPixels, maxHeight = displayMetrics.heightPixels)
|
||||||
|
|
||||||
|
fun aggregateRules(
|
||||||
|
target: MutableMap<String, MutableList<CssRule>>,
|
||||||
|
source: Map<String, List<CssRule>>
|
||||||
|
) {
|
||||||
|
source.forEach { (k, v) ->
|
||||||
|
target.getOrPut(k) { mutableListOf() }.addAll(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
book.css.forEach { (path, content) ->
|
book.css.forEach { (path, content) ->
|
||||||
val bookCssResult = CssParser.parse(
|
val bookCssResult = CssParser.parse(
|
||||||
cssContent = content,
|
cssContent = content,
|
||||||
cssPath = path,
|
cssPath = path,
|
||||||
baseFontSizeSp = 16f, // A reasonable default for non-rendering parsing
|
baseFontSizeSp = 16f,
|
||||||
density = density.density,
|
density = density.density,
|
||||||
constraints = constraints,
|
constraints = constraints,
|
||||||
isDarkTheme = false
|
isDarkTheme = false
|
||||||
)
|
)
|
||||||
parsingCssRules = parsingCssRules.merge(bookCssResult.rules)
|
|
||||||
|
val rules = bookCssResult.rules
|
||||||
|
aggregateRules(mergedByTag, rules.byTag)
|
||||||
|
aggregateRules(mergedByClass, rules.byClass)
|
||||||
|
aggregateRules(mergedById, rules.byId)
|
||||||
|
mergedOtherComplex.addAll(rules.otherComplex)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Parse HTML to SemanticBlocks
|
val parsingCssRules = OptimizedCssRules(
|
||||||
|
byTag = mergedByTag,
|
||||||
|
byClass = mergedByClass,
|
||||||
|
byId = mergedById,
|
||||||
|
otherComplex = mergedOtherComplex
|
||||||
|
)
|
||||||
|
|
||||||
val semanticBlocks = htmlToSemanticBlocks(
|
val semanticBlocks = htmlToSemanticBlocks(
|
||||||
html = chapter.htmlContent,
|
html = chapter.htmlContent,
|
||||||
cssRules = parsingCssRules,
|
cssRules = parsingCssRules,
|
||||||
textStyle = TextStyle(), // Not used for rendering, so a default is fine
|
textStyle = TextStyle(),
|
||||||
chapterAbsPath = chapter.absPath,
|
chapterAbsPath = chapter.absPath,
|
||||||
extractionBasePath = book.extractionBasePath,
|
extractionBasePath = book.extractionBasePath,
|
||||||
density = density,
|
density = density,
|
||||||
|
|
@ -83,13 +106,12 @@ class LocatorConverter(
|
||||||
constraints = constraints
|
constraints = constraints
|
||||||
)
|
)
|
||||||
|
|
||||||
// 3. Serialize and cache the result
|
|
||||||
val protoBytes = proto.encodeToByteArray(semanticBlocks)
|
val protoBytes = proto.encodeToByteArray(semanticBlocks)
|
||||||
val newCacheEntry = ProcessedChapter(
|
val newCacheEntry = ProcessedChapter(
|
||||||
bookId = book.title,
|
bookId = book.title,
|
||||||
chapterIndex = chapterIndex,
|
chapterIndex = chapterIndex,
|
||||||
contentBlocksProto = protoBytes,
|
contentBlocksProto = protoBytes,
|
||||||
estimatedPageCount = 0 // Page count is not relevant for locator conversion
|
estimatedPageCount = 0
|
||||||
)
|
)
|
||||||
bookCacheDao.insertProcessedChapters(listOf(newCacheEntry))
|
bookCacheDao.insertProcessedChapters(listOf(newCacheEntry))
|
||||||
Timber.i("On-demand processing SUCCESS for chapter $chapterIndex.")
|
Timber.i("On-demand processing SUCCESS for chapter $chapterIndex.")
|
||||||
|
|
|
||||||
|
|
@ -354,21 +354,31 @@ data class OptimizedCssRules(
|
||||||
@ProtoNumber(4) val otherComplex: List<CssRule> = emptyList()
|
@ProtoNumber(4) val otherComplex: List<CssRule> = emptyList()
|
||||||
) {
|
) {
|
||||||
fun merge(other: OptimizedCssRules): OptimizedCssRules {
|
fun merge(other: OptimizedCssRules): OptimizedCssRules {
|
||||||
val mergedByTag = (this.byTag.asSequence() + other.byTag.asSequence())
|
fun mergeMap(
|
||||||
.groupBy({ it.key }, { it.value })
|
m1: Map<String, List<CssRule>>,
|
||||||
.mapValues { (_, values) -> values.flatten() }
|
m2: Map<String, List<CssRule>>
|
||||||
|
): Map<String, List<CssRule>> {
|
||||||
|
if (m1.isEmpty()) return m2
|
||||||
|
if (m2.isEmpty()) return m1
|
||||||
|
|
||||||
val mergedByClass = (this.byClass.asSequence() + other.byClass.asSequence())
|
val result = LinkedHashMap(m1)
|
||||||
.groupBy({ it.key }, { it.value })
|
for ((key, value) in m2) {
|
||||||
.mapValues { (_, values) -> values.flatten() }
|
val existing = result[key]
|
||||||
|
if (existing != null) {
|
||||||
|
result[key] = existing + value
|
||||||
|
} else {
|
||||||
|
result[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
val mergedById = (this.byId.asSequence() + other.byId.asSequence())
|
return OptimizedCssRules(
|
||||||
.groupBy({ it.key }, { it.value })
|
byTag = mergeMap(this.byTag, other.byTag),
|
||||||
.mapValues { (_, values) -> values.flatten() }
|
byClass = mergeMap(this.byClass, other.byClass),
|
||||||
|
byId = mergeMap(this.byId, other.byId),
|
||||||
val mergedOtherComplex = this.otherComplex + other.otherComplex
|
otherComplex = this.otherComplex + other.otherComplex
|
||||||
|
)
|
||||||
return OptimizedCssRules(mergedByTag, mergedByClass, mergedById, mergedOtherComplex)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toFlatList(): List<CssRule> {
|
fun toFlatList(): List<CssRule> {
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.core.Animatable
|
import androidx.compose.animation.core.Animatable
|
||||||
import androidx.compose.animation.core.animateDpAsState
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.slideInVertically
|
import androidx.compose.animation.slideInVertically
|
||||||
|
|
@ -4144,9 +4145,10 @@ fun PdfViewerScreen(
|
||||||
// Custom Top Bar
|
// Custom Top Bar
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = showStandardBars,
|
visible = showStandardBars,
|
||||||
enter = slideInVertically { fullHeight -> -fullHeight } + fadeIn(),
|
enter = slideInVertically(animationSpec = tween(200)) { fullHeight -> -fullHeight } + fadeIn(animationSpec = tween(200)),
|
||||||
exit = slideOutVertically { fullHeight -> -fullHeight } + fadeOut(),
|
exit = slideOutVertically(animationSpec = tween(200)) { fullHeight -> -fullHeight } + fadeOut(animationSpec = tween(200)),
|
||||||
modifier = Modifier.align(Alignment.TopCenter)) {
|
modifier = Modifier.align(Alignment.TopCenter)
|
||||||
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|
@ -4469,8 +4471,8 @@ fun PdfViewerScreen(
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = showStandardBars && isReflowingThisBook,
|
visible = showStandardBars && isReflowingThisBook,
|
||||||
enter = fadeIn() + slideInVertically(),
|
enter = fadeIn(animationSpec = tween(200)) + slideInVertically(animationSpec = tween(200)),
|
||||||
exit = fadeOut() + slideOutVertically(),
|
exit = fadeOut(animationSpec = tween(200)) + slideOutVertically(animationSpec = tween(200)),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.TopCenter)
|
.align(Alignment.TopCenter)
|
||||||
.padding(top = 56.dp)
|
.padding(top = 56.dp)
|
||||||
|
|
@ -4579,11 +4581,12 @@ fun PdfViewerScreen(
|
||||||
// Search Navigation Controls
|
// Search Navigation Controls
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = searchState.isSearchActive && !searchState.showSearchResultsPanel && smartSearchResult != null,
|
visible = searchState.isSearchActive && !searchState.showSearchResultsPanel && smartSearchResult != null,
|
||||||
enter = slideInVertically { it } + fadeIn(),
|
enter = slideInVertically(animationSpec = tween(200)) { it } + fadeIn(animationSpec = tween(200)),
|
||||||
exit = slideOutVertically { it } + fadeOut(),
|
exit = slideOutVertically(animationSpec = tween(200)) { it } + fadeOut(animationSpec = tween(200)),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.BottomCenter)
|
.align(Alignment.BottomCenter)
|
||||||
.padding(bottom = 24.dp)) {
|
.padding(bottom = 24.dp)
|
||||||
|
) {
|
||||||
val currentResult = currentPdfSearchResult
|
val currentResult = currentPdfSearchResult
|
||||||
val searchData = smartSearchResult
|
val searchData = smartSearchResult
|
||||||
|
|
||||||
|
|
@ -4691,9 +4694,10 @@ fun PdfViewerScreen(
|
||||||
// Bottom Bar
|
// Bottom Bar
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = showStandardBars && !searchState.isSearchActive,
|
visible = showStandardBars && !searchState.isSearchActive,
|
||||||
enter = slideInVertically { fullHeight -> fullHeight } + fadeIn(),
|
enter = slideInVertically(animationSpec = tween(200)) { fullHeight -> fullHeight } + fadeIn(animationSpec = tween(200)),
|
||||||
exit = slideOutVertically { fullHeight -> fullHeight } + fadeOut(),
|
exit = slideOutVertically(animationSpec = tween(200)) { fullHeight -> fullHeight } + fadeOut(animationSpec = tween(200)),
|
||||||
modifier = Modifier.align(Alignment.BottomCenter)) {
|
modifier = Modifier.align(Alignment.BottomCenter)
|
||||||
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|
@ -5925,8 +5929,8 @@ fun PdfViewerScreen(
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = isAutoScrollControlsVisible,
|
visible = isAutoScrollControlsVisible,
|
||||||
enter = slideInVertically { it } + fadeIn(),
|
enter = slideInVertically(animationSpec = tween(200)) { it } + fadeIn(animationSpec = tween(200)),
|
||||||
exit = slideOutVertically { it } + fadeOut(),
|
exit = slideOutVertically(animationSpec = tween(200)) { it } + fadeOut(animationSpec = tween(200)),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(BiasAlignment(alignmentBias, 1f))
|
.align(BiasAlignment(alignmentBias, 1f))
|
||||||
.padding(bottom = autoScrollPadding)
|
.padding(bottom = autoScrollPadding)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue