Added screen position saving in the epub reader

This commit is contained in:
sNice/Python
2026-03-22 18:33:20 -05:00
parent 1e7538184d
commit 895f129b81
8 changed files with 46 additions and 37 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-03-22T22:43:40.813806872Z">
<DropdownSelection timestamp="2026-03-22T23:16:51.147475690Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=42011JEKB06640" />
+1
View File
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
-1
View File
@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
+1
View File
@@ -42,6 +42,7 @@ android {
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
Binary file not shown.
-21
View File
@@ -1,21 +0,0 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "net.pythonsden.ao3_",
"variantName": "debug",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 1,
"versionName": "1.0",
"outputFile": "app-debug.apk"
}
],
"elementType": "File",
"minSdkVersionForDexing": 24
}
@@ -72,6 +72,9 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.FileProvider
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.navigation.NavController
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavType
@@ -165,7 +168,8 @@ fun MainScreen() {
if (lastRoute == "reader/{filePath}") {
val lastEpubPath = prefs.getString("last_epub_path", null)
if (lastEpubPath != null) {
navController.navigate("reader/$lastEpubPath")
val encoded = URLEncoder.encode(lastEpubPath, StandardCharsets.UTF_8.toString())
navController.navigate("reader/$encoded")
}
} else if (!isOfflineMode || lastRoute == Screen.Downloads.route) {
navController.navigate(lastRoute) {
@@ -678,6 +682,7 @@ fun DownloadsScreen(navController: NavController, modifier: Modifier = Modifier)
@Composable
fun EpubReaderScreen(file: File, navController: NavController) {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
val prefs = remember { context.getSharedPreferences("ao3_prefs", Context.MODE_PRIVATE) }
val scrollKey = "scroll_${file.absolutePath}"
@@ -685,18 +690,30 @@ fun EpubReaderScreen(file: File, navController: NavController) {
var title by remember { mutableStateOf("Reading...") }
var webViewRef by remember { mutableStateOf<WebView?>(null) }
// Save scroll position
DisposableEffect(file.absolutePath) {
onDispose {
webViewRef?.let { wv ->
val scrollY = wv.scrollY
if (scrollY > 0) {
prefs.edit().putInt(scrollKey, scrollY).apply()
}
// Helper function to save scroll
fun saveScroll(wv: WebView?) {
wv?.let {
val scrollY = it.scrollY
if (scrollY > 0) {
prefs.edit().putInt(scrollKey, scrollY).apply()
}
}
}
// Save scroll position on pause or dispose
DisposableEffect(lifecycleOwner, file.absolutePath, webViewRef) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_PAUSE) {
saveScroll(webViewRef)
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
saveScroll(webViewRef)
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
LaunchedEffect(file) {
try {
val book: Book = EpubReader().readEpub(FileInputStream(file))
@@ -759,17 +776,27 @@ fun EpubReaderScreen(file: File, navController: NavController) {
super.onPageFinished(view, url)
val lastScroll = prefs.getInt(scrollKey, 0)
if (lastScroll > 0) {
view?.post {
view.scrollTo(0, lastScroll)
}
// Try scrolling after several delays to ensure layout is done
view?.postDelayed({ view.scrollTo(0, lastScroll) }, 100)
view?.postDelayed({ view.scrollTo(0, lastScroll) }, 300)
view?.postDelayed({ view.scrollTo(0, lastScroll) }, 600)
}
}
}
setOnScrollChangeListener { _, _, _, scrollY, _ ->
if (scrollY > 0) {
prefs.edit().putInt(scrollKey, scrollY).apply()
}
}
webViewRef = this
}
},
update = { webView ->
webView.loadDataWithBaseURL(null, htmlContent, "text/html", "UTF-8", null)
// Avoid reloading if content is the same
if (webView.tag != htmlContent && htmlContent.isNotBlank()) {
webView.loadDataWithBaseURL(null, htmlContent, "text/html", "UTF-8", null)
webView.tag = htmlContent
}
}
)
}
@@ -786,4 +813,4 @@ fun openFile(context: Context, file: File) {
} catch (e: Exception) {
Toast.makeText(context, "No app found to open this file", Toast.LENGTH_SHORT).show()
}
}
}
+2
View File
@@ -5,6 +5,7 @@ junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
lifecycleRuntimeKtx = "2.6.1"
lifecycleRuntimeCompose = "2.6.1"
activityCompose = "1.8.0"
kotlin = "2.2.10"
composeBom = "2024.09.00"
@@ -17,6 +18,7 @@ junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycleRuntimeCompose" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }