From fcb8869e0ac9967f1d30c6f7048a1f1d492daba5 Mon Sep 17 00:00:00 2001 From: Pedro Javier Date: Mon, 13 Jul 2026 13:35:40 +0200 Subject: [PATCH] =?UTF-8?q?A=C3=B1adir=20crear=20usuario=20a=20mariadb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/deviceManager.xml | 13 ++ README.md | 39 +++--- app/build.gradle.kts | 5 + .../java/com/example/saludapp/MainActivity.kt | 13 +- .../example/saludapp/data/DatabaseHelper.kt | 43 +++++- .../com/example/saludapp/ui/LoginScreen.kt | 6 +- .../com/example/saludapp/ui/RegisterScreen.kt | 130 ++++++++++++++++++ 7 files changed, 222 insertions(+), 27 deletions(-) create mode 100644 .idea/deviceManager.xml create mode 100644 app/src/main/java/com/example/saludapp/ui/RegisterScreen.kt diff --git a/.idea/deviceManager.xml b/.idea/deviceManager.xml new file mode 100644 index 0000000..91f9558 --- /dev/null +++ b/.idea/deviceManager.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 82f7da8..a68add4 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,21 @@ # Saludapp -Saludapp es una aplicación Android moderna enfocada en la salud y el bienestar, diseñada para ser multiusuario y conectada a una base de datos externa. +Saludapp es una aplicación Android moderna enfocada en la salud y el bienestar, diseñada para ser multiusuario y conectada a una base de datos externa MariaDB de forma segura. ## 🚀 Características -- **Sistema Multiusuario:** Autenticación de usuarios mediante Login. -- **Conexión a Base de Datos:** Integración directa con **MariaDB** autohospedado. -- **Interfaz Moderna:** Desarrollada íntegramente con **Jetpack Compose**. -- **Diseño Material 3:** Uso de componentes y temas dinámicos de última generación. -- **Navegación Fluida:** Flujo desde pantalla de inicio hasta el panel principal. +- **Sistema Multiusuario:** Registro e inicio de sesión de usuarios. +- **Seguridad:** Encriptación de contraseñas mediante **SHA-256** para proteger los datos del usuario. +- **Perfiles Completos:** Gestión de datos de usuario incluyendo nombre completo, nombre de usuario y email. +- **Conexión a Base de Datos:** Integración directa con **MariaDB** autohospedado en `mariadb.peta9.com`. +- **Interfaz Moderna:** Desarrollada íntegramente con **Jetpack Compose** y **Material Design 3**. +- **Navegación Fluida:** Flujo completo desde bienvenida hasta registro, login y panel principal. ## 🛠️ Tecnologías utilizadas - **Lenguaje:** [Kotlin](https://kotlinlang.org/) - **UI Framework:** [Jetpack Compose](https://developer.android.com/jetpack/compose) - **Base de Datos:** [MariaDB](https://mariadb.org/) (via MariaDB Java Client) -- **Asincronía:** Corrutinas de Kotlin para operaciones de red no bloqueantes. +- **Seguridad:** Algoritmo SHA-256 (MessageDigest) para hashing de credenciales. +- **Asincronía:** Corrutinas de Kotlin para operaciones de red eficientes. - **Diseño:** [Material Design 3](https://m3.material.io/) ## 📋 Requisitos previos @@ -27,20 +29,19 @@ Saludapp es una aplicación Android moderna enfocada en la salud y el bienestar, git clone https://github.com/tu-usuario/Saludapp.git ``` 2. **Configurar Credenciales:** - - Abre el archivo `app/src/main/java/com/example/saludapp/data/DatabaseHelper.kt`. - - Sustituye `TU_CONTRASEÑA_AQUÍ` por la contraseña real de tu base de datos MariaDB. -3. **Sincronizar Gradle:** Abre el proyecto en Android Studio y espera a que termine la sincronización de dependencias. -4. **Ejecutar:** Compila y ejecuta en un dispositivo o emulador con acceso a Internet. + - La aplicación ya viene configurada para conectar con el servidor MariaDB. + - Las credenciales se encuentran en `app/src/main/java/com/example/saludapp/data/DatabaseHelper.kt`. +3. **Sincronizar Gradle:** Abre el proyecto en Android Studio y sincroniza las dependencias. +4. **Ejecutar:** Compila y ejecuta la aplicación en un emulador o dispositivo real. ## 📂 Estructura del Proyecto -- `app/`: Módulo principal de la aplicación. - - `src/main/java/com/example/saludapp/`: - - `MainActivity.kt`: Gestiona el flujo de navegación principal. - - `data/DatabaseHelper.kt`: Lógica de conexión y consultas a MariaDB. - - `ui/`: Contiene los componentes de la interfaz de usuario. - - `StartScreen.kt`: Pantalla de bienvenida. - - `LoginScreen.kt`: Interfaz de inicio de sesión con validación real. - - `theme/`: Definición de estilos y colores de Material 3. +- `app/`: Módulo principal. + - `src/main/java/com/example/saludapp/data/DatabaseHelper.kt`: Gestión de seguridad (SHA-256) y conexión MariaDB. + - `src/main/java/com/example/saludapp/ui/`: + - `StartScreen.kt`: Pantalla de bienvenida. + - `LoginScreen.kt`: Inicio de sesión con validación de credenciales encriptadas. + - `RegisterScreen.kt`: Registro de nuevos usuarios con soporte para nombre completo. + - `theme/`: Configuración estética de Material 3. --- Desarrollado con ❤️ para mejorar la salud de los usuarios. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 592018c..1f0622d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -31,6 +31,11 @@ android { buildFeatures { compose = true } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } } dependencies { diff --git a/app/src/main/java/com/example/saludapp/MainActivity.kt b/app/src/main/java/com/example/saludapp/MainActivity.kt index eeb61eb..d94810d 100644 --- a/app/src/main/java/com/example/saludapp/MainActivity.kt +++ b/app/src/main/java/com/example/saludapp/MainActivity.kt @@ -14,6 +14,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import com.example.saludapp.ui.LoginScreen +import com.example.saludapp.ui.RegisterScreen import com.example.saludapp.ui.StartScreen import com.example.saludapp.ui.theme.SaludappTheme @@ -29,10 +30,14 @@ class MainActivity : ComponentActivity() { "start" -> StartScreen { currentScreen = "login" } - "login" -> LoginScreen { - // Aquí iría la lógica tras login exitoso - currentScreen = "home" - } + "login" -> LoginScreen( + onLoginSuccess = { currentScreen = "home" }, + onNavigateToRegister = { currentScreen = "register" } + ) + "register" -> RegisterScreen( + onRegisterSuccess = { currentScreen = "login" }, + onBackToLogin = { currentScreen = "login" } + ) "home" -> { Box( modifier = Modifier.fillMaxSize(), diff --git a/app/src/main/java/com/example/saludapp/data/DatabaseHelper.kt b/app/src/main/java/com/example/saludapp/data/DatabaseHelper.kt index 6de3d9a..e85bf78 100644 --- a/app/src/main/java/com/example/saludapp/data/DatabaseHelper.kt +++ b/app/src/main/java/com/example/saludapp/data/DatabaseHelper.kt @@ -2,6 +2,7 @@ package com.example.saludapp.data import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import java.security.MessageDigest import java.sql.Connection import java.sql.DriverManager import java.sql.SQLException @@ -9,20 +10,27 @@ import java.sql.SQLException object DatabaseHelper { private const val URL = "jdbc:mariadb://mariadb.peta9.com:3306/saludapp_db" private const val USER = "saludapp_dba" - private const val PASSWORD = "TU_CONTRASEÑA_AQUÍ" // TODO: Reemplazar con la contraseña real + private const val PASSWORD = "Pedro@110387" + + private fun hashPassword(password: String): String { + val bytes = password.toByteArray() + val md = MessageDigest.getInstance("SHA-256") + val digest = md.digest(bytes) + return digest.fold("") { str, it -> str + "%02x".format(it) } + } suspend fun validateLogin(user: String, pass: String): Boolean = withContext(Dispatchers.IO) { var connection: Connection? = null try { - // Cargar el driver explícitamente si es necesario Class.forName("org.mariadb.jdbc.Driver") connection = DriverManager.getConnection(URL, USER, PASSWORD) + val hashedPass = hashPassword(pass) val query = "SELECT COUNT(*) FROM usuarios WHERE (username = ? OR email = ?) AND password = ?" connection.prepareStatement(query).use { statement -> statement.setString(1, user) statement.setString(2, user) - statement.setString(3, pass) + statement.setString(3, hashedPass) val resultSet = statement.executeQuery() if (resultSet.next()) { @@ -40,4 +48,33 @@ object DatabaseHelper { } return@withContext false } + + suspend fun registerUser(user: String, email: String, pass: String, fullName: String): Boolean = withContext(Dispatchers.IO) { + var connection: Connection? = null + try { + Class.forName("org.mariadb.jdbc.Driver") + connection = DriverManager.getConnection(URL, USER, PASSWORD) + + val hashedPass = hashPassword(pass) + val query = "INSERT INTO usuarios (username, email, password, nombre_completo) VALUES (?, ?, ?, ?)" + connection.prepareStatement(query).use { statement -> + statement.setString(1, user) + statement.setString(2, email) + statement.setString(3, hashedPass) + statement.setString(4, fullName) + + val result = statement.executeUpdate() + return@withContext result > 0 + } + } catch (e: Exception) { + e.printStackTrace() + } finally { + try { + connection?.close() + } catch (e: SQLException) { + e.printStackTrace() + } + } + return@withContext false + } } diff --git a/app/src/main/java/com/example/saludapp/ui/LoginScreen.kt b/app/src/main/java/com/example/saludapp/ui/LoginScreen.kt index 5cb570e..1aed607 100644 --- a/app/src/main/java/com/example/saludapp/ui/LoginScreen.kt +++ b/app/src/main/java/com/example/saludapp/ui/LoginScreen.kt @@ -11,7 +11,7 @@ import com.example.saludapp.data.DatabaseHelper import kotlinx.coroutines.launch @Composable -fun LoginScreen(onLoginSuccess: () -> Unit) { +fun LoginScreen(onLoginSuccess: () -> Unit, onNavigateToRegister: () -> Unit) { var email by remember { mutableStateOf("") } var password by remember { mutableStateOf("") } var isLoading by remember { mutableStateOf(false) } @@ -97,6 +97,10 @@ fun LoginScreen(onLoginSuccess: () -> Unit) { Text("Entrar") } } + + TextButton(onClick = onNavigateToRegister) { + Text("¿No tienes cuenta? Regístrate aquí") + } } } } diff --git a/app/src/main/java/com/example/saludapp/ui/RegisterScreen.kt b/app/src/main/java/com/example/saludapp/ui/RegisterScreen.kt new file mode 100644 index 0000000..06b1f95 --- /dev/null +++ b/app/src/main/java/com/example/saludapp/ui/RegisterScreen.kt @@ -0,0 +1,130 @@ +package com.example.saludapp.ui + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.unit.dp +import com.example.saludapp.data.DatabaseHelper +import kotlinx.coroutines.launch + +@Composable +fun RegisterScreen(onRegisterSuccess: () -> Unit, onBackToLogin: () -> Unit) { + var fullName by remember { mutableStateOf("") } + var username by remember { mutableStateOf("") } + var email by remember { mutableStateOf("") } + var password by remember { mutableStateOf("") } + var isLoading by remember { mutableStateOf(false) } + var errorMessage by remember { mutableStateOf(null) } + val scope = rememberCoroutineScope() + + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Text( + text = "Crear Cuenta", + style = MaterialTheme.typography.headlineMedium, + color = MaterialTheme.colorScheme.primary + ) + + Spacer(modifier = Modifier.height(24.dp)) + + OutlinedTextField( + value = fullName, + onValueChange = { fullName = it }, + label = { Text("Nombre Completo") }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + enabled = !isLoading + ) + + Spacer(modifier = Modifier.height(12.dp)) + + OutlinedTextField( + value = username, + onValueChange = { username = it }, + label = { Text("Nombre de usuario") }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + enabled = !isLoading + ) + + Spacer(modifier = Modifier.height(12.dp)) + + OutlinedTextField( + value = email, + onValueChange = { email = it }, + label = { Text("Email") }, + modifier = Modifier.fillMaxWidth(), + singleLine = true, + enabled = !isLoading + ) + + Spacer(modifier = Modifier.height(12.dp)) + + OutlinedTextField( + value = password, + onValueChange = { password = it }, + label = { Text("Contraseña") }, + visualTransformation = PasswordVisualTransformation(), + modifier = Modifier.fillMaxWidth(), + singleLine = true, + enabled = !isLoading + ) + + if (errorMessage != null) { + Text( + text = errorMessage!!, + color = MaterialTheme.colorScheme.error, + modifier = Modifier.padding(top = 16.dp) + ) + } + + Spacer(modifier = Modifier.height(32.dp)) + + Button( + onClick = { + scope.launch { + isLoading = true + errorMessage = null + val success = DatabaseHelper.registerUser(username, email, password, fullName) + isLoading = false + if (success) { + onRegisterSuccess() + } else { + errorMessage = "Error al registrar. El usuario puede que ya exista." + } + } + }, + modifier = Modifier + .fillMaxWidth() + .height(56.dp), + enabled = !isLoading && fullName.isNotEmpty() && username.isNotEmpty() && email.isNotEmpty() && password.isNotEmpty() + ) { + if (isLoading) { + CircularProgressIndicator( + modifier = Modifier.size(24.dp), + color = MaterialTheme.colorScheme.onPrimary, + strokeWidth = 2.dp + ) + } else { + Text("Registrarse") + } + } + + TextButton(onClick = onBackToLogin) { + Text("¿Ya tienes cuenta? Inicia sesión") + } + } + } +}