feat(ui): Phase 3 Myne-style Material You 3 redesign

- Add Shapes.kt with rounded shape scheme (8-32dp corners) wired into AppTheme.
- BookshelfLibraryScreen: round cards (shapes.large), surfaceContainerLow, CenterAlignedTopAppBar, onSurfaceVariant labels, larger padding.
- ServerSettingsScreen: section cards with round shapes, scrollable, CenterAlignedTopAppBar.
- Dynamic color already enabled via AppTheme (Android 12+ dynamicColorScheme + materialkolor seed).
- ':app:assembleOssDebug' passes.
This commit is contained in:
Atte149 2026-06-24 17:05:33 +03:00
parent d0ceeef24f
commit 1c0eca423d
4 changed files with 130 additions and 61 deletions

View file

@ -0,0 +1,16 @@
package org.dueattendant149.bookreader.ui.theme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp
/**
* Myne-style Material You 3 shape scheme: generous rounded corners.
*/
val AppShapes = Shapes(
extraSmall = RoundedCornerShape(8.dp),
small = RoundedCornerShape(12.dp),
medium = RoundedCornerShape(16.dp),
large = RoundedCornerShape(24.dp),
extraLarge = RoundedCornerShape(32.dp),
)

View file

@ -178,6 +178,7 @@ fun AppTheme(
MaterialTheme(
colorScheme = finalColorScheme,
typography = appFontFamily?.let { AppTypography.withAppFontFamily(it) } ?: AppTypography,
shapes = AppShapes,
content = content
)
}

View file

@ -1,21 +1,26 @@
package org.dueattendant149.bookreader.bookshelf
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -23,7 +28,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@ -34,13 +38,13 @@ fun ServerSettingsScreen(
onBackClick: () -> Unit,
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
var bookshelfUrl by remember(uiState.bookshelfUrl) { androidx.compose.runtime.mutableStateOf(uiState.bookshelfUrl) }
var absUrl by remember(uiState.absUrl) { androidx.compose.runtime.mutableStateOf(uiState.absUrl) }
var absToken by remember(uiState.absToken) { androidx.compose.runtime.mutableStateOf(uiState.absToken) }
var bookshelfUrl by remember(uiState.bookshelfUrl) { mutableStateOf(uiState.bookshelfUrl) }
var absUrl by remember(uiState.absUrl) { mutableStateOf(uiState.absUrl) }
var absToken by remember(uiState.absToken) { mutableStateOf(uiState.absToken) }
Scaffold(
topBar = {
TopAppBar(
androidx.compose.material3.CenterAlignedTopAppBar(
title = { Text("Server settings") },
navigationIcon = {
IconButton(onClick = onBackClick) {
@ -54,10 +58,11 @@ fun ServerSettingsScreen(
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(16.dp)
.padding(horizontal = 16.dp, vertical = 8.dp)
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text("Bookshelf API")
Spacer(modifier = Modifier.height(8.dp))
SettingsSectionCard(title = "Bookshelf API") {
OutlinedTextField(
value = bookshelfUrl,
onValueChange = {
@ -68,11 +73,9 @@ fun ServerSettingsScreen(
singleLine = true,
modifier = Modifier.fillMaxWidth()
)
}
Spacer(modifier = Modifier.height(24.dp))
Text("Audiobookshelf")
Spacer(modifier = Modifier.height(8.dp))
SettingsSectionCard(title = "Audiobookshelf") {
OutlinedTextField(
value = absUrl,
onValueChange = {
@ -83,7 +86,7 @@ fun ServerSettingsScreen(
singleLine = true,
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(8.dp))
Spacer(modifier = Modifier.height(12.dp))
OutlinedTextField(
value = absToken,
onValueChange = {
@ -95,20 +98,47 @@ fun ServerSettingsScreen(
visualTransformation = PasswordVisualTransformation(),
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(24.dp))
}
Button(
onClick = viewModel::save,
modifier = Modifier.fillMaxWidth()
modifier = Modifier.fillMaxWidth(),
shape = MaterialTheme.shapes.large
) {
Text("Save")
}
if (uiState.saved) {
Spacer(modifier = Modifier.height(8.dp))
Text("Saved")
Text(
"Saved",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.primary
)
}
}
}
}
@Composable
private fun SettingsSectionCard(
title: String,
content: @Composable () -> Unit,
) {
Card(
modifier = Modifier.fillMaxWidth(),
shape = MaterialTheme.shapes.large,
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceContainerLow
)
) {
Column(modifier = Modifier.padding(20.dp)) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(bottom = 12.dp)
)
content()
}
}
}

View file

@ -19,7 +19,6 @@ import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@ -56,7 +55,7 @@ fun BookshelfLibraryScreen(
Scaffold(
topBar = {
TopAppBar(
androidx.compose.material3.CenterAlignedTopAppBar(
title = { Text("Server Library") },
actions = {
IconButton(onClick = onOpenSettings) {
@ -145,9 +144,13 @@ private fun LibraryCard(
onClick = onClick,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp)
.padding(vertical = 6.dp),
shape = MaterialTheme.shapes.large,
colors = androidx.compose.material3.CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceContainerLow
)
) {
Column(modifier = Modifier.padding(16.dp)) {
Column(modifier = Modifier.padding(20.dp)) {
Text(
text = library.name,
style = MaterialTheme.typography.titleMedium,
@ -156,7 +159,9 @@ private fun LibraryCard(
)
Text(
text = "${library.itemCount} items · ${library.mediaType}",
style = MaterialTheme.typography.bodySmall
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 4.dp)
)
}
}
@ -172,9 +177,13 @@ private fun ItemCard(
onClick = onClick,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp)
.padding(vertical = 6.dp),
shape = MaterialTheme.shapes.large,
colors = androidx.compose.material3.CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceContainerLow
)
) {
Column(modifier = Modifier.padding(16.dp)) {
Column(modifier = Modifier.padding(20.dp)) {
Text(
text = item.title,
style = MaterialTheme.typography.titleMedium,
@ -184,16 +193,29 @@ private fun ItemCard(
if (item.author.isNotBlank()) {
Text(
text = item.author,
style = MaterialTheme.typography.bodyMedium
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 4.dp)
)
}
Text(
text = item.type,
style = MaterialTheme.typography.bodySmall
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 4.dp)
)
if (isDownloading) {
LinearProgressIndicator(modifier = Modifier.fillMaxWidth().padding(top = 8.dp))
Text("Downloading...", style = MaterialTheme.typography.bodySmall)
LinearProgressIndicator(
modifier = Modifier
.fillMaxWidth()
.padding(top = 12.dp),
)
Text(
"Downloading...",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(top = 4.dp)
)
}
}
}