book-reader/app/src/main/java/com/aryan/reader/epubreader/EpubReaderVertical.kt
Aryan c6363f3061
Update readme (#3)
docs: Revamp README and add APK naming convention

This commit introduces a complete overhaul of the `README.md` to improve project presentation. Key changes include:
*   Adds a new header with the app icon and a Google Play badge.
*   Updates the feature graphic and includes a note distinguishing the OSS version from the Play Store version.
*   Refines the "Open Source Libraries" section to be more structured.
*   Moves the main preview image into the `docs` directory.

Additionally, the `app/build.gradle.kts` file is updated to configure a standardized naming convention for output APK files, including flavor, version, and build type.

* refactor copyright headers and fix README icon alignment

This commit introduces two main changes across the codebase:

The `spotless/copyright.kt` template have been updated for consistency:
*   The author name is simplified from "Episteme Authors" to "Episteme".
*   The contact email prefix is shortened from "Electronic mail:" to "mail:".

Second, the `README.md` file is adjusted to correct the vertical alignment of the app icon with the project title.
2026-02-25 21:30:28 +05:30

140 lines
No EOL
4.9 KiB
Kotlin

/*
* Episteme Reader - A native Android document reader.
* Copyright (C) 2026 Episteme
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* mail: epistemereader@gmail.com
*/
package com.aryan.reader.epubreader
import timber.log.Timber
import android.webkit.JavascriptInterface
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import org.json.JSONObject
import kotlin.math.min
val DRAG_TO_CHANGE_CHAPTER_THRESHOLD_DP = 100.dp
val PAGE_INFO_BAR_HEIGHT = 25.dp
@Composable
fun ChapterChangeIndicator(
text: String,
progress: Float,
isPullingDown: Boolean,
modifier: Modifier = Modifier
) {
val alpha = min(1f, progress * 1.5f)
if (alpha > 0.1f) {
Surface(
modifier = modifier
.fillMaxWidth()
.alpha(alpha)
.padding(horizontal = 16.dp),
shape = MaterialTheme.shapes.medium,
color = MaterialTheme.colorScheme.inverseSurface.copy(alpha = 0.5f),
tonalElevation = 4.dp
) {
Column(
modifier = Modifier.padding(vertical = 8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
imageVector = if (isPullingDown) Icons.AutoMirrored.Filled.ArrowBack else Icons.AutoMirrored.Filled.ArrowForward,
contentDescription = null,
tint = MaterialTheme.colorScheme.inverseOnSurface,
modifier = Modifier.size(20.dp * min(1f, progress + 0.2f))
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = if (progress >= 1.0f) text else "Pull further... (${(progress * 100).toInt()}%)",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.inverseOnSurface,
textAlign = TextAlign.Center
)
}
}
}
}
enum class ChapterScrollPosition {
START, END
}
data class SelectionRect(
val x: Float,
val y: Float,
val width: Float,
val height: Float,
val text: String
)
interface TextSelectionListener {
fun onTextSelected(rect: SelectionRect)
fun onSelectionCleared()
}
@Suppress("unused")
class TextSelectionJsInterface(private val listener: TextSelectionListener) {
@JavascriptInterface
fun onTextSelected(rectJson: String) {
try {
val json = JSONObject(rectJson)
val rect = SelectionRect(
x = json.getDouble("x").toFloat(),
y = json.getDouble("y").toFloat(),
width = json.getDouble("width").toFloat(),
height = json.getDouble("height").toFloat(),
text = json.getString("text")
)
listener.onTextSelected(rect)
} catch (e: Exception) {
Timber.e(e, "Error parsing selection rect JSON")
}
}
@JavascriptInterface
fun onSelectionCleared() {
listener.onSelectionCleared()
}
}
class PageInfoBridge(
private val onUpdate: (scrollY: Int, scrollHeight: Int, clientHeight: Int, activeFragmentId: String?) -> Unit
) {
@JavascriptInterface
fun updateScrollState(scrollY: Int, scrollHeight: Int, clientHeight: Int, activeFragmentId: String?) {
val fragment = if (activeFragmentId == "null" || activeFragmentId.isNullOrBlank()) null else activeFragmentId
Timber.tag("FRAG_NAV_DEBUG").d("Bridge received fragmentId: $fragment")
onUpdate(scrollY, scrollHeight, clientHeight, fragment)
}
}