phase 0: fork Episteme, rename package to org.dueattendant149.bookreader
- Cloned from Aryan-Raj3112/episteme (AGPL-3.0) - Package: com.aryan.reader → org.dueattendant149.bookreader - Application ID: org.dueattendant149.bookreader - Added AGENTS.md with migration plan - Upstream: github.com/Aryan-Raj3112/episteme - Origin: git.dueattendant149.org/Atte149/book-reader
This commit is contained in:
parent
e615128a23
commit
5f64f3d722
631 changed files with 3082 additions and 3006 deletions
|
|
@ -0,0 +1,268 @@
|
|||
package org.dueattendant149.bookreader
|
||||
|
||||
import android.content.Context
|
||||
import android.view.Window
|
||||
import android.view.WindowManager
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
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.filled.Add
|
||||
import androidx.compose.material.icons.filled.Remove
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.edit
|
||||
import org.dueattendant149.bookreader.shared.ui.ReaderMinimalSlider
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
private const val READER_PREFS_NAME = "reader_prefs"
|
||||
private const val PREF_READER_BRIGHTNESS_USE_SYSTEM = "reader_brightness_use_system"
|
||||
private const val PREF_READER_BRIGHTNESS_VALUE = "reader_brightness_value"
|
||||
private const val DEFAULT_CUSTOM_BRIGHTNESS = 0.75f
|
||||
private const val MIN_CUSTOM_BRIGHTNESS_PERCENT = 1
|
||||
private const val MAX_CUSTOM_BRIGHTNESS_PERCENT = 100
|
||||
private const val CUSTOM_BRIGHTNESS_STEP_PERCENT = 1
|
||||
private const val MIN_CUSTOM_BRIGHTNESS = 0.01f
|
||||
|
||||
data class ReaderBrightnessSettings(
|
||||
val useSystemBrightness: Boolean = true,
|
||||
val customBrightness: Float = DEFAULT_CUSTOM_BRIGHTNESS
|
||||
) {
|
||||
val safeCustomBrightness: Float
|
||||
get() = normalizeReaderBrightness(customBrightness)
|
||||
}
|
||||
|
||||
internal fun normalizeReaderBrightness(brightness: Float): Float {
|
||||
val percent = (brightness * 100f).roundToInt()
|
||||
.coerceIn(MIN_CUSTOM_BRIGHTNESS_PERCENT, MAX_CUSTOM_BRIGHTNESS_PERCENT)
|
||||
return percent / 100f
|
||||
}
|
||||
|
||||
internal fun stepReaderBrightness(brightness: Float, percentDelta: Int): Float {
|
||||
val currentPercent = (normalizeReaderBrightness(brightness) * 100f).roundToInt()
|
||||
val nextPercent = (currentPercent + percentDelta)
|
||||
.coerceIn(MIN_CUSTOM_BRIGHTNESS_PERCENT, MAX_CUSTOM_BRIGHTNESS_PERCENT)
|
||||
return nextPercent / 100f
|
||||
}
|
||||
|
||||
fun loadReaderBrightnessSettings(context: Context): ReaderBrightnessSettings {
|
||||
val prefs = context.getSharedPreferences(READER_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
return ReaderBrightnessSettings(
|
||||
useSystemBrightness = prefs.getBoolean(PREF_READER_BRIGHTNESS_USE_SYSTEM, true),
|
||||
customBrightness = prefs.getFloat(PREF_READER_BRIGHTNESS_VALUE, DEFAULT_CUSTOM_BRIGHTNESS)
|
||||
.let(::normalizeReaderBrightness)
|
||||
)
|
||||
}
|
||||
|
||||
fun saveReaderBrightnessSettings(context: Context, settings: ReaderBrightnessSettings) {
|
||||
context.getSharedPreferences(READER_PREFS_NAME, Context.MODE_PRIVATE).edit {
|
||||
putBoolean(PREF_READER_BRIGHTNESS_USE_SYSTEM, settings.useSystemBrightness)
|
||||
putFloat(PREF_READER_BRIGHTNESS_VALUE, settings.safeCustomBrightness)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ReaderBrightnessEffect(
|
||||
window: Window?,
|
||||
settings: ReaderBrightnessSettings
|
||||
) {
|
||||
DisposableEffect(window) {
|
||||
val originalBrightness = window?.attributes?.screenBrightness
|
||||
?: WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
|
||||
onDispose {
|
||||
window?.setReaderBrightness(originalBrightness)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(window, settings) {
|
||||
val brightness = if (settings.useSystemBrightness) {
|
||||
WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
|
||||
} else {
|
||||
settings.safeCustomBrightness
|
||||
}
|
||||
window?.setReaderBrightness(brightness)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ReaderBrightnessSheet(
|
||||
settings: ReaderBrightnessSettings,
|
||||
onSettingsChange: (ReaderBrightnessSettings) -> Unit,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = onDismiss,
|
||||
sheetState = sheetState
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 24.dp, vertical = 8.dp)
|
||||
.padding(bottom = 32.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.reader_brightness_title),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(stringResource(R.string.action_done))
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalDivider()
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = stringResource(R.string.reader_brightness_system),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.reader_brightness_system_desc),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
Switch(
|
||||
checked = settings.useSystemBrightness,
|
||||
onCheckedChange = { useSystem ->
|
||||
onSettingsChange(settings.copy(useSystemBrightness = useSystem))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.reader_brightness_custom),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
Text(
|
||||
text = stringResource(
|
||||
R.string.reader_brightness_percent,
|
||||
(settings.safeCustomBrightness * 100f).roundToInt()
|
||||
),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
ReaderBrightnessControl(
|
||||
settings = settings,
|
||||
onSettingsChange = onSettingsChange
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.reader_brightness_custom_desc),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(4.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ReaderBrightnessControl(
|
||||
settings: ReaderBrightnessSettings,
|
||||
onSettingsChange: (ReaderBrightnessSettings) -> Unit
|
||||
) {
|
||||
val brightness = settings.safeCustomBrightness
|
||||
val canDecrease = brightness > MIN_CUSTOM_BRIGHTNESS
|
||||
val canIncrease = brightness < 1f
|
||||
|
||||
fun updateBrightness(value: Float) {
|
||||
onSettingsChange(
|
||||
settings.copy(
|
||||
useSystemBrightness = false,
|
||||
customBrightness = normalizeReaderBrightness(value)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
updateBrightness(stepReaderBrightness(brightness, -CUSTOM_BRIGHTNESS_STEP_PERCENT))
|
||||
},
|
||||
enabled = canDecrease,
|
||||
modifier = Modifier.size(36.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Remove,
|
||||
contentDescription = stringResource(R.string.content_desc_decrease),
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
}
|
||||
ReaderMinimalSlider(
|
||||
value = brightness,
|
||||
onValueChange = ::updateBrightness,
|
||||
valueRange = MIN_CUSTOM_BRIGHTNESS..1f,
|
||||
activeColor = MaterialTheme.colorScheme.primary,
|
||||
inactiveColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f),
|
||||
thumbColor = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
IconButton(
|
||||
onClick = {
|
||||
updateBrightness(stepReaderBrightness(brightness, CUSTOM_BRIGHTNESS_STEP_PERCENT))
|
||||
},
|
||||
enabled = canIncrease,
|
||||
modifier = Modifier.size(36.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Add,
|
||||
contentDescription = stringResource(R.string.content_desc_increase),
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Window.setReaderBrightness(brightness: Float) {
|
||||
attributes = attributes.apply {
|
||||
screenBrightness = brightness
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue