Epub page turn animation (#23)
* Added realistic page turn animation option in epub pagination mode * Added volume key navigation support for paginated render mode and added page turn animation to "tap to change page" and "volume button to change page"
This commit is contained in:
parent
7d26e10c07
commit
1ce353ad44
6 changed files with 1203 additions and 926 deletions
|
|
@ -39,6 +39,7 @@ import androidx.annotation.RequiresApi
|
|||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInVertically
|
||||
|
|
@ -175,6 +176,17 @@ private const val AUTO_SCROLL_LOCKED_KEY = "auto_scroll_locked"
|
|||
private const val AUTO_SCROLL_USE_SLIDER_KEY = "auto_scroll_use_slider"
|
||||
private const val AUTO_SCROLL_MIN_SPEED_KEY = "auto_scroll_min_speed"
|
||||
private const val AUTO_SCROLL_MAX_SPEED_KEY = "auto_scroll_max_speed"
|
||||
private const val PAGE_TURN_ANIMATION_KEY = "page_turn_animation_enabled"
|
||||
|
||||
private fun savePageTurnAnimationSetting(context: Context, isEnabled: Boolean) {
|
||||
val prefs = context.getSharedPreferences("reader_prefs", Context.MODE_PRIVATE)
|
||||
prefs.edit { putBoolean(PAGE_TURN_ANIMATION_KEY, isEnabled) }
|
||||
}
|
||||
|
||||
private fun loadPageTurnAnimationSetting(context: Context): Boolean {
|
||||
val prefs = context.getSharedPreferences("reader_prefs", Context.MODE_PRIVATE)
|
||||
return prefs.getBoolean(PAGE_TURN_ANIMATION_KEY, false)
|
||||
}
|
||||
|
||||
private fun saveAutoScrollMinSpeed(context: Context, speed: Float) {
|
||||
val prefs = context.getSharedPreferences("reader_prefs", Context.MODE_PRIVATE)
|
||||
|
|
@ -309,6 +321,10 @@ fun EpubReaderHost(
|
|||
mutableStateOf(loadTapToNavigateSetting(context))
|
||||
}
|
||||
|
||||
var isPageTurnAnimationEnabled by remember {
|
||||
mutableStateOf(loadPageTurnAnimationSetting(context))
|
||||
}
|
||||
|
||||
val locatorConverter = remember(context) {
|
||||
LocatorConverter(
|
||||
bookCacheDao = BookCacheDatabase.getDatabase(context).bookCacheDao(),
|
||||
|
|
@ -1109,6 +1125,14 @@ fun EpubReaderHost(
|
|||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(paginatedPagerState.currentPage, currentRenderMode) {
|
||||
if (currentRenderMode == RenderMode.PAGINATED && volumeScrollEnabled) {
|
||||
delay(200)
|
||||
containerFocusRequester.requestFocus()
|
||||
Timber.d("Paginated: Page changed to ${paginatedPagerState.currentPage}, re-requesting focus for volume keys.")
|
||||
}
|
||||
}
|
||||
|
||||
BackHandler(enabled = true) {
|
||||
if (isPageSliderVisible) {
|
||||
isPageSliderVisible = false
|
||||
|
|
@ -1467,9 +1491,31 @@ fun EpubReaderHost(
|
|||
scope.launch {
|
||||
initialScrollTargetForChapter = target
|
||||
if (target == ChapterScrollPosition.START) currentScrollYPosition = 0
|
||||
|
||||
currentChapterIndex += offset
|
||||
}
|
||||
},
|
||||
onNextPage = {
|
||||
scope.launch {
|
||||
val pageCount = paginatedPagerState.pageCount
|
||||
if (pageCount > 0) {
|
||||
val targetPage = (paginatedPagerState.currentPage + 1).coerceAtMost(pageCount - 1)
|
||||
if (targetPage != paginatedPagerState.currentPage) {
|
||||
if (isPageTurnAnimationEnabled) {
|
||||
paginatedPagerState.animateScrollToPage(targetPage, animationSpec = tween(700))
|
||||
} else paginatedPagerState.scrollToPage(targetPage)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onPrevPage = {
|
||||
scope.launch {
|
||||
val targetPage = (paginatedPagerState.currentPage - 1).coerceAtLeast(0)
|
||||
if (targetPage != paginatedPagerState.currentPage) {
|
||||
if (isPageTurnAnimationEnabled) {
|
||||
paginatedPagerState.animateScrollToPage(targetPage, animationSpec = tween(700))
|
||||
} else paginatedPagerState.scrollToPage(targetPage)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
) {
|
||||
|
|
@ -2170,6 +2216,7 @@ fun EpubReaderHost(
|
|||
textAlign = currentTextAlign,
|
||||
activeHighlightPalette = currentHighlightPalette,
|
||||
onUpdatePalette = onUpdateHighlightPalette,
|
||||
isPageTurnAnimationEnabled = isPageTurnAnimationEnabled,
|
||||
ttsHighlightInfo = TtsHighlightInfo(
|
||||
text = ttsState.currentText ?: "",
|
||||
cfi = ttsState.sourceCfi ?: "",
|
||||
|
|
@ -2182,9 +2229,15 @@ fun EpubReaderHost(
|
|||
},
|
||||
onTap = { tapOffset ->
|
||||
Timber.d("PaginatedReaderScreen onTap called with offset: $tapOffset")
|
||||
|
||||
if (volumeScrollEnabled) {
|
||||
containerFocusRequester.requestFocus()
|
||||
}
|
||||
|
||||
if (tapOffset == null || !tapToNavigateEnabled) {
|
||||
Timber.d("Condition met: Toggling bars. tapOffset is null: ${tapOffset == null}, tapToNavigateEnabled: $tapToNavigateEnabled")
|
||||
focusManager.clearFocus()
|
||||
if (volumeScrollEnabled) containerFocusRequester.requestFocus()
|
||||
|
||||
if (showBars || showFormatAdjustmentBars) {
|
||||
showBars = false
|
||||
showFormatAdjustmentBars = false
|
||||
|
|
@ -2192,37 +2245,34 @@ fun EpubReaderHost(
|
|||
showBars = true
|
||||
}
|
||||
} else {
|
||||
// Handle zoned navigation since tapOffset is not null and the feature is enabled.
|
||||
Timber.d("Condition met: Handling zoned navigation.")
|
||||
val oneQuarterWidthPx = constraints.maxWidth / 4f
|
||||
when {
|
||||
tapOffset.x < oneQuarterWidthPx -> {
|
||||
// Left 25%: go to previous page
|
||||
Timber.d("Tapped left zone. Navigating to previous page.")
|
||||
scope.launch {
|
||||
val targetPage = (paginatedPagerState.currentPage - 1).coerceAtLeast(0)
|
||||
if (targetPage != paginatedPagerState.currentPage) {
|
||||
paginatedPagerState.scrollToPage(targetPage)
|
||||
if (isPageTurnAnimationEnabled) {
|
||||
paginatedPagerState.animateScrollToPage(targetPage, animationSpec = tween(700))
|
||||
} else paginatedPagerState.scrollToPage(targetPage)
|
||||
}
|
||||
}
|
||||
}
|
||||
tapOffset.x > (constraints.maxWidth - oneQuarterWidthPx) -> {
|
||||
// Right 25%: go to next page
|
||||
Timber.d("Tapped right zone. Navigating to next page.")
|
||||
scope.launch {
|
||||
val pageCount = paginatedPagerState.pageCount
|
||||
if (pageCount > 0) {
|
||||
val targetPage = (paginatedPagerState.currentPage + 1).coerceAtMost(pageCount - 1)
|
||||
if (targetPage != paginatedPagerState.currentPage) {
|
||||
paginatedPagerState.scrollToPage(targetPage)
|
||||
if (isPageTurnAnimationEnabled) {
|
||||
paginatedPagerState.animateScrollToPage(targetPage, animationSpec = tween(700))
|
||||
} else paginatedPagerState.scrollToPage(targetPage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// Middle 50%: toggle bars
|
||||
Timber.d("Tapped middle zone. Toggling bars.")
|
||||
focusManager.clearFocus()
|
||||
if (volumeScrollEnabled) containerFocusRequester.requestFocus()
|
||||
if (showBars || showFormatAdjustmentBars) {
|
||||
showBars = false
|
||||
showFormatAdjustmentBars = false
|
||||
|
|
@ -2635,6 +2685,7 @@ fun EpubReaderHost(
|
|||
isTtsActive = isTtsSessionActive,
|
||||
tapToNavigateEnabled = tapToNavigateEnabled,
|
||||
volumeScrollEnabled = volumeScrollEnabled,
|
||||
isPageTurnAnimationEnabled = isPageTurnAnimationEnabled,
|
||||
onNavigateBack = { triggerSaveAndExit() },
|
||||
onCloseSearch = {
|
||||
searchState.isSearchActive = false
|
||||
|
|
@ -2675,6 +2726,10 @@ fun EpubReaderHost(
|
|||
tapToNavigateEnabled = enabled
|
||||
saveTapToNavigateSetting(context, enabled)
|
||||
},
|
||||
onTogglePageTurnAnimation = { enabled ->
|
||||
isPageTurnAnimationEnabled = enabled
|
||||
savePageTurnAnimationSetting(context, enabled)
|
||||
},
|
||||
onToggleVolumeScroll = { enabled ->
|
||||
volumeScrollEnabled = enabled
|
||||
saveVolumeScrollSetting(context, enabled)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue