📚 Proyecto educativo — Este repositorio forma parte de mi formación temprana en DUOC UC (Instituto Profesional). Las prácticas aquí reflejadas corresponden al momento de desarrollo y pueden no representar estándares actuales.
Tienda de beats musicales para Android, desarrollada en Kotlin
Arquitectura · Funcionalidades · Estructura · Instalación · Tests · Configuración
FullSound es una aplicación Android desarrollada en Kotlin que funciona como una tienda de beats musicales. Permite a los usuarios comprar y vender instrumentales, gestionar un carrito de compras y administrar su perfil.
-
MVVM (Model-View-ViewModel)
-
Supabase (Base de datos remota)
-
Room Database (Caché local)
-
Navigation Component
-
Material Design 3
-
Retrofit + OkHttp (API Fixer para conversión de monedas)
-
Sistema de autenticación de usuarios (Login y Registro).
-
Gestión completa de Beats (Crear, Leer, Actualizar, Eliminar).
-
Carrito de compras funcional.
-
Base de datos en la nube mediante Supabase.
-
Interfaz de usuario moderna basada en Material Design 3.
-
Navegación fluida utilizando Navigation Component.
El proyecto sigue el patrón de arquitectura MVVM (Model-View-ViewModel) para asegurar una separación clara de responsabilidades y facilitar el mantenimiento.
- Lenguaje: Kotlin 1.9.22
- Arquitectura: MVVM
- Base de Datos: Supabase (remota) + Room (caché local)
- UI: XML con ViewBinding y componentes de Jetpack Compose
- Navegación: Navigation Component
- Diseño: Material Design 3
- SDK mínimo: Android 7.0 (API 24) · Objetivo: API 34
FullSound-KOTLIN/
├── app/
│ ├── build.gradle.kts # Configuración del módulo app
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/com/grupo8/fullsound/
│ │ │ ├── MainActivity.kt
│ │ │ ├── data/
│ │ │ │ ├── local/ # Room DAOs y AppDatabase
│ │ │ │ └── remote/
│ │ │ │ ├── dto/ # Data Transfer Objects
│ │ │ │ ├── fixer/ # Integración API Fixer (conversión monedas)
│ │ │ │ └── supabase/ # Cliente y repositorios Supabase
│ │ │ ├── model/ # Entidades de datos (Beat, User, CarritoItem)
│ │ │ ├── repository/ # Capa de repositorios
│ │ │ ├── ui/
│ │ │ │ ├── auth/ # Fragments de Login y Registro
│ │ │ │ ├── beats/ # Catálogo y lista de beats
│ │ │ │ ├── Carrito/ # Carrito de compras
│ │ │ │ ├── profile/ # Fragment de Perfil de usuario
│ │ │ │ └── theme/ # Tema Compose (Color, Type, Theme)
│ │ │ ├── utils/ # Utilidades (validadores, sesiones, etc.)
│ │ │ └── viewmodel/ # ViewModels (Carrito, Login, Beats, Register)
│ │ └── res/ # Recursos (layouts, drawables, animaciones, raw)
│ ├── androidTest/ # Tests instrumentados (UI/Recursos)
│ └── test/ # Tests unitarios
├── gradle/
│ └── libs.versions.toml # Catálogo de versiones
├── build.gradle.kts # Build script raíz
├── settings.gradle.kts
├── gradle.properties
├── local.properties.example # Plantilla de credenciales (ver Configuración)
├── LICENSE
└── README.md
📸 Screenshots coming soon — Las capturas de pantalla se agregarán en una próxima actualización.
Para compilar y ejecutar el proyecto, utilice el siguiente comando en la terminal:
# Compilar la aplicación
./gradlew assembleDebug
# Generar APK firmado
./gradlew assembleReleaseNota: En Windows usa
gradlew.baten lugar de./gradlew.
# Ejecutar todos los tests unitarios
./gradlew test
# Ejecutar tests instrumentados (requiere dispositivo/emulador)
./gradlew connectedAndroidTest
# Ejecutar tests específicos
./gradlew test --tests "com.grupo8.fullsound.model.*"
./gradlew test --tests "com.grupo8.fullsound.viewmodel.*"- ✅ Tests de Modelos (Beat, User, CarritoItem) - 20 tests
- ✅ Tests de Repositorios (Beat, User, Carrito) - casos éxito/fallback/error
- ✅ Tests de ViewModels (Beats, Login, Register, Carrito)
- ✅ Tests de Utilidades (FormValidator, RutValidator, Constants) - 13 tests
- ✅ Tests de API (ExchangeRateProvider) - 2 tests
- ✅ Tests Instrumentados (UI/Recursos) - 8 tests
- ✅ Sistema de autenticación con Supabase Auth / GoTrue (Login/Registro) — ver docs/AUTH.md
- ✅ Roles de usuario (User/Admin/Productor) determinados por
id_rolen la tablausuario(no por el dominio del correo) - ✅ CRUD completo de Beats conectado a Supabase
- ✅ Catálogo de Beats desde base de datos remota
- ✅ Búsqueda en tiempo real de beats por título, artista o género en
BeatsListaFragment(barra de búsqueda conTextWatcher) - ✅ Estados de UI reutilizables (carga, vacío y error con botón de reintentar) mediante
layout_loading.xml,layout_empty.xmlylayout_error.xml, integrados enBeatsListaFragmentyBeatsFragment - ✅ Fragment de Perfil (
ProfileFragment) que muestra nombre, usuario, email, RUT y rol del usuario autenticado, con botón de cerrar sesión. Accesible desde la barra de navegación inferior - ✅ Imágenes y audios desde Supabase Storage (buckets)
- ✅ Conversión de monedas CLP ↔ USD con API de Fixer
- ✅ Carrito de compras con persistencia local
- ✅ Reglas de negocio (No se puede agregar más de un beat duplicado)
- ✅ Manejo de errores consistente: todos los repositorios exponen resultados mediante
Resource<T>(Loading/Success/Error) - ✅ Strings externalizados en
strings.xml(sin textos hardcoded en Kotlin) - ✅ KDoc en las APIs públicas (repositorios, ViewModels y utilidades)
- ✅ Persistencia híbrida: Supabase (remoto) + Room (caché local)
- ✅ Navegación con Navigation Component
- ✅ Animaciones personalizadas
- ✅ Material Design 3
- ✅ Arquitectura MVVM
- ✅ Tests unitarios e instrumentados (35+ tests)
La pantalla de catálogo (BeatsListaFragment) incluye una barra de búsqueda que
filtra la lista en tiempo real a medida que el usuario escribe. La búsqueda es
insensible a mayúsculas/minúsculas y compara el texto contra el título,
artista y género de cada beat. Se combina con el filtro por género
existente, por lo que ambos criterios se aplican simultáneamente. Cuando no hay
resultados se muestra un estado vacío con un mensaje específico.
Se añadieron layouts reutilizables para los estados de la UI:
layout_loading.xml— indicador de progreso con texto "Cargando…".layout_empty.xml— ilustración + título y subtítulo configurables desde código.layout_error.xml— mensaje de error + botón Reintentar.
Estos layouts se incluyen en los fragments y su visibilidad se alterna según el
Resource emitido por el ViewModel. El botón de reintentar vuelve a disparar la
carga de datos.
El nuevo ProfileFragment (añadido al nav_graph) muestra la información del
usuario en sesión (nombre, usuario, email, RUT y rol) leída desde UserSession,
y permite cerrar sesión. Es accesible desde el botón de perfil de la barra
de navegación inferior en BeatsFragment, BeatsListaFragment y CarritoFragment.
El proyecto usa un archivo local.properties (gitignored) para las credenciales sensibles.
Se incluye una plantilla local.properties.example que puedes copiar y completar:
cp local.properties.example local.properties# Supabase Configuration
SUPABASE_URL=your_supabase_url
SUPABASE_ANON_KEY=your_supabase_anon_key
# Fixer API (Conversión de monedas)
FIXER_API_KEY=your_fixer_api_key🔒 Seguridad: Las credenciales se inyectan en tiempo de compilación mediante
BuildConfig. Nunca se suben a Git. Como alternativa, también puedes definir las variables de entornoSUPABASE_URL,SUPABASE_ANON_KEYyFIXER_API_KEY.
id_beat(PK, auto-increment)titulo,slug,artistaprecio(CLP),bpm,tonalidad,duraciongenero,etiquetas,descripcionimagen_url,audio_url,audio_demo_urlreproducciones,estadocreated_at,updated_at
id_usuario(PK, auto-increment)nombre_usuario,correonombre,apellidoid_rol(FK a tipo_usuario) — determina el rol (1=admin,2=productor, otro=user)activo,created_at,updated_at
🔐 Autenticación: La contraseña no se almacena en esta tabla desde la app ni en el caché local. La autenticación se realiza con Supabase Auth (GoTrue). Consulta docs/AUTH.md para el detalle del flujo, la configuración y las políticas RLS recomendadas.
beats-images: Imágenes de beatsbeats-audio: Archivos de audio de beats
La app usa Supabase como backend (Postgres + Auth + Storage). Para configurar tu propio proyecto:
-
Crea un proyecto en supabase.com.
-
Copia
local.properties.examplealocal.propertiesy completa:SUPABASE_URL=https://TU_PROYECTO.supabase.co SUPABASE_ANON_KEY=tu_anon_key FIXER_API_KEY=tu_fixer_api_key
Estos valores se inyectan en tiempo de compilación vía
BuildConfig. -
Crea las tablas
beatyusuario(ytipo_usuariocon los roles) según el esquema de la sección anterior. -
Habilita Supabase Auth (proveedor Email) — la autenticación la maneja GoTrue. Consulta docs/AUTH.md para el detalle completo y las políticas RLS recomendadas.
-
Crea los buckets de Storage
Imagenesyaudios(públicos o con políticas de lectura). -
Si cambias de proyecto de Supabase, verifica que el dominio en
app/src/main/res/xml/network_security_config.xmlcoincida con tuSUPABASE_URL(el dominio genéricosupabase.coya cubre todos los subdominios).
La base de datos local (Room, fullsound_database) usa migraciones
incrementales para preservar los datos del usuario al actualizar la app.
Las migraciones definidas en AppDatabase.kt:
- v6 → v7: elimina la columna
passwordde la tablausers(la autenticación ahora se maneja con Supabase Auth; no se persisten contraseñas). - v7 → v8: añade índices a
beats(slug,titulo,genero) y acarrito(userId,beatId), e incorpora la columnauserIdacarritopara alinear el caché local con el campouser_idde Supabase.
⚠️ Si modificas las entidades (Beat,CarritoItem,User), recuerda subir la versión de la base de datos enAppDatabasey añadir la migración correspondiente. Sin migración, Room lanza una excepción en tiempo de ejecución al detectar un cambio de esquema.
-
La app no carga beats / error de red
- Verifica que
SUPABASE_URLySUPABASE_ANON_KEYenlocal.propertiessean correctos. - Confirma que el dispositivo tenga conexión a internet.
- Revisa que la tabla
beattenga datos en Supabase. - Asegúrate de que el dominio en
network_security_config.xmlcoincida con tu proyecto (el dominiosupabase.cocon subdominios suele ser suficiente).
- Verifica que
-
No puedo iniciar sesión
- La autenticación se realiza con Supabase Auth (GoTrue). Verifica que el
usuario exista en Auth y en la tabla
usuario. - El rol se obtiene de
id_rolenusuario; si esnullo distinto de1/2, el usuario se trata comousernormal.
- La autenticación se realiza con Supabase Auth (GoTrue). Verifica que el
usuario exista en Auth y en la tabla
-
No soy admin aunque uso un correo @admin.cl
- El rol no se detecta por el dominio del correo. Debe estar configurado
en la columna
id_rolde la tablausuario(1= admin).
- El rol no se detecta por el dominio del correo. Debe estar configurado
en la columna
-
Error de Room: "A migration from X to Y was required but not found"
- Falta una migración. Revisa la sección anterior y añade la migración en
AppDatabase.kt, o incrementa la versión correctamente.
- Falta una migración. Revisa la sección anterior y añade la migración en
-
Las imágenes/audios no se ven
- Verifica que los buckets
Imagenesyaudiosexistan y sean públicos (o tengan políticas de lectura). - Las URLs se construyen a partir de
SUPABASE_URL+ nombre de archivo.
- Verifica que los buckets
-
La conversión CLP → USD no funciona
- Requiere una API key válida de Fixer en
FIXER_API_KEY. Sin ella, solo se muestra el precio en CLP.
- Requiere una API key válida de Fixer en
-
BuildConfig no encuentra SUPABASE_URL
- Asegúrate de haber creado
local.properties(no se sube a Git) o de tener las variables de entorno definidas.
- Asegúrate de haber creado
Este proyecto está bajo la Licencia MIT — consulta el archivo LICENSE para más detalles.