Activa JavaScript para disfrutar de los vídeos de la Mediateca.
MyPilot TFG Herrera
Ajuste de pantallaEl ajuste de pantalla se aprecia al ver el vídeo en pantalla completa. Elige la presentación que más te guste:
Explicación y demo de mi TFG - MyPilot y MyPilotDriver
Hola, mi nombre es Juan Herrera y soy el desarrollador de MyPilot.
00:00:02
MyPilot es una aplicación hecha para Android que es mi trabajo de fin de grado.
00:00:08
La idea es bastante simple. Hay situaciones en las que uno tiene un coche,
00:00:13
pero en ese momento no quiere o no puede manejarlo, ya sea porque ha bebido de más,
00:00:19
porque tiene una incapacidad física temporal, por lo que sea.
00:00:24
MyPilot es donde entra en ese momento.
00:00:28
La aplicación te permite pedir un conductor que se acerca a donde estás y te lleva a ti en tu vehículo hacia donde quieras
00:00:30
Es parecido a Uber o a Cabify, pero al revés
00:00:37
En esas aplicaciones el conductor llega en su propio vehículo, te recoge y te lleva
00:00:40
Aquí el conductor llegaría o en transporte público, o en bicicleta, o en algún otro medio de transporte o a pie
00:00:45
Y utiliza tu vehículo para llevarte a sitios
00:00:52
El sistema está compuesto por tres partes
00:00:56
El backend, que es el cerebro de todo, que es un API restricado.
00:00:59
Y las otras dos aplicaciones, que son las que van en el teléfono.
00:01:03
MyPilot, que va en el teléfono del viajero o el consumidor final.
00:01:06
Y MyPilotDriver, que es la app que va a manejar el conductor.
00:01:10
Las tres están conectadas entre sí y funcionan en tiempo real.
00:01:14
La arquitectura del sistema es de cliente-servidor.
00:01:18
Las dos aplicaciones en Kotlin se comunican con la API a través de dos canales principales de comunicación.
00:01:21
que son REST, para llamadas que no tienen necesidad de que sean inmediatas, ni que sean de respuesta bidireccional,
00:01:29
como hacer un rating, por ejemplo, que después de terminar un viaje se le pone una calificación a la otra persona,
00:01:38
y a través de WebSockets para las actividades o eventos que tienen que aparecer en tiempo real.
00:01:45
El uso de WebSockets y protocolos Storm permiten que la aplicación sea mucho más eficiente
00:01:51
a la hora de mantener un contacto en vivo con una API, porque en vez de estar llamando
00:01:59
constantemente o periódicamente a la API diciendo ocurrió algo, ocurrió algo, ocurrió
00:02:06
algo, la API no tiene ningún evento nuevo, simplemente espera a que la API cuando ocurra
00:02:12
algo realmente, le notifique. Eso lo hace mucho más eficiente en tiempo de ejecución
00:02:19
y en batería, en uso de batería.
00:02:24
El objetivo del proyecto era construir todo desde cero. El backend, la frontend, las aplicaciones
00:02:28
en Android, la comunicación en tiempo real y el funcionamiento entre sí, especialmente
00:02:34
conectado. Vamos a verlo.
00:02:42
Para el backend utilicé Java con Springwood, que es el framework estándar para APIs REST
00:02:44
en Java. La base de datos es MySQL gestionada con JPA e Hibernate, que me permiten saltarme
00:02:49
a escribir el SQL a mano y simplemente trabajar directamente con los objetos Java. Esto lo
00:02:56
podemos ver en los entities, más que nada, con estas anotaciones, las importaciones del
00:03:04
on-book para además saltarme a hacer los gets y sets, y a las transformaciones de algunos
00:03:08
elementos de la tabla de bases de datos a DTOS, porque no utilizaba todos los elementos de la tabla.
00:03:15
El motivo de la implementación de MGSQL como motor de bases de datos tiene que ver con la
00:03:25
cualidad de relacional que tiene el propio motor. Son varias tablas que tienen relaciones de foreign
00:03:30
key entre ellas y además es uno de los motores de base de datos que se integra mejor con API REST y con Spring Boot.
00:03:36
y con Spring Boot
00:03:42
para la autenticación utilicé
00:03:43
JWT o JSON Web Tokens
00:03:46
a grandes rasgos
00:03:48
esta clase lo que hace es
00:03:50
el gestor de autenticación
00:03:52
mediante JWT en la aplicación
00:03:54
su función principal sería generar
00:03:56
los tokens de acceso
00:03:58
cuando el usuario
00:03:59
inicia sesión correctamente
00:04:02
y posteriormente verificar que esos tokens sean válidos
00:04:03
en cada petición
00:04:06
para que nadie sin haber
00:04:07
que ha hecho el inicio de sesión pueda acceder a la base de datos ni a la lógica de negocio.
00:04:10
Además, se puede extraer información del usuario almacenada dentro del token, como el email o el rol.
00:04:16
Esta clase centraliza toda la lógica relacionada con JWT dentro de la API.
00:04:24
Se utiliza para implementar la autenticación stateless en Spring Boot.
00:04:29
Cuando un usuario inicia sesión, la clase genera un token firmado digitalmente que contiene su identidad y permisos.
00:04:32
Posteriormente, en la petición protegida, el sistema valida el token
00:04:39
y recupera la información del usuario sin necesidad de mantener sesiones en el servidor.
00:04:42
Este sistema utiliza la firma HMAC SHA-256
00:04:49
y expiración temporal para garantizar la seguridad en la arquitectura REST stateless.
00:04:54
Para la comunicación en tiempo real, utilicé WebSockets con el protocolo STOMP
00:05:03
que es el Spring que tiene integrado de forma nativa.
00:05:08
Básicamente es un canal persistente entre el servidor y cada aplicación por donde viajan los eventos.
00:05:11
Que el conductor aceptó, que el viaje empezó, que el viaje terminó y demás.
00:05:16
La mayor parte de lógica detrás del protocolo STOMP están en las aplicaciones.
00:05:23
Por ejemplo, aquí en MyPilotDriver tenemos un método que construye el canal
00:05:29
por el que va a estar escuchando todos los mensajes que manda la API, y cómo se desconecta.
00:05:36
Y en clases como el LoginViewModel es que tenemos la implementación de los métodos,
00:05:46
por ejemplo, que tenemos en SocketManager, en los que espera la respuesta de la API con
00:05:54
respecto al token del login, y si es exitoso pues true, y si no, pues dar la advertencia correspondiente.
00:06:04
Para los mapas y las rutas, utilicé la Google Maps Platform, el SDK de mapas, la Direction
00:06:16
SAPI para calcular las rutas, y la Place SAPI para el autocompletado de direcciones.
00:06:23
las llamadas http las hago con retrofit y el cliente stomp en android es una librería que
00:06:29
se llama crossfit, podemos ver retrofit como se usa por ejemplo aquí en api service
00:06:37
o en el propio repositorio de mapas o en el de viajes
00:06:45
Compose, Boot, Pack, eso simplemente conecta lo que es la aplicación con los endpoints de la API.
00:06:52
Las dos aplicaciones están escritas en Kotlin, las aplicaciones de Android,
00:07:09
están escritas en Kotlin y utilizan Jetpack Compose, que es el sistema de UI moderno de Android.
00:07:15
Este sistema permite generar componentes, animaciones, cambios de color, cambios de estado en cada uno de los componentes de la UI de una forma muy directa y muy sencilla.
00:07:19
El proyecto lo desarrollé en fases. Primero te debe construir el backend completo, las entidades de la base de datos, de la lógica de API REST y toda la lógica de estados de viaje.
00:07:38
Como podemos ver aquí, son cinco.
00:07:49
Solicitado, conductor en camino, en curso, finalizado y cancelado.
00:07:53
Las transiciones entre estados están validadas en el servidor, así que no podés pasar directamente de solicitado a finalizado, por ejemplo.
00:07:58
Se puede pasar de solicitado a cancelado.
00:08:05
Esto lo validamos en el service.
00:08:08
En el método de cambiar de estado en un momento se llama a Extransición Válida
00:08:09
Que es otro método que verifica efectivamente de que se cambie el estado de forma natural
00:08:23
Después desarrollé la app del viajero
00:08:29
Todo lo que es el mapa, la búsqueda del destino, el flujo de la solicitud
00:08:37
Recién después de eso, integré los WebSockets para que todo fuera en tiempo real
00:08:41
Y finalmente agregué la autentificación con cota WT
00:08:46
Ya para terminar, desarrollé MyPilotDriver
00:08:52
La disponibilidad, la cola de conductores, el flujo de aceptación y la gestión del viaje
00:08:57
El proyecto simplemente me parecía más sencillo y más lógico
00:09:01
Empezar por el backend y probar con Postman antes de tener apps
00:09:11
Porque el hecho de tener otra aplicación
00:09:15
Añadía otra capa de debugueo
00:09:18
Antes de que las cosas estuvieran realmente bien programadas
00:09:20
Obviamente del dicho al hecho hay un trecho
00:09:22
Realmente me había tomado conciencia
00:09:28
De la magnitud del proyecto
00:09:30
Teniendo en cuenta que son tres aplicaciones distintas
00:09:32
Si bien sí tiene mucho en común actuar en conjunto
00:09:35
Pero son tres aplicaciones
00:09:38
Y evidentemente siendo un solo integrante
00:09:40
el plan sufrió muchos cambios y yo pensaba que la API ya estaba terminada y el backend estaba
00:09:43
completo y funcionaba perfecto y al pensar en una nueva funcionalidad o una funcionalidad necesaria
00:09:52
que se me había olvidado al desarrollar algunas aplicaciones de las aplicaciones Kotlin teníamos
00:10:00
que teníamos. Tenía que volver a desarrollar el backend de otra forma, teniendo en cuenta
00:10:06
otros parámetros en pos de otra idea. Así que fue un vaivén bastante importante de
00:10:13
bastantes veces el proyecto.
00:10:20
Si tuviera que destacar tres cosas técnicas del proyecto serían las siguientes. Una de
00:10:24
ellas es la cola de conductores en la memoria. Cuando un viajero solicita un viaje, el sistema
00:10:30
notifica a los primeros 10 conductores disponibles, ordenados por tiempo de espera. El primero
00:10:35
en aceptar se le asigna el viaje y sale de la cola. Si lo rechaza, el sistema ofrece
00:10:40
el viaje al siguiente y cuando el viaje termina, el conductor vuelve automáticamente al final
00:10:44
de la cola. Inicializa la lista a partir de la base de datos y si en algún momento
00:10:49
un conductor se da de alta, o sea, activa el Available dentro de la aplicación, lo
00:10:56
mete a esperar en la cola. Una vez se le asigna el viaje se llama al
00:11:02
método eliminar para sacarlo de la cola e incorporar si es que
00:11:08
reincorporar es básicamente lo mismo que registrar.
00:11:18
La segunda y la tercera serían la implementación de la API de Google en la
00:11:23
aplicación de MyPilot y MyPilotDriver
00:11:29
porque el hecho
00:11:31
de contar con una API de Google
00:11:33
ya tenía aparte
00:11:35
otro proceso de trabajo
00:11:37
tu quedar de alta en una
00:11:38
cuenta
00:11:41
generar la API key secreta
00:11:42
de Google, generar una cuota
00:11:45
de uso máxima
00:11:47
para la API para que el proyecto
00:11:49
siga siendo gratis y
00:11:51
académicamente viable
00:11:53
y la tercera
00:11:54
cosa sería la implementación de esta
00:11:57
API de Google en conjunto
00:11:59
con la arquitectura
00:12:02
de las apps, el ModelViewViewModel
00:12:04
donde ViewModel
00:12:06
expone el estado de forma reactiva
00:12:08
y Compose redibuja
00:12:10
la UI automáticamente cuando algo
00:12:12
cambia. Además
00:12:14
destacó Datastore para persistir
00:12:16
la sesión ante reinicios de la app
00:12:18
teniendo en cuenta las tokens.
00:12:20
Durante el desarrollo del
00:12:26
proyecto hubo varios problemas interesantes
00:12:27
el principal fue de hardware
00:12:29
que no podía correr dos emuladores al mismo tiempo en la computadora
00:12:31
y además tener IntelliJ abierto con el backend corriendo para probarlo.
00:12:36
La solución que encontré fue simplemente exportarlo como HA al backend
00:12:41
y ejecutarlo directamente desde el terminal.
00:12:45
Consumía muchísimo menos memoria.
00:12:48
Además, durante gran parte del desarrollo utilicé Postman
00:12:50
con las solicitudes que haría el segundo emulador.
00:12:53
si tenía el MyPilot abierto, emulaba MyPilotDriver y viceversa.
00:13:00
El segundo problema fue con los WebSockets.
00:13:06
Al tratar de probarlo desde Android me tiraba al 400.
00:13:08
No me había dado cuenta de que por defecto Spring activa SockJS,
00:13:12
que es una capa de compatibilidad que choca con Android.
00:13:17
La solución fue simplemente deshabilitar SockJS y utilizar WebSocket puro.
00:13:22
El último problema que tuve, que parece ser un clásico de Android, fue que los mensajes
00:13:27
de WebSocket llegaban bien a la aplicación, los veía en el log, pero no se actualizaba
00:13:34
la UI. El problema principal era que el callback se estaba ejecutando en el hilo de I.O. y
00:13:38
no en el principal. La solución fue forzar que se ejecute en el hilo principal con Dispatches
00:13:46
Main.
00:13:51
Tenemos a la derecha la aplicación MyPilot, a la izquierda Postman y una terminal ejecutando la API
00:13:52
Iniciamos la sesión como James Walker que es un viajero que tenemos en la base de datos
00:14:00
Vemos que hace realmente la petición
00:14:05
Esperamos que cargue la pantalla
00:14:07
Permitimos el uso de la ubicación en el teléfono
00:14:10
Esto ya es una cosa del emulador
00:14:15
Por defecto define la ubicación como la oficina central de Google
00:14:18
Yo no estoy en California.
00:14:22
Podemos seleccionar cualquier parte del mapa o simplemente apretar el botón Take Me Home.
00:14:24
Ese botón carga de la base de datos al mapa la ubicación del hogar de la persona que está en sesión.
00:14:29
Todas las solicitudes que estamos haciendo las estamos haciendo en Postman utilizando el token de Login como una variable alojada en el entorno Collection de Postman.
00:14:36
Así podemos emular una autenticación válida para el Get que estamos haciendo.
00:14:44
Hacemos este Get para verificar que realmente se está creando un nuevo viaje.
00:14:48
creamos la solicitud del viaje con viajar ahora
00:14:52
y podemos ver en la API que realmente se hace la petición
00:14:57
ahí vemos el viaje nuevo creado con su nuevo ID
00:15:02
y ahora lo que vamos a hacer es cambiar la petición a post
00:15:13
para asignar el conductor a este viaje solicitado
00:15:17
en este caso evidentemente el token que estamos usando
00:15:21
es de un login de un rol conductor, más específicamente el conductor con los datos de nombre Carlos Ramírez.
00:15:24
Y ahí podemos ver efectivamente cómo sin tocar nada cambia el banner de la app de MyPilot
00:15:35
y efectivamente cambia el estado del viaje a conductor en camino.
00:15:43
Hacemos el cambio a PUT para cambiar el estado del viaje a iniciar.
00:15:48
y ahí vemos que tanto en la UI como en la base de datos
00:16:02
el estado del viaje ha cambiado en curso
00:16:08
cambiamos el estado de viaje finalizado y nos sale el banner de calificar al conductor
00:16:10
y guardamos
00:16:22
y ese sería todo el ciclo de vida de un viaje de MyPilot
00:16:25
esencialmente MyPilotDriver es muy parecido
00:16:28
simplemente cambia un poco la UI porque no tiene la barra de búsqueda
00:16:32
sino que tiene un switch que al activarlo empieza a escuchar viajes nuevos en la API
00:16:35
usamos aquí un post de un viaje que yo tenía ya preparado
00:16:40
y vemos como de nuevo sin tocar nada
00:16:43
el banner cambia y el estado del viaje aparece
00:16:46
porque es un viaje nuevo
00:16:49
apretamos aceptar, esperamos que cargue
00:16:51
y podemos ver como el estado cambia a ongoing trip
00:16:54
lo que pasa es que realmente el estado del viaje cambió a conductor en camino
00:16:57
en la pantalla podemos ver como se trazan las líneas de directions
00:17:00
hacia el punto de inicio del viaje solicitado
00:17:03
porque tiene que ir a buscar al pasajero
00:17:07
Una vez suponiendo que hayamos llegado ya a buscar al pasajero
00:17:10
Apretamos el botón de Let's Pilot
00:17:15
Que inicia el viaje
00:17:18
Y podemos ver como el mapa se limpia
00:17:20
Y marca la ubicación del destino del viajero
00:17:23
Google Directions carga desde la ubicación del propio teléfono
00:17:26
O el emulador en este caso
00:17:29
Por eso es que está marcándolo desde tan lejos
00:17:31
Una vez apretamos el botón de finalizar viaje
00:17:33
O End Trip o End Journey
00:17:37
damos por terminado el flujo
00:17:38
natural de
00:17:40
MyPilotDriver
00:17:42
En conclusión, si bien este
00:17:43
proyecto fue un reto, fue algo desafiante
00:17:46
fue muy entretenido
00:17:49
me enseñó muchísimo
00:17:50
y me dieron muchas ganas
00:17:52
de seguir desarrollando la app
00:17:54
incluso después de terminar el TFG
00:17:56
y entregarlo, porque creo que realmente
00:17:58
tiene futuro, es una app que
00:18:00
es 1. monetizable
00:18:02
2. resolvió un problema
00:18:04
real y específico
00:18:06
y cotidiano en la vida
00:18:08
de cualquier persona
00:18:10
y tiene mucho potencial
00:18:11
pero en lo que al TFG respecta
00:18:13
ya estaríamos llegando al final
00:18:16
muchísimas gracias por ver
00:18:18
hasta luego, un saludo
00:18:19
- Idioma/s:
- Etiquetas:
- Aprendizaje Basado en Proyectos, Pensamiento Computacional
- Autor/es:
- Juan Herrera
- Subido por:
- Juan José H.
- Moderado por el profesor:
- Lucia San Miguel López (lucia.sanmiguel)
- Licencia:
- Reconocimiento - Compartir igual
- Visualizaciones:
- 9
- Fecha:
- 18 de mayo de 2026 - 23:28
- Visibilidad:
- Público
- Centro:
- IES FRANCISCO DE QUEVEDO
- Duración:
- 18′ 25″
- Relación de aspecto:
- 1.78:1
- Resolución:
- 1920x1080 píxeles
- Tamaño:
- 718.10 MBytes