From 739b7def10d90e45e1226b6bff0c9e68fef1c786 Mon Sep 17 00:00:00 2001 From: Leandro Schaguhn Date: Tue, 10 Jun 2025 12:36:24 +0200 Subject: [PATCH 1/3] Added Animation for TextFields in login form --- .../de/lelehier/keeper/screens/home_screen.kt | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 app/src/main/java/de/lelehier/keeper/screens/home_screen.kt diff --git a/app/src/main/java/de/lelehier/keeper/screens/home_screen.kt b/app/src/main/java/de/lelehier/keeper/screens/home_screen.kt new file mode 100644 index 0000000..a7d6e07 --- /dev/null +++ b/app/src/main/java/de/lelehier/keeper/screens/home_screen.kt @@ -0,0 +1,133 @@ +package de.lelehier.keeper.screens + +import KeeperLargeFontFamily +import android.print.PrintAttributes.Margins +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.core.EaseIn +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.togetherWith +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun homeScreen(paddingValues: PaddingValues) { + var currentScreen by remember { mutableStateOf(0) } + var nextScreen by remember { mutableStateOf(1) } + var serverURL = "" + var username by remember { mutableStateOf("") } + var password by remember { mutableStateOf("") } + var apiKey by remember { mutableStateOf("") } + + Column ( + modifier = Modifier + .padding(paddingValues) + .fillMaxSize() + .imePadding(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center) { + Greeting(); + AnimatedContent( + targetState = currentScreen, + transitionSpec = { + fadeIn( + animationSpec = tween(250) + ) togetherWith fadeOut(animationSpec = tween(250)) + }, + + ) { targetState -> when(targetState) { + 0 -> serverDialog(serverURL) + 1 -> passwordDialog(username, password) + } + } + + Button(onClick = {currentScreen = nextScreen}, modifier = Modifier.padding(top = 24.dp)) { + Row() { + Text(text = "Next") + } + } + } +} + +@Composable +fun Greeting() { + Column (horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(bottom = 24.dp)) { + Text( + text = "Welcome to", + style = MaterialTheme.typography.headlineLarge, + textAlign = TextAlign.Center + ) + Text( + text = "Keeper", + style = TextStyle( + fontSize = 72.sp, + fontFamily = KeeperLargeFontFamily, + brush = Brush.linearGradient(listOf(MaterialTheme.colorScheme.onPrimaryContainer, MaterialTheme.colorScheme.onSecondaryContainer))), + textAlign = TextAlign.Center, + ) + } +} + + +@Composable +fun serverDialog(serverURL: String) { + var serverURL by remember { mutableStateOf("") } + OutlinedTextField( + label = { Text(text = "Server URL") }, + textStyle = MaterialTheme.typography.bodySmall, + value = serverURL, + onValueChange = { text -> + serverURL = text + } + + ) +} + +@Composable +fun passwordDialog(username: String, password: String) { + var username by remember { mutableStateOf("") } + var password by remember { mutableStateOf("") } + Column { + OutlinedTextField( + label = { Text(text = "Username") }, + textStyle = MaterialTheme.typography.bodySmall, + value = username, + onValueChange = { text -> + username = text + }); + OutlinedTextField( + label = { Text(text = "Password") }, + textStyle = MaterialTheme.typography.bodySmall, + value = password, + onValueChange = { text -> + password = text + } + ) + + } +} \ No newline at end of file From 832ad6a7f14211dcb4e68fd1e3d711590664a16b Mon Sep 17 00:00:00 2001 From: Leandro Schaguhn Date: Tue, 10 Jun 2025 18:26:30 +0200 Subject: [PATCH 2/3] add url validation --- .../de/lelehier/keeper/screens/home_screen.kt | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/de/lelehier/keeper/screens/home_screen.kt b/app/src/main/java/de/lelehier/keeper/screens/home_screen.kt index a7d6e07..2fe8a38 100644 --- a/app/src/main/java/de/lelehier/keeper/screens/home_screen.kt +++ b/app/src/main/java/de/lelehier/keeper/screens/home_screen.kt @@ -2,8 +2,10 @@ package de.lelehier.keeper.screens import KeeperLargeFontFamily import android.print.PrintAttributes.Margins +import android.util.Patterns import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.EaseIn import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn @@ -16,6 +18,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField @@ -31,6 +34,7 @@ import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -51,7 +55,7 @@ fun homeScreen(paddingValues: PaddingValues) { .imePadding(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) { - Greeting(); + Greeting(serverURL); AnimatedContent( targetState = currentScreen, transitionSpec = { @@ -75,7 +79,7 @@ fun homeScreen(paddingValues: PaddingValues) { } @Composable -fun Greeting() { +fun Greeting(serverURL: String) { Column (horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(bottom = 24.dp)) { Text( text = "Welcome to", @@ -90,6 +94,7 @@ fun Greeting() { brush = Brush.linearGradient(listOf(MaterialTheme.colorScheme.onPrimaryContainer, MaterialTheme.colorScheme.onSecondaryContainer))), textAlign = TextAlign.Center, ) + } } @@ -103,11 +108,29 @@ fun serverDialog(serverURL: String) { value = serverURL, onValueChange = { text -> serverURL = text + }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri), + supportingText = { + AnimatedVisibility(!isValidUrl(serverURL)) { + Text( + text = "No valid URL", + color = MaterialTheme.colorScheme.error + ) + } } - ) } +fun isValidUrl(url: String): Boolean { + return try { + // Use Android's Patterns.WEB_URL for robust URL validation + // This handles various URL formats, including those without schemes. + Patterns.WEB_URL.matcher(url).matches() + } catch (e: Exception) { + false + } +} + @Composable fun passwordDialog(username: String, password: String) { var username by remember { mutableStateOf("") } From ac695e31090cec3db8f03a7fbd6c749a15ec02d0 Mon Sep 17 00:00:00 2001 From: Leandro Schaguhn Date: Wed, 11 Jun 2025 15:00:20 +0200 Subject: [PATCH 3/3] replace remeber with rememberSaveable to prevent state loss --- .../java/de/lelehier/keeper/screens/home_screen.kt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/de/lelehier/keeper/screens/home_screen.kt b/app/src/main/java/de/lelehier/keeper/screens/home_screen.kt index 2fe8a38..8537d1b 100644 --- a/app/src/main/java/de/lelehier/keeper/screens/home_screen.kt +++ b/app/src/main/java/de/lelehier/keeper/screens/home_screen.kt @@ -27,6 +27,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -41,12 +42,12 @@ import androidx.compose.ui.unit.sp @Composable fun homeScreen(paddingValues: PaddingValues) { - var currentScreen by remember { mutableStateOf(0) } - var nextScreen by remember { mutableStateOf(1) } - var serverURL = "" - var username by remember { mutableStateOf("") } - var password by remember { mutableStateOf("") } - var apiKey by remember { mutableStateOf("") } + var currentScreen by rememberSaveable { mutableStateOf(0) } + var nextScreen by rememberSaveable { mutableStateOf(1) } + var serverURL by rememberSaveable { mutableStateOf("") } + var username by rememberSaveable { mutableStateOf("") } + var password by rememberSaveable { mutableStateOf("") } + var apiKey by rememberSaveable { mutableStateOf("") } Column ( modifier = Modifier