Android SQlite y Json PDF
Android SQlite y Json PDF
Android SQlite y Json PDF
Proyecto
Ingeniería Fin de Carrera
de Telecomunicación
Ingeniería de Telecomunicación
Formato de Publicación
Aplicación de la Escuela
Android Técnica
de rutinas
Superior de Ingeniería
de entrenamiento adaptadas al
usuario usando SQLite y JSON
Autor:Autor:
F. JavierMirian Franco
Payán Somet Maireles
Tutor: Juan José Murillo Fuentes
Tutor: María Teresa Ariza Gómez
Departamento de Ingeniería
Dep. Teoría de la Señal Telemática
y Comunicaciones
Escuela Técnica
Escuela Técnica Superior de Ingeniería
Superior de Ingeniería
Universidad de Sevilla
Universidad de Sevilla
Sevilla, 2013
Sevilla, 2015
Proyecto Fin de Carrera
Ingeniería de Telecomunicación
Autor:
Tutor:
Profesor Titular
Sevilla, 2015
Proyecto Fin de Carrera: Aplicación Android de rutinas de entrenamiento adapta-
das al usuario usando SQLite y JSON
El tribunal nombrado para juzgar el trabajo arriba indicado, compuesto por los
siguientes profesores:
Presidente:
Vocal/es:
Secretario:
Afortunada soy, pues muchas son las personas a las que debo y quiero agradecer por
un motivo u otro a lo largo de esta difícil y maravillosa etapa. El agradecimiento
más grande se lo doy a mis padres, Antonio y Reme, gracias por toda una vida llena
de trabajo y sacrificio para que mi hermano y yo tuviésemos la mejor educación.
Desde las clases de música hasta nuestras respectivas carreras, habéis invertido y
apostado siempre por nuestra formación. Ni un momento lo habéis dudado, aunque
algún que otro año no hubiera beca, siempre habéis sabido tirar hacia adelante y
superar todas las adversidades, por eso no sólo os doy las gracias, también toda
mi admiración porque sois los mejores padres del mundo. Gracias a mi hermano,
Antonio, por ser durante toda mi vida un ejemplo a seguir, y por su implicación
directa en este proyecto, te admiro por el gran profesional que eres, pero también
por la persona humilde y trabajadora en que te has convertido. No quiero dejar pasar
este momento tan importante en mi vida sin mencionar a mi hermana Nuria, que la
vida nos arrebató tan injustamente, siempre estarás presente en mis pensamientos y
siempre te querré estés donde estés.
En segundo lugar quiero agradecer a mi pareja, Jesús Ángel con los que llevo
compartiendo casi siete maravillosos años. Llegaste justo en el momento en que
más lo necesitaba. Gracias por tu apoyo incondicional, por tus consejos y por tus
palabras de aliento en los momentos más duros de esta etapa.
Gracias a toda mi familia, primos, cuñada, tíos, y abuelos, los que están y los que
desgraciadamente no, siempre he recibido vuestro apoyo y vuestros animos para
seguir hacia adelante.
I
mis “ansias”. Te conozco desde antes de lo que pueda recordar y siempre has estado
ahí, gracias por todo lo que hemos vivido juntas y por nuestra increíble conexión
mental. Gracias también a mi “hermano” Rafa, mi primer verdadero amigo de la
universidad, por todos los momentos que hemos vivido, por tus cancioncitas, por
los almuerzos de “tupper” y simplemente por ser la persona maravillosa que eres.
Gracias a mis amigas de toda la vida, mis “farrus” Virginia, María, Esther, Ce-
lia, Belén y Viki. Es un gustazo ver cómo hemos cambiado todas, y sin embargo
seguimos igual de unidas, a pesar de la distancia.
Sevilla, 2015
Índice Abreviado
1. Introducción 1
1.1. Contexto 1
1.3. Antecedentes 3
2. Tecnologías utilizadas 13
2.1. Android 13
2.2. Java 19
2.3. Eclipse 21
2.4. JSON 23
2.5. SQLite 25
III
IV Índice Abreviado
3. Modelado de la información 31
3.1. Modelo EE-RR 31
4. Arquitectura y análisis 35
5.1. Introducción 51
Bibliografía 191
Índice
VII
VIII Índice
XIII
XIV Índice de Figuras
XVII
1 Introducción
1.1 Contexto
Por otro lado, nos encontramos ante una sociedad con un ritmo cada vez más
frenético en el que se dispone de poco tiempo para la realización de ejercicio
o para acudir a un centro especializado. Tampoco hay que olvidarse de la crisis
económica mundial de los últimos años, en los que para muchas personas, acudir a
un gimnasio o centro de entrenamiento ha quedado relegado a segundo plano por
motivos económicos. De aquí surgen aplicaciones deportivas de diferentes clases.
Nosotros nos centraremos en las especializadas en vídeos de rutinas de ejercicios y
fitness.
1
2 Capítulo 1. Introducción
aquellas personas que por alguna causa no pueden acudir a un centro profesional.
1.3 Antecedentes
En este apartado presentaremos los antecedentes que han servido de base para la
realización de este proyecto.
1.3.1 Referencias
Aplicación Android para la rehabilitación física del usuario usando MySQL, PHP y
codificación JSON
Quizás el uso de esta aplicación pueda parecer más ventajoso en el ámbito médico
de rehabilitaciones físicas, sin embargo, con nuestra aplicación también podríamos
cubrir estas necesidades. Al ser creadas las rutinas por los propios usuarios, podrían
diseñar rutinas de ejercicios de cualquier tipo y naturaleza, como por ejemplo rutinas
específicas para distintas dolencias.
De nuevo vemos que el catálogo de ejercicios es muy rígido, pues son la propia
empresa quienes lo imponen. Además sólo cuenta con cuatro categorías de ejercicios.
Esta aplicación propone un objetivo, realizar siete minutos de ejercicio al día durante
siete meses, no se necesita ningún aparato de ejercicio, y viene con muchas imágenes
explicativas. Además, como en un juego, te da tres vidas, en el momento que dejas
de realizar ejercicio tres días en un mes, el progreso comenzará de nuevo.
Sin embargo esta aplicación tiene la desventaja de que no contiene vídeos para
ver mejor la ejecución del ejercicio, y de que además debes ceñirte a su plan de
entrenamiento.
A esta aplicación le falta quizás algún tipo de interactividad con el usuario y más
dinamismo en forma de vídeos que lo hagan más atractivo al usuario.
Esta aplicación (Figura 1.5) es específica para esta zona del cuerpo. Cada rutina
de ejercicios viene bien especificada, el tiempo que ha de durar cada ejercicio y el
tiempo de descanso entre cada uno de ellos. También cuenta con vídeos explicativos.
Sin embargo es específica sólo para un área. Para ejercitar otras áreas deben de
descargarse otro tipo de aplicaciones.
Esta aplicación Adidas (Figura 1.6) tiene una interfaz de usuario muy trabajada
y atractiva al usuario. Necesita la creación de una cuenta para acceder, para así
almacenar la información propia del usuario. Tiene la opción de elegir el tipo de
actividad, o bien correr o hacer ejercicios.
1.4 Descripción de la solución 7
1.4.1 Objetivos
Podríamos adquirir nuevas rutinas desde una página web por ejemplo, y descargar-
las en un .zip en nuestro teléfono móvil. Por tanto la aplicación podrá reconocer estos
.zip que se encontrará en la carpeta Download del dispositivo y descomprimirlos en
una carpeta que crea la aplicación, la carpeta storage/emulated/0/app_deportes/ruti-
nas/.
Cabe mencionar de nuevo que queda fuera del alcance de este proyecto la reali-
zación de la web de donde poder descargarse las rutinas en .zip, simplemente se
partirá de que estas rutinas ya han sido descargadas en Download.
También podemos mencionar que cada cuenta de usuario dispone de un alto nivel
de seguridad, pues la contraseña de usuario nunca se almacena en claro en la base
de datos sino que se hace mediante la adición de una salt originada aleatoriamente,
1.4 Descripción de la solución 9
posteriormente, esta pareja de contraseña + salt se pasará por una función hash. El
salt y este hash es lo que verdaderamente almacenamos en la base de datos.
1.4.2 Funcionalidades
Una vez se accede a una rutina determinada, aparecerá una lista con sus vídeos,
pero sólo aquellos que se correspondan con el nivel de forma que ha indicado el
usuario en su registro.
Si seleccionamos sobre uno de los elementos de la vista nos llevará a una nueva
actividad con los siguientes elementos:
– Número de repeticiones.
– Material necesario.
– Músculos implicados.
1.4.3 Arquitectura
Veremos la arquitectura de nuestra solución que va a ser una arquitectura muy simple,
de la que el usuario saldrá beneficiado en rapidez de ejecución.
Controlador : Esto sería la lógica Java de nuestra aplicación. Es quien realiza todos
los cálculos y operaciones, y quien se comunica con el resto de componentes
de la arquitectura. Realiza las consultas a la base de datos SQLite, accede a
los elementos de la vista y lee y escribe sobre la memoria interna de nuestro
dispositivo.
Base de datos SQLite : Base de datos que utiliza la aplicación para administrar
toda la información tanto del usuario como de las rutinas y vídeos de la
aplicación, o sobre los ejercicios que ha realizado un usuario. Recibirá las
peticiones Android de los usuarios.
Cabe mencionar que las carpetas de rutinas tienen una estructura bien definida
para que funcionen correctamente en la aplicación. Cada rutina contará con un
archivo JSON, también con una organización interna específica, que consta de un
array de dos objetos, uno para la descripción de la rutina, y el otro objeto contiene un
array con la información de todos los vídeos. La carpeta de una rutina también deberá
contener los vídeos en formato mp4. Hemos de tener en cuenta que el nombre que
le pongamos al vídeo tiene que corresponderse con el campo Nombre que hayamos
añadido en el archivo JSON para ese vídeo.
Cualquier usuario que construya una carpeta rutina de este tipo, será válida para
su ejecución en la aplicación.
En el cuarto capítulo, se hará uso del UML (Unified Modeling Language) para
describir toda la estructura de nuestro software. Se trata de un lenguaje gráfico de
modelado para visualizar y especificar métodos o procesos. Haremos uso de sus
diferentes tipos de diagramas: diagramas de casos de uso, diagramas de clase y
diagramas de secuencia.
2.1 Android
13
14 Capítulo 2. Tecnologías utilizadas
diferente es que está basado en Linux, un núcleo de sistema operativo libre, gratuito
y multiplataforma.
2.1.2 Historia
Android Inc. era un sistema operativo prácticamente desconocido hasta que Google
lo adquirió en el año 2005. Ese mismo año, comienzan a trabajar en la creación de
una máquina virtual Java optimizada para móviles.
En el año 2007 se creó el consorcio Handset Alliance formado por varias empresas,
entre ellas Google, con el objetivo de desarrollar estándares abiertos para móviles.
Uno de sus objetivos era promover el diseño y la difusión de la plataforma Android.
En noviembre del 2007 se lanza una primera versión del Android SDK. Al año
siguiente apareció el primer teléfono con Android (T-Mobile G1). En octubre Google
libera el código fuente de Android bajo licencia de código abierto Apache. Ese mismo
mes se abre Android Market para la descarga de aplicaciones. En 2009 se lanza la
versión 1.5 del SDK que incorpora el teclado en pantalla. A finales del 2009 se lanza
la versión 2.0 y durante el 2010 las versiones 2.1, 2.2 y 2.3.
En 2011 se lanzan las versiones 3.0, 3.1 y 3.2 específica para tablets y la 4.0 tanto
para móviles como para tablets. Durante este año Android se consolida como la
plataforma para móviles más importante, alcanzando cuotas de mercado del 50 %.
En 2012 Google reemplaza su Android Market por Google Play Store y se lanzan
las versiones 4.1 y 4.2 del SDK. Android mantiene su espectacular crecimiento
alcanzando cuotas de mercado del 70 %.
En 2013 se lanzan las versiones 4.3 y 4.4 y en 2015 se lanza la versión 5.0
(Lollipop). A finales de este año la cuota de mercado alcanza el 85 %.
• Portabilidad asegurada.
Las aplicaciones finales son desarrolladas en Java, lo que nos proporciona que
podrán ser ejecutadas en cualquier tipo de CPU. Esto se consigue gracias al
concepto de máquina virtual.
Su arquitectura (Figura 2.2) está formada por 4 capas, y cada una de ellas están
basadas en software libre.
• El núcleo Linux.
El núcleo de Android está formado por el sistema operativo Linux. Esta capa
proporciona servicios como la seguridad, el manejo de la memoria, el multiproceso,
la pila de protocolos o el soporte de drivers para dispositivos.
• Runtime de Android.
• Librerías nativas.
2.1 Android 17
• Entorno de aplicación.
Proporciona una plataforma de desarrollo libre para aplicaciones con gran riqueza
e innovaciones (sensores, localización, servicios,. . . ). Esta capa ha sido diseñada para
facilitar la reutilización de componentes. Los servicios más importantes que incluye
son: Views (extenso conjunto de vistas), Resource Manager (acceso a recursos que
no son en código), Activity Manager (maneja el ciclo de vida de las aplicaciones),
Notification Manager (permite a las aplicaciones mostrar alertas personalizadas en
la barra de estado) y los Content Providers (mecanismo para acceder a datos de otras
aplicaciones).
• Aplicaciones.
Según un estudio realizado por la empresa Gartner Group sobre la evolución del
mercado según el número de terminales vendidos (Figura 2.4) puede observarse el
espectacular ascenso de la plataforma Android desde el 2009 al 2014.
En el segundo cuatrimestre del 2015, según datos de esta misma empresa, reveló
que actualmente el 82.2 % de la cuota de mercado la posee Android, seguida de
Apple con un 14.6 % y con Microsoft cerrando el podio (Figura 2.5).
año anterior, mientras que su gran competidor Apple ha descendido en 0.2 puntos.
2.2 Java
bajo nivel. Las aplicaciones de Java son generalmente compiladas a bytecode (clase
Java) que puede ejecutarse en cualquier máquina virtual Java (JVM) sin importar la
arquitectura de la computadora subyacente.
• Orientado a objetos. Diseño del software de forma que los distintos tipos
de dato que usen estén unidos a sus operaciones. Así, los datos y el código
(funciones o métodos) se combinan en entidades llamadas objetos. Un objeto
puede verse como un paquete que contiene el “comportamiento” (el código) y
el “estado” (datos).
El diseño de Java y su robustez hace que Java sea muy utilizado en distintos
ámbitos:
• En el navegador web.
2.3 Eclipse 21
• En sistemas de servidor.
En la parte del servidor, Java es más popular que nunca, desde la aparición de
la especificación de Servlets, componentes de la parte del servidor de Java,
encargados de generar respuestas a las peticiones recibidas de los clientes.
• En aplicaciones de escritorio.
2.3 Eclipse
src : Carpeta que contiene el código fuente de la aplicación. Todos los ficheros
Java se almacenarán en esta carpeta.
gen : Carpeta que contiene el código generado de forma automática por el SDK.
Nunca hay que modificar de forma manual estos ficheros. Dentro encontramos.
R.java : Define una clase que asocia los recursos de la aplicación con identi-
ficadores. De esta forma podremos acceder a los recursos desde el código
Java.
assets : Carpeta que puede contener una serie arbitraria de ficheros carpetas que
podrán ser usados por la aplicación. A diferencia de la carpeta res, nunca se
modifica el contenido de esta carpeta, ni se les asociará un identificador.
libs : Código jar con las librerías que quieras usar en tu proyecto.
res : Carpeta que contiene los resursos usados por la aplicación. Dentro encontra-
remos una serie de carpetas entre las que destacan:
layout : Contiene los ficheros xml con las vistas de la aplicación. Las vistas
nos permitirán configurar las diferentes pantallas que componen la interfaz
del usuario.
2.4 JSON 23
2.4 JSON
• Valores JSON.
• Objeto JSON.
1 {
2 "Nombreejercicio": "Extension de pierna",
3 "Repeticiones": 25
4 }
• Array JSON.
1 [
2 {
3 "Nombre": "video01",
4 "Titulo": "Extension de pierna",
5 "Subtitulo": "zona: piernas",
6 "Descripcion": "Descripcion extensa del ejercicio",
7 "Estado de forma": "Bajo",
8 "Repeticiones": 6
9 },
10 {
11 "Nombre": "video02",
12 "Titulo": "Contraccion abdomen",
13 "Subtitulo": "zona: abdomen",
14 "Descripcion": "Descripcion extensa y detallada del
ejercicio2",
15 "Estado de forma": "Medio",
16 "Repeticiones": 4
17 }
18 ]
2.5 SQLite 25
2.5 SQLite
SQLite es un sistema gestor de base de datos relacional. Lo que hace único a SQLite
es que se considera una solución embebida y muy ligera, por lo que se trata de una
solución ideal para dispositivos móviles.
2.5.1 Características
• Requiere poca memoria en tiempo de ejecución (250 kB), por lo que va a ser
muy rápida.
26 Capítulo 2. Tecnologías utilizadas
La base de datos que crea una determinada aplicación es sólo accesible por ella,
otras aplicaciones no podrán acceder. Una vez creada, la base de datos se almacena en
la carpeta /data/data/<nombre_del_paquete>/databases de un dispositivo Android.
• Sitios web.
SQLite funciona como sistema gestor de base de datos para páginas web de
tráfico bajo-medio. La cantidad de tráfico web que SQLite puede manejar
depende de la frecuencia con que la página web utiliza su base de datos. En
general, cualquier sitio que recibe menos de 100k visitas/día debe funcionar
bien con SQLite, aunque este límite es muy conservador. La propia página web
de SQLite https://www.sqlite.org/ utiliza SQLite y maneja sobre 400k-500k
2.5 SQLite 27
peticiones HTTP por día, el cual el 15 %-20 % de estas peticiones tocan la base
de datos.
• Análisis de datos.
Muchas empresas, utilizan SQLite como si fuera una “caché” que contiene los
datos más utilizados en RDBMS empresariales. Esto reduce la latencia, ya que
la mayoría de consultas irán a esta “caché”. También se reduce la carga de la red
y en el servidor de datos central. Gracias a esto, en muchos casos la aplicación
del lado del cliente puede continuar operando durante interrupciones de la red.
Para los programas que tienen una gran cantidad de datos que tienen que ser
ordenados de diversas formas.
• Educación y entrenamiento.
El diseño simple y modular de SQLite hace que sea una buena plataforma para
la creación de nuevos prototipos.
28 Capítulo 2. Tecnologías utilizadas
Cada valor almacenado en una base de datos SQLite tiene una de las siguientes
clases de almacenamiento:
Como vemos, SQLite no tiene una clase de almacenamiento boolean, los valores
booleanos se almacenan como enteros 0 (false) o 1 (true). Tampoco tiene una clase
de almacenamiento reservado para recoger fechas o tiempo. En su lugar, SQLite
los almacenará como valores TEXT, REAL o INTEGER. En el caso de nuestra
aplicación, hemos elegido almacenarlos como TEXT.
La forma recomendada para crear una nueva base de datos SQLite, es instanciar
un objeto de una clase que herede de la clase SQLiteOpenHelper y sobrescribir el
método onCreate(), en el cuál se ejecutarán los comandos para crear las tablas nece-
sarias para estructura de la base de datos. Para escribir y leer de la base de datos, se
usan los métodos getWritableDatabase() y getReadableDatabase() respectivamente.
Ambos métodos devuelven un objeto de la clase SQLiteDatabase que representa a
la base de datos y proporciona métodos para su gestión como INSERT, DELETE,
QUERY Y RAWQUERY (sentencia de consulta SQL que devuelve los resultados
en forma de objeto Cursor). Este objeto Cursor que devuelve, es el mecanismo con
el que se puede navegar por los resultados de una consulta de la base de datos, y
leer filas y columnas.
2.6 Android Plot 29
En este apartado se realizará una descripción de cada una de las tablas de nuestra base
de datos SQLite “app_deportes”, indicando los campos por los que está formada.
usuarios Tabla que almacena los datos principales de los usuarios, tales como
nombre, apellidos, usuario, hash y salt (para encriptar la contraseña), e_mail y
su estadodeforma. Como clave primaria está el campo “Id” para identificarlo
de manera única en la tabla.
rutinas Contiene las distintas rutinas y sus datos, que han sido descomprimidas
por el usuario desde la carpeta Download. Esta tabla, cuando se crea, se le
introduce automáticamente la rutina “GAP”, que está incorporada dentro del
apk de la aplicación. Contiene también información sobre cada rutina: nombre,
una descripción breve y la ruta que tendrá dentro del dispositivo una vez des-
comprimida, y que será del tipo: /storage/emulated/0/app_deportes/rutinas/. . .
31
32 Capítulo 3. Modelado de la información
ejercicios Contiene cada uno de los ejercicios que contienen las rutinas, junto
con toda su información y la ruta a la que pertenecen. Sus campos son ruta_-
ejercicio (la ruta del vídeo), título, subtítulo, descripción (descripción extensa
que el usuario debería leer antes de ejecutar el ejercicio, pues incluye los
músculos implicados en la realización, o cuál es la forma correcta de ejecu-
tarlo), estadodeforma (el estado de forma física para el que resulta óptimo),
repeticiones (número de repeticiones que deben de hacerse de este ejercicio
para que resulte efectivo) y el id_rutina, identificador de la rutina a la que
pertenece el ejercicio. Como clave primaria está el campo “id_ejercicio” para
identificar a cada ejercicio de manera única en la tabla.
usuario_rutina Contiene cada una de las rutinas que tiene en su lista, un usuario
determinado. Contiene tan sólo dos campos, y ambos son claves primarias,
los campos id_usuario e id_rutina.
Nos sirve tanto para mostrar la lista de rutinas personalizadas de cada usuario,
como para llevar una cuenta de cuántos usuarios tiene una rutina en concreto.
Si un usuario elimina una rutina de su lista, no se eliminará de la carpeta del
dispositivo (. . . /app_deportes/rutinas/) a no ser que fuera el único usuario que
poseía esa rutina en su lista.
4 Arquitectura y análisis
En este apartado haremos uso del UML (Lenguaje Unificado de Modelado) para
analizar nuestro sistema de forma detallada. Lo examinaremos por medio de tres tipos
de diagramas. Diagramas de casos de uso, diagramas de secuencia y el diagrama de
clase.
A continuación se expone una descripción de los casos de uso y sus diagramas para
una mejor comprensión, posteriormente se especifican los actores que harán uso del
sistema con el fin de identificar todos los casos de uso a desarrollar.
Usuario: Este actor es el usuario del sistema, el cual podrá acceder a las funciones
del sistema.
35
36 Capítulo 4. Arquitectura y análisis
Cursos alternativos
Ninguno.
4.1 Diagramas de Casos de uso 37
Cursos alternativos
Ninguno.
4.2 Diagramas de secuencia 39
Una vez iniciada la aplicación, el usuario debe pulsar sobre el botón “Registrar-
se” para que pueda iniciar sesión posteriormente. Al pulsarlo, la actual actividad
“MainActivity”, lanza un Intent que inicia una nueva pantalla, “NuevoUsuario”, en
la que se mostrará al usuario un formulario que debe rellenar con sus datos. Los
campos de este formulario son:
40 Capítulo 4. Arquitectura y análisis
• Nombre.
• Apellidos.
• Usuario.
• Contraseña.
• E-mail.
1. Comprueba que todos los campos estén rellenos. En caso contrario creará un
objeto toast que muestre un mensaje por pantalla de unos segundos, informando
al usuario.
2. Si todos los campos están rellenos, comprueba si el valor del campo usuario
introducido ya existe en la tabla usuarios de la base de datos. Si esto ocurre se
informa con un toast al usuario para que introduzca un valor diferente.
4.2 Diagramas de secuencia 41
Vamos a ver el proceso que se sigue cuando un usuario inicia sesión (Figura 4.6).
Veamos cómo accede el sistema a los ejercicios de una rutina (Figura 4.7).
Si el usuario pulsa sobre una de las rutinas, el sistema activará la pantalla ListE-
jercicios, pasándole como extras el identificador y el path de la rutina seleccionada.
En esta actividad, el sistema:
2. Obtiene el estado de forma del usuario, y obtiene sólo los ejercicios de la rutina
que coinciden con el estado de forma del usuario: listaEjEstadoForma(ruta,
estado)
El siguiente diagrama nos muestra el proceso de acceso a otras rutinas que ya han
descomprimido otros usuarios en la carpeta /app_deportes/rutinas/, y que por tanto
no será necesario repetir este proceso (Figura 4.8).
Si el usuario hace click sobre una de ellas, y pulsa “Aceptar”, el sistema intro-
ducirá una nueva fila en la tabla usuario_rutina, correspondientes al id de la rutina
seleccionada y al id del usuario: introRutinaUsuario(id_usuario, id_rutina). Tras
esto, el sistema vuelve a la actividad Inicio.
Una vez se han descomprimido todos los archivos correctamente, mete los vídeos
46 Capítulo 4. Arquitectura y análisis
Este diagrama (Figura 4.10) nos muestra la secuencia de acciones que realiza el
sistema cuando el usuario selecciona un ejercicio de una rutina.
• Si presiona “Comenzar”:
• Si toca “Terminado”:
4.2 Diagramas de secuencia 47
5.1 Introducción
51
52 Capítulo 5. Aplicación de entrenamiento. Interfaz de usuario y funcionalidad.
• Acceso al rendimiento semanal del usuario mediante una gráfica y una lista
detallada.
• Acceso a las rutinas que otro usuario ha cargado en el dispositivo con anterio-
ridad.
• Parar el vídeo.
• Pulsar el botón “Comenzar”. Con este botón, el sistema entiende que el usuario
ha decidido realizar el ejercicio e inicia una serie de funciones:
– Reproduce el vídeo tantas veces como sea necesario, para que haya tantas
repeticiones del ejercicio como las que debe realizar el usuario. De esta
forma se consigue cierta interactividad entre la aplicación y el usuario.
2. Autentificarse.
3. Registrarse.
El usuario podrá rellenar estos campos con los valores que desee, excepto la
respuesta a la pregunta “¿Cuál es su estado de forma?” que deberá elegir una de las
tres opciones. El sistema comprobará que no exista otro usuario con el mismo valor
de campo usuario, en cuyo caso, notificaremos al usuario para que elija otro valor
para este campo.
Una vez el usuario haya introducido todos los valores, pulsará el botón “Regis-
trarse”, y se comprobará que todos los campos hayan sido rellenados. Tras dicha
comprobación, en caso de que alguno/s estén en blanco, se informará al usuario.
5.2 Pantalla principal 55
En ella, el usuario deberá introducir su usuario y e-mail con los que está regis-
trado. Al pulsar “Enviar”, si el e-mail introducido no se corresponde con el e-mail
almacenado de este usuario, se informará del error (Figura 5.7).
5.2 Pantalla principal 57
Figura 5.7 Error: el e-mail introducido no es con el que el usuario está registrado.
Figura 5.8 Mensaje de espera mientras el sistema realiza todas las acciones.
58 Capítulo 5. Aplicación de entrenamiento. Interfaz de usuario y funcionalidad.
Esta actividad aparece una vez el usuario ha iniciado sesión correctamente, y consta
de los siguientes componentes, que podemos ver en la imagen:
Saludo : Aparece debajo del título, nos muestra una frase de bienvenida personali-
zada con el nombre del usuario que ha iniciado sesión.
• Configuración.
• Acerca De...
• Cerrar Sesión.
5.3 Pantalla Inicio 59
5.3.1 Mi rendimiento
Si el usuario pulsa sobre “Mi rendimiento”, se mostrará una nueva pantalla (Figura
5.10).
Zona gráfica : En la parte superior se sitúa una gráfica en la que el usuario puede
ver su rendimiento semanal de una manera vistosa e intuitiva. En el eje de
abscisas se representan los días de la semana: L, M, X, J, V, S, D y en el eje
de ordenadas el tiempo indicado en minutos que el usuario ha dedicado cada
día a entrenarse.
5.3.2 Configuración
• Modificar la cuenta.
• Cambiar la contraseña.
• Eliminar la cuenta.
5.3.3 Acerca de
Cuando el usuario pulsa sobre el botón “Acerca De”, se muestra una pantalla con
información de la aplicación que es de interés al usuario.
62 Capítulo 5. Aplicación de entrenamiento. Interfaz de usuario y funcionalidad.
Cuando el usuario pulsa sobre “Cerrar sesión”, se saldrá del servicio automáti-
camente y volverá a la pantalla principal de la aplicación. Pulsando dicho botón
será la única forma de volver a la pantalla principal ya que se ha bloqueado el
funcionamiento del botón “atrás” del dispositivo en la actividad Inicio.
En el caso en que el usuario pulse sobre el botón “Cargar más”, aparecerá una
actividad (Figura 5.17) en la que podrá verse un listado de las rutinas que no posee
el usuario en su lista, pero que han sido descomprimidas por otros usuarios de
la aplicación en este dispositivo y que se encuentran en la carpeta /sdcard/app_-
deportes/rutinas/ de la memoria del dispositivo. Si selecciona una y pulsa aceptar,
se incluirá ya en su lista de rutinas.
Cuando el usuario pulse sobre una de las rutinas de su lista, se le mostrará una
interfaz (Figura 5.20) con los siguientes componentes:
Figura 5.20 Listado de ejercicios de una rutina, según el estado de forma del usuario.
• Imagen de un instante del vídeo del ejercicio, para que el usuario pueda hacerse
una idea de en qué consistirá.
• Breve descripción del ejercicio de a lo sumo dos líneas. En nuestra rutina GAP
la usamos para mostrar la zona general que se ejercitará.
Si el usuario pulsa sobre uno de los ejercicios de la rutina, el sistema mostrará una
nueva interfaz al usuario con estos elementos (Figura 5.21):
• Play/Pause: Reproduce el vídeo una vez para que el usuario pueda ver
en qué consiste. Si está reproduciéndose y lo pulsa otra vez, el vídeo se
pondrá en pausa, y si pulsa de nuevo, continuará la reproducción por
donde lo había dejado.
Cada rutina, incorpora vídeos según los tres niveles de forma que permite la
aplicación y cada vídeo incluye una descripción detallada señalando aspectos como
en qué consiste el ejercicio, los materiales, los aspectos a destacar o los músculos
implicados.
69
70 Capítulo 6. Conclusiones y líneas futuras del proyecto
automáticamente el archivo JSON, con los datos introducidos por el usuario que
la está creando en un formulario. Se debe dar la opción de que cualquier usuario
pueda descargarse las rutinas creadas por cualquier otro usuario, e incluso la opción
de puntuarlas, recomendarlas o realizar comentarios sobre ella. Podría considerar-
se la opción de sacar provecho económico, ya sea pagando por algunas rutinas o
incorporando función publicitaria.
Podría incluirse una percepción subjetiva del esfuerzo, de forma que el usuario se
evaluara así mismo su nivel de “cansancio” después del entrenamiento, con el fin de
poder progresar al grado de intensidad más adecuado de cada sujeto. Así mismo,
podría incorporarse gráficos sobre esta progresión.
Una vez concluidas todas las fases de desarrollo de la aplicación, podemos sacar las
conclusiones de nuestro trabajo.
Hemos construido una aplicación que ofrece al usuario libertad en muchos aspec-
tos. Libertad para escoger el momento de entrenamiento y libertad en el sentido en
que ofrece al usuario la posibilidad de personalizar el contenido de su aplicación.
El usuario puede escoger sólo las rutinas que le interese y además puede crear sus
propias rutinas y compartirlas con otros usuarios. En este sentido, la aplicación
gana en utilidad, pues puede moverse en muchos entornos, desde rutinas de fitness
y ejercicios de rehabilitación a deportes más especializados como pilates, yoga,
etc, el límite lo imponen los propios usuarios. En definitiva, hemos diseñado una
aplicación horizontal, pues sirve para cualquier tipo de rutinas deportivas.
6.2 Conclusiones finales 71
A.1 Introducción
73
74 Capítulo A. Seguridad en los datos sensibles de las aplicaciones
Vamos a ver mecanismos para proteger las credenciales y datos del usuario, en el
caso de que no quede más remedio que almacenarlos en el dispositivo, como es el
caso de nuestra aplicación.
Los hash o funciones de resumen son algoritmos que consiguen crear a partir de una
entrada (ya sea un texto, una contraseña, un archivo, etc.) una salida alfanumérica
de longitud normalmente fija que representa un resumen de toda la información
que se le ha dado, es decir, a partir de los datos de la entrada crea una cadena que
sólo puede volverse a crear con esos mismos datos. Tiene varios objetivos, como
asegurar que no se ha modificado un archivo en una transmisión, hacer ilegible
una contraseña (que es para lo que la utilizamos en nuestra aplicación), o firmar
digitalmente un documento.
Este algoritmo de criptografía usa algoritmos que asegura que con la respuesta (o
hash) nunca se podrá saber cuáles han sido los datos de entrada, no son reversibles,
lo que indica que es una función unidireccional y que comúnmente se les llama
algoritmos de cifrado de una sola vía. Sabiendo que los resúmenes son de longi-
tud fija, y normalmente menor que la longitud de los datos de entrada podríamos
preguntarnos si podrían repetirse estos hashs. La respuesta teóricamente es que sí,
ya que no es posible tener una función hash perfecta (que no repita la respuesta),
pero esto no supone ningún problema ya que si se consiguieran dos hash iguales los
contenidos serían totalmente distintos.
Las funciones hash son muy utilizadas, una de las utilidades que tiene es la de
proteger la confidencialidad de una contraseña, ya que si estuviera en texto plano
podría ser accesible por cualquiera y aun así no podrían ser capaces de resolverla.
Para saber si una contraseña que está guardada en una base de datos, es igual a
la que hemos introducido, no se descifra el hash (ya que es imposible), sino que se
aplicará la misma función hashing de resumen a la contraseña que hemos introducido,
y se comparará con el resultado que teníamos guardado.
Sin embargo hay que tener en cuenta que no necesariamente el usuario está
seguro usando alguno de estos algoritmos de hash, ya que también existe lo que se
denominan “Ataques por Diccionario” (Dictionary Attack), que consiste en utilizar
bases de datos con los hash resultantes de cifrar los tipos de claves de acceso más
comunes, incluso puede conseguirse este tipo de bases de datos en línea. Entonces,
¿cuál es la solución para proteger las contraseñas de los usuarios de este tipo de
ataques? En estos diccionarios se encuentran claves deficientes o pobres, por lo
que el problema se reduciría si los usuarios escogieran claves menos evidentes. Lo
mejor para evitar este tipo de ataques es lo que se conoce como “Salt key”, que no
es más que una cadena de texto que se agrega al texto original antes de ser cifrado.
De esta forma el atacante no puede comparar los hash ya que necesitaría un nuevo
diccionario, es más, incluso si el atacante llegara a conocer el Salt, no le serviría
de nada, ya que los diccionarios existentes no tendrían las cadenas resultantes de
utilizarlo. El programador sólo tiene que tener en cuenta agregarlo de igual forma
cada vez que compare el hash guardado con el producido a la hora de validar la
clave.
Todo este proceso puede mejorarse aún más y complicarse tanto como se desee
agregando más funciones hashing, tantas iteraciones como se desee o cálculos
recursivos de hash, pero deberá antes pensarse el tiempo y recursos que podría tomar
un cálculo demasiado complejo en relación a la cantidad de usuarios.
76 Capítulo A. Seguridad en los datos sensibles de las aplicaciones
Otro uso que tiene esta función es la de asegurar la integridad de los datos, por
ejemplo en páginas webs que proporcionan descargas de archivos grandes, dan
también el resumen del archivo y la función usada. Esto es un método para saber si
un documento está íntegro tras su recepción.
Nuestra aplicación guarda el password de forma muy segura ya que no sólo realiza
el hash, sino que además hemos añadido una “Salt key” de 16 bytes, generado
aleatoriamente para cada nuevo usuario registrado con una función SecureRandom.
En nuestra base de datos hemos almacenado este salt, y también el resultado de
realizar el hash a la concatenación de contraseña + salt con el algoritmo SHA-1.
Tal vez el uso de estos métodos de encriptado esté más justificado en aplicaciones
que “salen” a internet. En una ejecución normal de nuestra aplicación, no accederá a
internet en ningún momento, sin embargo, si hacemos uso de la función de olvido de
contraseña, sí que lo hará. Además, no hay que olvidar que SQLite guarda los datos
en un único archivo y en texto plano, por lo que será conveniente para casos como
extravíos o robos de los dispositivos móviles o tabletas, que evitará que puedan
acceder a nuestros datos personales.
Anexo B
Manual de instalación
• Eclipse Mars
• Java (JDK)
• Android (SDK)
77
78 Capítulo B. Manual de instalación
Para más comodidad, podemos crear un acceso directo del ejecutable pulsando
botón derecho sobre la aplicación “eclipse”->”Enviar a”->”Escritorio (crear un
acceso directo)”.
Pulsamos "Next".
Pulsamos "Next"
B.2 Instalación de JDK de Java 83
Si queremos desarrollar una aplicación Android, nos hará falta una herramienta de
desarrollo para realizar esta tarea. Ésta es el Android SDK. Para su descarga, iremos
a la siguiente dirección web: https://developer.android.com/sdk/installing/index.html
Pulsamos “Next”.
Pulsamos “Next”.
B.3 Instalación del Android SDK 87
Nos aparecen dos opciones donde podemos elegir si queremos instalar el programa
para todos los usuarios del ordenador o no. Elegimos una de las dos y pulsamos
“Next”.
Tras esto, hemos finalizado la vinculación de las dos herramientas necesarias para
el desarrollo de nuestra aplicación Android en Eclipse. Reiniciamos Eclipse.
Anexo C
En este anexo se encuentran todos los códigos Java y xml que hemos desarrollado
para crear la aplicación. Como ya hemos comentado, el lenguaje Android está
estructurado en dos partes, el xml, donde se diseña la interfaz que verá el usuario
(las distintas actividades) y el código Java que aporta la lógica para poder relizar
todas las operaciones de la actividad en la que estemos.
Vamos a ver cada uno de estos archivos separados por familias, que corresponden
a cada actividad de la que dispone la aplicación. Adjuntaremos también clase que
se encarga de mantener y de realizar todas las operaciones sobre la base de datos,
la clase que gestiona el fichero preferencias, donde almacenaremos el identificador
de usuario de una determinada sesión y la clase que se encarga de leer un fichero
JSON. También mostraremos el archivo de configuración AndroidManifest.xml y el
fichero de recursos string.xml.
C.1.1 MainActivity.java
1
2 package com.deporte.proyectofc;
3
4 /*
5 * Clase principal de la aplicacion. En ella el usuario podra
6 * registrarse, logearse o indicar que ha olvidado la contraseña.
91
92 Capítulo C. Códigos Java y xml de la aplicación Android
7 */
8
9 import java.util.ArrayList;
10 import android.app.Activity;
11 import android.content.Intent;
12 import android.os.Bundle;
13 import android.view.View;
14 import android.widget.EditText;
15 import android.widget.Toast;
16
17 public class MainActivity extends Activity {
18
19 private EditText usuario;
20 private EditText contrasenia;
21 private BasedatosSQLite basedatos;
22
23
24 @Override
25 protected void onCreate(Bundle savedInstanceState) {
26 super.onCreate(savedInstanceState);
27 //Para que la primera vez se cree la base de datos antes de
mostrar la pantalla
28 basedatos= new BasedatosSQLite(this);
29 basedatos.creaSQLitedatabase();
30 setContentView(R.layout.activity_main);
31 usuario= (EditText) findViewById(R.id._usuario);
32 contrasenia= (EditText) findViewById(R.id._contrasenia);
33 }
34
35
43
44 /*Metodo llamado cuando se pulsa sobre el boton Iniciar.
45 *Comprobamos si el usuario y la contraseña existen y accedemos
46 *a la actividad Inicio*/
47 public void lanzarInicio(View view){
48
56 toast.show();
57 }
58 else {
59 Toast toast= Toast.makeText(getApplicationContext(), "El
campo Usuario está vacío.", Toast.LENGTH_SHORT);
60 toast.show();
61 }
62 }
63 //Comprobamos que en el campo contraseña se han introducido
caracteres, en caso contrario se muestra mensaje.
64 else if (v_contrasenia.isEmpty()){
65 Toast toast= Toast.makeText(getApplicationContext(), "El campo
Contraseña está vacío ", Toast.LENGTH_SHORT);
66 toast.show();
67 }
68 //Si los campos usuario y contraseña contienen caracteres,
comprobamos si existe el usuario
69 //y si escorrecta la contraseña.
70 else {
71 if (basedatos.existeUsuario(v_usuario)){
72 //El usuario existe.
73 //Obtenemos el hash y el salt almacenados de este usuario
74 ArrayList<String> hashysalt= basedatos.obtenerHashYSalt(
v_usuario);
75 //Validamos la contraseña introducida
76 boolean correcta= EncriptadoContraseña.validaContrasenia(
v_contrasenia, hashysalt.get(0),
77 EncriptadoContraseña.fromHexString(hashysalt.get(1)
));
78 if (correcta){
79 int id_us= basedatos.obtenerIdUsuario(v_usuario);
80 //Obtenemos el nombre del usuario para el saludo
inicial en la siguiente pantalla
81 String nombre_usuario= basedatos.obtenerNombreUsuario(
id_us);
82 //Guardamos de forma permanente durante la sesion el
id del usuario en fichero preferencias.
83 AlmacenId valor_id= new AlmacenId(this);
84 Intent i = new Intent(this, Inicio.class);
85 valor_id.guardarId(id_us);
86 i.putExtra("nombre_usuario", nombre_usuario);
87 //Lanzamos actividad Inicio
88 startActivity(i);
89 }
90 else {
91 Toast toast= Toast.makeText(getApplicationContext(),
92 "Contraseña incorrecta.", Toast.LENGTH_SHORT);
93 toast.show();
94 }
95 }
96 //No existe el usuario
97 else {
94 Capítulo C. Códigos Java y xml de la aplicación Android
105
106 /*Metodo llamado cuando se pulsa sobre el boton olvido de contraseña
*/
107 public void lanzarOlvidoContrasenia(View view){
108 Intent i= new Intent(this, ContraseniaOlvidada.class);
109 startActivity(i);
110 }
111
112 }
C.1.2 activity_main.xml
11 <TextView
12 android:id="@+id/titulo"
13 android:layout_width="match_parent"
14 android:layout_height="wrap_content"
15 android:layout_gravity="center"
16 android:layout_margin="8dp"
17 android:layout_weight="1"
18 android:gravity="center"
19 android:shadowColor="#222"
20 android:shadowDx="1"
21 android:shadowDy="2"
22 android:shadowRadius="1.5"
23 android:text="@string/app_name"
24 android:textAppearance="?android:attr/textAppearanceLarge"
25 android:textColor="#FFF"
26 android:textStyle="bold" />
27
28 <LinearLayout
29 android:layout_width="match_parent"
30 android:layout_height="wrap_content"
31 android:layout_weight="1"
C.1 Familia MainActivity 95
32 android:orientation="vertical" >
33
34 <TextView
35 android:id="@+id/usuario"
36 android:layout_width="wrap_content"
37 android:layout_height="wrap_content"
38 android:text="@string/usuario" />
39
40 <EditText
41 android:id="@+id/_usuario"
42 android:layout_width="match_parent"
43 android:layout_height="40dp"
44 android:background="@drawable/edittextstyle"
45 android:ems="10"
46 android:hint="@string/introduce_usuario"
47 android:paddingLeft="8dp"
48 android:paddingRight="8dp" >
49
54 <LinearLayout
55 android:layout_width="match_parent"
56 android:layout_height="wrap_content"
57 android:layout_weight="1"
58 android:orientation="vertical" >
59
60 <TextView
61 android:id="@+id/contrasenia"
62 android:layout_width="match_parent"
63 android:layout_height="wrap_content"
64 android:layout_marginTop="18sp"
65 android:text="@string/contrasenia_usuario" />
66
67 <EditText
68 android:id="@+id/_contrasenia"
69 android:layout_width="match_parent"
70 android:layout_height="40dp"
71 android:background="@drawable/edittextstyle"
72 android:ems="10"
73 android:hint="@string/introduce_contrasenia"
74 android:inputType="textPassword"
75 android:paddingLeft="8dp"
76 android:paddingRight="8dp" />
77 </LinearLayout>
78
79 <LinearLayout
80 android:layout_width="match_parent"
81 android:layout_height="wrap_content"
82 android:layout_marginTop="35sp"
83 android:layout_weight="1"
96 Capítulo C. Códigos Java y xml de la aplicación Android
84 android:orientation="vertical" >
85
86 <Button
87 android:id="@+id/iniciar"
88 android:layout_width="225dp"
89 android:layout_height="40sp"
90 android:layout_gravity="center"
91 android:layout_margin="10dp"
92 android:background="@drawable/buttonstyle"
93 android:onClick="lanzarInicio"
94 android:text="@string/login" />
95
96 <Button
97 android:id="@+id/registrarse"
98 android:layout_width="225dp"
99 android:layout_height="40sp"
100 android:layout_gravity="center"
101 android:background="@drawable/buttonstyle"
102 android:onClick="lanzarRegistro"
103 android:text="@string/registro" />
104 </LinearLayout>
105
106 <LinearLayout
107 android:layout_width="match_parent"
108 android:layout_height="wrap_content"
109 android:layout_weight="1"
110 android:orientation="vertical" >
111
112 <Button
113 android:id="@+id/olvido_pass"
114 android:layout_width="225dp"
115 android:layout_height="wrap_content"
116 android:layout_gravity="center"
117 android:background="@android:color/transparent"
118 android:onClick="lanzarOlvidoContrasenia"
119 android:text="@string/olvido_contrasenia"
120 android:textColor="#004C99" />
121 </LinearLayout>
122
123 </LinearLayout>
C.2.1 NuevoUsuario.java
1
2 package com.deporte.proyectofc;
C.2 Familia Nuevo Usuario 97
3
4 /* Clase que registra a un nuevo usuario en la base de datos. */
5
6 import java.security.MessageDigest;
7 import java.security.SecureRandom;
8 import android.app.Activity;
9 import android.database.sqlite.SQLiteDatabase;
10 import android.os.Bundle;
11 import android.view.View;
12 import android.widget.ArrayAdapter;
13 import android.widget.EditText;
14 import android.widget.Spinner;
15 import android.widget.Toast;
16
17 public class NuevoUsuario extends Activity {
18
19 private EditText nombre;
20 private EditText apellidos;
21 private EditText usuario;
22 private EditText contrasenia;
23 private EditText e_mail;
24 private Spinner estadodeforma;
25
26 @Override
27 protected void onCreate(Bundle savedInstanceState) {
28
29 super.onCreate(savedInstanceState);
30 setContentView(R.layout.nuevo_usuario);
31 //Accedemos a los views que vamos a necesitar
32 nombre= (EditText) findViewById(R.id._nombre);
33 apellidos= (EditText) findViewById(R.id._apellidos);
34 usuario= (EditText) findViewById(R.id._usuario);
35 contrasenia= (EditText) findViewById(R.id._contrasenia);
36 e_mail= (EditText) findViewById(R.id._e_mail);
37 estadodeforma= (Spinner) findViewById(R.id.estado);
38 String[] valores = {"Bajo","Medio","Alto"};
39 estadodeforma.setAdapter(new ArrayAdapter<String>(this,
40 android.R.layout.simple_spinner_item, valores));
41 }
42
43 //Al pulsar el boton, nos registramos como nuevo usuario en la base
de datos
44 public void registrarse(View view){
45
96
97 /*
98 * Clase que se encarga de gestionar la encriptacion de la contraseña
para el almacenamiento en la base de datos, y de validar si la
contraseña introducida es correcta.
99 */
100 class EncriptadoContraseña {
101
102 private static final char digits[] = { ’0’, ’1’, ’2’, ’3’, ’4’, ’5’, ’6’
, ’7’, ’8’, ’9’, ’A’, ’B’, ’C’, ’D’, ’E’, ’F’ };
103
104 //Devuelve una salt aleatorio, que será aplicada junto a la contraseñ
a para el hashing.
105 public static byte[] getSalt(){
106
136 }
137
163 }
C.2.2 nuevo_usuario.xml
6 <LinearLayout
7 android:layout_width="match_parent"
8 android:layout_height="wrap_content"
9 android:orientation="vertical" >
10
11 <TextView
12 android:id="@+id/nombre"
13 android:layout_width="wrap_content"
14 android:layout_height="wrap_content"
15 android:layout_margin="8dp"
16 android:text="@string/nombre"
17 android:textColor="#FFF" />
18
19 <EditText
20 android:id="@+id/_nombre"
21 android:layout_width="match_parent"
22 android:layout_height="40dp"
23 android:layout_marginLeft="8dp"
24 android:layout_marginRight="8dp"
C.2 Familia Nuevo Usuario 101
25 android:paddingLeft="8dp"
26 android:paddingRight="8dp"
27 android:background="@drawable/edittextgen"
28 android:ems="10"
29 android:hint="@string/intro_nombre">
30
31 <requestFocus />
32 </EditText>
33
34 <TextView
35 android:id="@+id/apellidos"
36 android:layout_width="wrap_content"
37 android:layout_height="wrap_content"
38 android:layout_margin="8dp"
39 android:text="@string/apellidos"
40 android:textColor="#FFF" />
41
42 <EditText
43 android:id="@+id/_apellidos"
44 android:layout_width="match_parent"
45 android:layout_height="40dp"
46 android:layout_marginLeft="8dp"
47 android:layout_marginRight="8dp"
48 android:paddingLeft="8dp"
49 android:paddingRight="8dp"
50 android:background="@drawable/edittextgen"
51 android:ems="10"
52 android:hint="@string/intro_apellidos" />
53
54 <TextView
55 android:id="@+id/usuario"
56 android:layout_width="wrap_content"
57 android:layout_height="wrap_content"
58 android:layout_margin="8dp"
59 android:text="@string/usuario"
60 android:textColor="#FFF" />
61
62 <EditText
63 android:id="@+id/_usuario"
64 android:layout_width="match_parent"
65 android:layout_height="40dp"
66 android:layout_marginLeft="8dp"
67 android:layout_marginRight="8dp"
68 android:paddingLeft="8dp"
69 android:paddingRight="8dp"
70 android:background="@drawable/edittextgen"
71 android:ems="10"
72 android:hint="@string/intro_usuario"/>
73
74 <TextView
75 android:id="@+id/contrasenia"
76 android:layout_width="wrap_content"
102 Capítulo C. Códigos Java y xml de la aplicación Android
77 android:layout_height="wrap_content"
78 android:layout_margin="8dp"
79 android:text="@string/contrasenia_usuario"
80 android:textColor="#FFF" />
81
82 <EditText
83 android:id="@+id/_contrasenia"
84 android:layout_width="match_parent"
85 android:layout_height="40dp"
86 android:layout_marginLeft="8dp"
87 android:layout_marginRight="8dp"
88 android:paddingLeft="8dp"
89 android:paddingRight="8dp"
90 android:background="@drawable/edittextgen"
91 android:ems="10"
92 android:hint="@string/intro_contrasenia"
93 android:inputType="textPassword" />
94
95 <TextView
96 android:id="@+id/e_mail"
97 android:layout_width="wrap_content"
98 android:layout_height="wrap_content"
99 android:layout_margin="8dp"
100 android:text="@string/e_mail"
101 android:textColor="#FFF" />
102
103 <EditText
104 android:id="@+id/_e_mail"
105 android:layout_width="match_parent"
106 android:layout_height="40dp"
107 android:layout_marginLeft="8dp"
108 android:layout_marginRight="8dp"
109 android:paddingLeft="8dp"
110 android:paddingRight="8dp"
111 android:background="@drawable/edittextgen"
112 android:ems="10"
113 android:hint="@string/intro_e_mail"
114 android:inputType="textEmailAddress" />
115
116 <LinearLayout
117 android:layout_width="match_parent"
118 android:layout_height="wrap_content"
119 android:layout_margin="6dp"
120 android:orientation="horizontal" >
121
122 <TextView
123 android:id="@+id/textView1"
124 android:layout_width="wrap_content"
125 android:layout_height="wrap_content"
126 android:layout_margin="6dp"
127 android:text="@string/nivel"
128 android:textSize="16sp" />
C.3 Familia Inicio de sesión 103
129
130 <Spinner
131 android:id="@+id/estado"
132 android:layout_width="0dp"
133 android:layout_height="wrap_content"
134 android:layout_marginLeft="6dp"
135 android:layout_marginRight="6dp"
136 android:layout_marginTop="12dp"
137 android:layout_weight="1"
138 android:background="@android:drawable/btn_dropdown" />
139 </LinearLayout>
140
141 <Button
142 android:id="@+id/button1"
143 android:layout_width="match_parent"
144 android:layout_height="wrap_content"
145 android:layout_margin="14dp"
146 android:background="@drawable/buttongen"
147 android:onClick="registrarse"
148 android:text="@string/registro" />
149 </LinearLayout>
150
151 </ScrollView>
C.3.1 Inicio.java
1
2 package com.deporte.proyectofc;
3
4 /*
5 * Clase encargada de visualizar la pantalla Inicio, una vez el usuario
ha sido autentificado. Podra elegir entre las rutinas que ya ha
cargado en su lista, o cargar nuevas rutinas. El usuario también
podrá modificar sus datos de usuario y su contraseña, o bien ver su
rendimiento semanal.
6 */
7 import java.util.ArrayList;
8
9 import android.app.ListActivity;
10 import android.content.Intent;
11 import android.database.sqlite.SQLiteDatabase;
12 import android.graphics.Typeface;
13 import android.os.Bundle;
14 import android.view.KeyEvent;
15 import android.view.View;
104 Capítulo C. Códigos Java y xml de la aplicación Android
16 import android.view.animation.Animation;
17 import android.view.animation.AnimationUtils;
18 import android.widget.AdapterView;
19 import android.widget.AdapterView.OnItemClickListener;
20 import android.widget.AdapterView.OnItemLongClickListener;
21 import android.widget.ArrayAdapter;
22 import android.widget.Button;
23 import android.widget.ListView;
24 import android.widget.TextView;
25
26 public class Inicio extends ListActivity{
27
28 private int id_usuario;
29 private BasedatosSQLite basedatos;
30 private ArrayList<String> nombrerutinas= new ArrayList<String>();
31 private ArrayList<Integer> ids_rutinas;
32 private ListView listarutinas;
33 private ArrayAdapter<String> adapter;
34
35 @Override
36 public void onCreate(Bundle savedInstanceState) {
37
38 super.onCreate(savedInstanceState);
39 setContentView(R.layout.inicio);
40
41 TextView titulo= (TextView) findViewById(R.id.titulo_app);
42 TextView pregunta= (TextView) findViewById(R.id.pregunta);
43 Bundle extras = getIntent().getExtras();
44 String nombre_usuario = extras.getString("nombre_usuario");
45 Typeface font = Typeface.createFromAsset(getAssets(),"ActionMan.
ttf");
46 titulo.setTypeface(font);
47 //Asignamos animacion tween al titulo.
48 Animation animacionT= AnimationUtils.loadAnimation(this, R.anim.
animacion);
49 titulo.startAnimation(animacionT);
50
103 bd.close();
104 i.putExtra("id_rutina", id_rutina);
105 startActivity(i);
106 }
107 }
108 });
109
134
135 /* Metodo que lanza la actividad que muestra la grafica de su
rendimiento semanal. */
136 public void lanzarRendimiento(View view){
137 Intent i= new Intent(this, GraficasRendimiento.class);
138 startActivity(i);
139 }
140
141
148
172
173 /* Metodo que recarga la actividad despues de ser detenida, pero al
volver lo hara sin animaciones. */
174 @Override
175 public void onResume(){
176 super.onResume();
177 adapter.clear();
178 ids_rutinas= basedatos.obtenerRutinasUsuario(id_usuario);
179 for(int i=0 ; i<ids_rutinas.size() ; i++){
180 nombrerutinas.add(basedatos.obtenerNombreRutina(ids_rutinas.
get(i)));
181 }
182 //Añadimos el elemento a la lista para cargar más rutinas.
183 nombrerutinas.add("Cargar mas");
184 listarutinas= (ListView) findViewById(android.R.id.list);
185 //Le asignamos un adaptador a este ListView.
186 adapter= new ArrayAdapter<String>(this, R.layout.entrada_rutina, R.
id.nombrerutina, nombrerutinas);
187 listarutinas.setAdapter(adapter);
188 listarutinas.setItemsCanFocus(false);
189 }
108 Capítulo C. Códigos Java y xml de la aplicación Android
190
191 }
C.3.2 listadorutinas.xml
La clase Inicio contiene un elemento ListView que muestra las rutinas que tiene
almacenadas el usuario, por ello, esta clase se compone dos XML:
inicio.xml
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:tools="http://schemas.android.com/tools"
3 android:id="@+id/LinearLayout1"
4 android:layout_width="match_parent"
5 android:layout_height="match_parent"
6 android:orientation="vertical"
7 android:padding="16dp"
8 tools:context="com.deporte.proyectofc.MainActivity" >
9
10 <TextView
11 android:id="@+id/titulo_app"
12 android:layout_width="match_parent"
13 android:layout_height="wrap_content"
14 android:layout_gravity="center"
15 android:layout_marginTop="10dp"
16 android:layout_weight="3"
17 android:gravity="center"
18 android:text="@string/app_name"
19 android:textColor="#FFF"
20 android:textSize="45sp"
21 android:textStyle="bold" />
22
23 <TextView
24 android:id="@+id/pregunta"
25 android:layout_width="wrap_content"
26 android:layout_height="wrap_content"
27 android:layout_gravity="center"
28 android:layout_weight="0.51"
29 android:text="@string/pregunta"
30 android:textSize="20sp"
31 android:textStyle="bold" />
C.3 Familia Inicio de sesión 109
32
33 <FrameLayout
34 android:layout_width="match_parent"
35 android:layout_height="200dp"
36 android:background="@drawable/fondolistrutinas"
37 android:focusable="false" >
38
39 <ListView
40 android:id="@android:id/list"
41 android:layout_width="match_parent"
42 android:layout_height="wrap_content"
43 android:drawSelectorOnTop="false"
44 android:focusable="false"
45 android:focusableInTouchMode="false" >
46 </ListView>
47
48 <TextView
49 android:id="@android:id/empty"
50 android:layout_width="match_parent"
51 android:layout_height="wrap_content"
52 android:focusable="false"
53 android:text="@string/error_list_r" />
54 </FrameLayout>
55
56 <LinearLayout
57 android:layout_width="match_parent"
58 android:layout_height="wrap_content"
59 android:orientation="horizontal" >
60
61 <Button
62 android:id="@+id/misestadisticas"
63 android:layout_width="match_parent"
64 android:layout_height="40dp"
65 android:layout_margin="3dp"
66 android:layout_weight="1"
67 android:background="@drawable/buttongen"
68 android:drawableLeft="@android:drawable/ic_menu_week"
69 android:drawablePadding="0dp"
70 android:drawableStart="@android:drawable/ic_menu_week"
71 android:singleLine="true"
72 android:onClick="lanzarRendimiento"
73 android:paddingLeft="4dp"
74 android:paddingRight="4dp"
75 android:text="@string/mis_estadisticas"
76 android:textSize="12sp" />
77
78 <Button
79 android:id="@+id/configuracion"
80 android:layout_width="match_parent"
81 android:layout_height="40dp"
82 android:layout_margin="3dp"
83 android:layout_weight="1"
110 Capítulo C. Códigos Java y xml de la aplicación Android
84 android:background="@drawable/buttongen"
85 android:drawableLeft="@android:drawable/ic_menu_manage"
86 android:drawablePadding="0dp"
87 android:drawableStart="@android:drawable/ic_menu_manage"
88 android:singleLine="true"
89 android:onClick="lanzarConfiguracion"
90 android:paddingLeft="4dp"
91 android:paddingRight="4dp"
92 android:text="@string/configuracion"
93 android:textSize="12sp" />
94 </LinearLayout>
95
96 <LinearLayout
97 android:layout_width="match_parent"
98 android:layout_height="wrap_content"
99 android:orientation="horizontal" >
100
101 <Button
102 android:id="@+id/acerca_de"
103 android:layout_width="match_parent"
104 android:layout_height="40dp"
105 android:layout_margin="3dp"
106 android:layout_weight="1"
107 android:background="@drawable/buttongen"
108 android:drawableLeft="@android:drawable/ic_menu_info_details"
109 android:drawablePadding="0dp"
110 android:drawableStart="@android:drawable/ic_menu_info_details"
111 android:onClick="lanzarAcercaDe"
112 android:singleLine="true"
113 android:paddingLeft="4dp"
114 android:paddingRight="4dp"
115 android:text="@string/acerca_de"
116 android:textSize="12sp" />
117
118 <Button
119 android:id="@+id/cerrar"
120 android:layout_width="match_parent"
121 android:layout_height="40dp"
122 android:layout_margin="3dp"
123 android:layout_weight="1"
124 android:background="@drawable/buttongen"
125 android:drawableLeft="@android:drawable/ic_lock_power_off"
126 android:drawablePadding="0dp"
127 android:drawableStart="@android:drawable/ic_lock_power_off"
128 android:onClick="cerrarSesion"
129 android:singleLine="true"
130 android:paddingLeft="4dp"
131 android:paddingRight="4dp"
132 android:text="@string/cerrar"
133 android:textSize="12sp" />
134 </LinearLayout>
135
C.4 Familia Olvido de contraseña 111
136 </LinearLayout>
entrada_rutina.xml
10 <Button
11 android:id="@+id/nombrerutina"
12 android:layout_width="match_parent"
13 android:layout_height="wrap_content"
14 android:layout_margin="1dp"
15 android:layout_marginLeft="4dp"
16 android:layout_marginRight="4dp"
17 android:background="@drawable/itemlistrutinas"
18 android:clickable="false"
19 android:focusable="false"
20 android:focusableInTouchMode="false"
21 android:textColor="#FFF"
22 android:textStyle="bold" />
23
24 </LinearLayout>
C.4.1 ContraseniaOlvidada.java
1
2 package com.deporte.proyectofc;
3
4 /* Clase que se encarga de gestionar el olvido de contrasenia cuando un
usuario lo ha solicitado.*/
5
6 import java.util.Properties;
7 import java.util.Random;
8
9 import javax.mail.Message;
10 import javax.mail.MessagingException;
11 import javax.mail.PasswordAuthentication;
12 import javax.mail.Session;
112 Capítulo C. Códigos Java y xml de la aplicación Android
13 import javax.mail.Transport;
14 import javax.mail.internet.InternetAddress;
15 import javax.mail.internet.MimeMessage;
16
17 import android.app.Activity;
18 import android.app.ProgressDialog;
19 import android.content.Context;
20 import android.os.AsyncTask;
21 import android.os.Bundle;
22 import android.view.View;
23 import android.view.View.OnClickListener;
24 import android.widget.Button;
25 import android.widget.EditText;
26 import android.widget.Toast;
27
33 @Override
34 protected void onCreate(Bundle savedInstanceState){
35 super.onCreate(savedInstanceState);
36 setContentView(R.layout.contrasenia_olvidada);
37 basedatos= new BasedatosSQLite(this);
38
61 new EnvioMail(ContraseniaOlvidada.this).execute(
v_e_mail, cadenaAl);
62
63 }
64 else {
65 Toast toast= Toast.makeText(getApplicationContext(),
66 "El e-mail no se corresponde con el usuario",
Toast.LENGTH_SHORT);
67 toast.show();
68 }
69 }
70 });
71 }
72
73 //Generador aleatorio de contraseñas
74 protected String getCadenaAleatoria(int longitud){
75 String cadena="";
76 long milis= new java.util.GregorianCalendar().getTimeInMillis();
77 Random random= new Random(milis);
78 int i=0;
79 while(i<longitud){
80 char c= (char)random.nextInt();
81 //Solo caracteres de 0 a 9, de A a Z y de a a z
82 if ( (c<=’9’ && c>=’0’) || (c>=’a’ && c<=’z’) || (c>=’A’ && c<=
’Z’)){
83 cadena+= c;
84 i++;
85 }
86 }
87 return cadena;
88 }
89
90 }
91
92
141 try {
142
156 }
157 }
C.4.2 contrasenia_olvidada.xml
7 <TextView
8 android:id="@+id/textView1"
9 android:layout_width="wrap_content"
10 android:layout_height="wrap_content"
11 android:layout_marginBottom="20sp"
12 android:layout_marginLeft="25sp"
13 android:layout_marginRight="25sp"
14 android:layout_marginTop="30sp"
15 android:text="@string/info_olvido"
16 android:textStyle="bold"
17 android:textAppearance="?android:attr/textAppearanceMedium" />
18
19 <TextView
20 android:id="@+id/usuario"
21 android:layout_width="wrap_content"
22 android:layout_height="wrap_content"
23 android:layout_marginLeft="25sp"
24 android:layout_marginStart="25sp"
25 android:text="@string/usuario"
26 android:textAppearance="?android:attr/textAppearanceSmall" />
27
28 <EditText
29 android:id="@+id/_usuario"
30 android:layout_width="match_parent"
31 android:layout_height="35dp"
32 android:layout_marginLeft="25sp"
33 android:layout_marginRight="25sp"
34 android:layout_marginBottom="20sp"
35 android:layout_marginStart="25sp"
36 android:paddingLeft="8dp"
37 android:paddingRight="8dp"
38 android:hint="@string/introduce_usuario"
39 android:background="@drawable/edittextgen"
40 android:ems="10"/>
41
42 <TextView
43 android:id="@+id/email"
44 android:layout_width="wrap_content"
45 android:layout_height="wrap_content"
116 Capítulo C. Códigos Java y xml de la aplicación Android
46 android:layout_marginLeft="25sp"
47 android:layout_marginStart="25sp"
48 android:text="@string/e_mail"
49 android:textAppearance="?android:attr/textAppearanceSmall" />
50
51 <EditText
52 android:id="@+id/_email"
53 android:layout_width="match_parent"
54 android:layout_height="35dp"
55 android:layout_marginLeft="25sp"
56 android:layout_marginRight="25sp"
57 android:layout_marginStart="25sp"
58 android:paddingLeft="8dp"
59 android:paddingRight="8dp"
60 android:background="@drawable/edittextgen"
61 android:ems="10"
62 android:inputType="textEmailAddress" />
63
64 <Button
65 android:id="@+id/enviar"
66 android:layout_width="wrap_content"
67 android:layout_height="wrap_content"
68 android:layout_marginLeft="25sp"
69 android:layout_marginRight="25sp"
70 android:layout_marginTop="30sp"
71 android:background="@drawable/buttongen"
72 android:text="@string/enviar" />
73
74 </LinearLayout>
C.5.1 Listadeejercicios.java
ListEjercicios.java
C.5 Familia ListaEjercicios de una rutina 117
1 package com.deporte.proyectofc;
2
3 /*
4 * Clase que muestra una lista con los videos contenidos en una rutina,
con sus items representados con una determinada estructura indicada
por la clase Adaptador.
5 */
6
7 import java.util.ArrayList;
8
9 import android.app.ListActivity;
10 import android.content.Intent;
11 import android.os.Bundle;
12 import android.view.View;
13 import android.widget.AdapterView;
14 import android.widget.AdapterView.OnItemClickListener;
15 import android.widget.ListAdapter;
16 import android.widget.ListView;
17 import android.widget.TextView;
18
AdaptadorEjercicio.java
1 package com.deporte.proyectofc;
2
C.5 Familia ListaEjercicios de una rutina 119
3 /*
4 * Clase encargada de definir el adaptador para mostrar la lista de
ejercicios de una rutina. Los elementos (item) del ListView serán
una pequeña imagen del video del ejercicio junto con un titulo, un
pequeño subtitulo y la fecha y hora si ya se ha realizado antes el
ejercicio.
5 */
6
7 import java.util.ArrayList;
8 import android.app.Activity;
9 import android.content.Context;
10 import android.graphics.Bitmap;
11 import android.media.ThumbnailUtils;
12 import android.provider.MediaStore.Images.Thumbnails;
13 import android.view.LayoutInflater;
14 import android.view.View;
15 import android.view.ViewGroup;
16 import android.widget.BaseAdapter;
17 import android.widget.ImageView;
18 import android.widget.TextView;
19
20 public class AdaptadorEjercicio extends BaseAdapter{
21
22 private final Activity actividad;
23 private final ArrayList<String> lista_ejercicios;
24 private final ArrayList<String> lista_titulos;
25 private final ArrayList<String> lista_subtitulos;
26 private final ArrayList<Integer> visualizados;
27 private final int id_usuario;
28
32 super();
33 this .actividad = actividad;
34 this .lista_ejercicios = lista_ejercicios;
35 this .lista_titulos= lista_titulos;
36 this .lista_subtitulos= lista_subtitulos;
37 this .id_usuario= id_usuario;
38 this .visualizados= visualizados;
39 notifyDataSetChanged();
40 }
41
42
89 return row;
90 }
91
92 public int getCount() {
93 return lista_ejercicios.size();
94 }
C.5 Familia ListaEjercicios de una rutina 121
95
96 public Object getItem(int arg0) {
97 return lista_ejercicios.get(arg0);
98 }
99
104 }
105
C.5.2 Listadeejercicios.xml
list_ejercicios.xml
14 android:layout_marginBottom="10sp"
15 android:contentDescription="@string/nom_rutina"
16 android:textAppearance="?android:attr/textAppearanceMedium"
17 android:textColor="#FFF"
18 android:textSize="10pt"
19 android:textStyle="bold" />
20
21 <ScrollView
22 android:id="@+id/scrolldescripcion"
23 android:layout_width="match_parent"
24 android:layout_height="120dp"
25 android:layout_margin="10sp"
26 android:background="@drawable/fondolistrutinas">
27
28 <TextView
29 android:id="@+id/descripcionrutina"
30 android:layout_width="match_parent"
31 android:layout_height="wrap_content"
32 android:padding="10sp"
33 android:contentDescription="@string/desc_rutina"
34 android:textStyle="bold"
35 android:textSize="16sp"/>
36
37 </ScrollView>
38
39 <FrameLayout
40 android:layout_width="match_parent"
41 android:layout_height="0dip"
42 android:layout_marginLeft="10dp"
43 android:layout_marginRight="10dp"
44 android:layout_weight="1"
45 android:focusable="false" >
46
47 <ListView
48 android:id="@android:id/list"
49 android:layout_width="match_parent"
50 android:layout_height="match_parent"
51 android:drawSelectorOnTop="false"
52 android:focusable="false"
53 android:focusableInTouchMode="false" >
54 </ListView>
55
56 <TextView
57 android:id="@android:id/empty"
58 android:layout_width="match_parent"
59 android:layout_height="match_parent"
60 android:focusable="false"
61 android:text="@string/error_list_e" />
62 </FrameLayout>
63
64 </LinearLayout>
C.5 Familia ListaEjercicios de una rutina 123
entrada_ejercicio.xml
11 <ImageView
12 android:id="@+id/ic_video"
13 android:layout_width="125dp"
14 android:layout_height="110dp"
15 android:layout_alignParentLeft="true"
16 android:layout_alignParentStart="true"
17 android:layout_margin="5dp"
18 android:clickable="false"
19 android:contentDescription="@string/imag"
20 android:focusable="false"
21 android:focusableInTouchMode="false" />
22
23 <TextView
24 android:id="@+id/titulo"
25 android:layout_width="match_parent"
26 android:layout_height="wrap_content"
27 android:layout_alignParentTop="true"
28 android:layout_margin="3dp"
29 android:layout_toEndOf="@id/ic_video"
30 android:layout_toRightOf="@id/ic_video"
31 android:focusable="false"
32 android:focusableInTouchMode="false"
33 android:singleLine="false"
34 android:textAppearance="?android:attr/textAppearanceLarge"
35 android:textSize="18sp" />
36
37 <LinearLayout
38 android:id="@+id/layout"
39 android:layout_width="match_parent"
40 android:layout_height="wrap_content"
41 android:layout_below="@id/titulo"
42 android:layout_toEndOf="@id/ic_video"
43 android:layout_toRightOf="@id/ic_video"
44 android:focusable="false"
45 android:orientation="horizontal" >
46
47 <TextView
48 android:id="@+id/visto"
49 android:layout_width="wrap_content"
124 Capítulo C. Códigos Java y xml de la aplicación Android
50 android:layout_height="wrap_content"
51 android:layout_weight="3"
52 android:focusable="false"
53 android:text="@string/hecho"
54 android:textColor="#F00"
55 android:textSize="12sp"
56 android:visibility="gone" />
57
58 <TextView
59 android:id="@+id/fecha"
60 android:layout_width="wrap_content"
61 android:layout_height="wrap_content"
62 android:layout_weight="1"
63 android:focusable="false"
64 android:textSize="12sp"
65 android:visibility="gone" />
66
67 <TextView
68 android:id="@+id/hora"
69 android:layout_width="wrap_content"
70 android:layout_height="wrap_content"
71 android:layout_weight="1"
72 android:focusable="false"
73 android:textSize="12sp"
74 android:visibility="gone" />
75 </LinearLayout>
76
77 <TextView
78 android:id="@+id/descripcion"
79 android:layout_width="match_parent"
80 android:layout_height="match_parent"
81 android:layout_below="@id/layout"
82 android:layout_margin="3dp"
83 android:layout_toEndOf="@id/ic_video"
84 android:layout_toRightOf="@id/ic_video"
85 android:focusable="false"
86 android:focusableInTouchMode="false" />
87
88 </RelativeLayout>
C.6.1 EliminacionRutina.java
1 package com.deporte.proyectofc;
2
3 /*
C.6 Familia Eliminacionrutinas 125
19 @Override
20 public void onCreate(Bundle savedInstanceState){
21 super.onCreate(savedInstanceState);
22 setContentView(R.layout.eliminacion_rutina);
23 Bundle extras= getIntent().getExtras();
24 id_rutina= extras.getInt("id_rutina");
25 cuenta= extras.getInt("cuentarutina");
26
27 }
28
59 //Elimina una carpeta, pero para ello debe eliminar todo su contenido
60 public boolean eliminaCarpeta(String path_rutina){
61 File file= new File(path_rutina);
62 //Si existe lo borramos
63 if (file.exists()){
64 borrarfic(file);
65 if (file.delete()){
66 return true;
67 } else return false ;
68 } else return false ;
69 }
70
71 //Borra el contenido de un directorio
72 public void borrarfic(File directorio){
73 File[] ficheros= directorio.listFiles();
74 for(int i=0 ; i<ficheros.length ; i++){
75 if (ficheros[i].isDirectory()){
76 borrarfic(ficheros[i]);
77 }
78 ficheros[i].delete();
79 }
80 }
81
82 //Cancelamos la operacion
83 public void lanzarCancelar(View view){
84 finish();
85 }
86 }
C.6.2 eliminacion_rutina.xml
7 <TextView
8 android:id="@+id/eliminarut"
C.7 Familia ReproductorEjercicio 127
9 android:layout_width="match_parent"
10 android:layout_height="wrap_content"
11 android:gravity="center"
12 android:text="@string/confir_eliminacion" />
13
14 <LinearLayout
15 android:layout_width="match_parent"
16 android:layout_height="wrap_content"
17 android:orientation="horizontal" >
18
19 <Button
20 android:id="@+id/aceptar"
21 android:layout_width="match_parent"
22 android:layout_height="40dp"
23 android:layout_weight="1"
24 android:onClick="lanzarAceptar"
25 android:text="@string/aceptar" />
26
27 <Button
28 android:id="@+id/cancelar"
29 android:layout_width="match_parent"
30 android:layout_height="40dp"
31 android:layout_weight="1"
32 android:onClick="lanzarCancelar"
33 android:text="@string/cancelar" />
34 </LinearLayout>
35
36 </LinearLayout>
C.7.1 ReproductorEjercicio.java
1 package com.deporte.proyectofc;
2
3 /* Clase que se encarga de reproducir el video correspondiente a un
ejercicios seleccionado. */
4
5 import java.text.DateFormat;
6 import java.text.SimpleDateFormat;
7 import java.util.Date;
8 import android.app.Activity;
9 import android.content.Intent;
10 import android.media.MediaPlayer;
11 import android.net.Uri;
12 import android.os.Bundle;
13 import android.os.SystemClock;
128 Capítulo C. Códigos Java y xml de la aplicación Android
14 import android.view.View;
15 import android.view.View.OnClickListener;
16 import android.widget.Button;
17 import android.widget.Chronometer;
18 import android.widget.ImageButton;
19 import android.widget.TextView;
20 import android.widget.Toast;
21 import android.widget.VideoView;
22
43
46 super.onCreate(savedInstanceState);
47 setContentView(R.layout.reproductor_ejercicio);
48 Bundle extras= getIntent().getExtras();
49 uri_ejercicio= Uri.parse(extras.getString("ruta"));
50
51 basedatos= new BasedatosSQLite(getApplicationContext());
52 id_ejercicio= basedatos.obtenerIdEjercicio(uri_ejercicio.toString
());
53 id_rutina= basedatos.obtenerIdRutEjercicio(id_ejercicio);
54 num_repeticiones= basedatos.obtenerNumRepeticiones(id_ejercicio);
55 mVideoView= (VideoView) findViewById(R.id.surface_view);
56 repeticiones= (TextView) findViewById(R.id.repeticiones);
57 mVideoView.setZOrderOnTop(true);
58 mVideoView.setVideoURI(uri_ejercicio);
59 comenzar= (Button) findViewById(R.id.comenzar);
60 cronometro= (Chronometer) findViewById(R.id.cronometro);
61
85 mVideoView.setOnCompletionListener(new MediaPlayer.
OnCompletionListener(){
86 public void onCompletion(MediaPlayer media){
87 //Si hemos llegado a la ultima repeticion pasamos a
considerarlo como visualizado, es decir, el
ejercicio ha sido realizado.
88 if (contador_repet == num_repeticiones){
89 mVideoView.stopPlayback();
90 }
91 else {
92 //Lo reproduce de nuevo.
93 mVideoView.start();
94 contador_repet++;
95 }
96 }
97 });
98 }
99 //Si se habia pulsado play
100 else if (estado_bplay){
101 contador_repet= savedInstanceState.getInt("contador");
102 int pos= savedInstanceState.getInt("posicion");
103 mVideoView.seekTo(pos);
130 Capítulo C. Códigos Java y xml de la aplicación Android
104 mVideoView.start();
105 }
106 else {
107 mVideoView.seekTo(500);
108 }
109 }
110
111 mVideoView.requestFocus();
112 repeticiones.setText(num_repeticiones + " repeticiones");
113 descripcion= basedatos.obtenerDescripEjercicio(id_ejercicio);
114 _descripcion= (TextView) findViewById(R.id._descripcion);
115 _descripcion.setText(descripcion);
116
151 mVideoView.setVideoURI(uri_ejercicio);
152 /*Hemos acabado el ejercicio, por tanto lo guardamos con
la fecha y la hora
153 *en que lo hemos realizado.*/
154 //Obtenemos id del usuario.
155 AlmacenId _id= new AlmacenId(getApplicationContext());
156 int id= _id.obtenerId();
157 //Obtenemos fecha y hora
158 DateFormat formatodate= new SimpleDateFormat("yyyy/MM/dd")
;
159 String date= formatodate.format(new Date());
160 DateFormat formatotime= new SimpleDateFormat("HH:mm:ss");
161 String time= formatotime.format(new Date());
162 long milis= SystemClock.elapsedRealtime()-cronometro.
getBase();
163 int tiempo= (int)(milis/1000);
164 basedatos.guardarNuevaVisualizacion(id, id_ejercicio, date,
time, tiempo);
165 }
166 });
167
168 //Usuario pulsa play/pause.
169 //Comenzamos la reproduccion del video
170 play_pause= (ImageButton) findViewById(R.id.play);
171 play_pause.setOnClickListener(new OnClickListener(){
172 public void onClick(View view){
173 estado_bplay= true;
174 //Drawable fondo;
175 if (mVideoView.isPlaying()){
176 System.out.println("reproduciendo play");
177 mVideoView.pause();
178 play_pause.setImageResource(R.drawable.pause);
179 }
180 else {
181 System.out.println("pausa");
182 mVideoView.start();
183 play_pause.setImageResource(R.drawable.play);
184 }
185 }
186 });
187
199
200 //Usuario pulsa "Siguiente".
201 //Reproducimos el siguiente ejercicio.
202 siguiente= (Button) findViewById(R.id.siguiente);
203 siguiente.setOnClickListener(new OnClickListener(){
204 public void onClick(View view){
205 AlmacenId id_usuario= new AlmacenId(getApplicationContext
());
206 int id_us= id_usuario.obtenerId();
207 String estadoformausuario= basedatos.obtenerEstadoDeForma(
id_us);
208 do{
209 id_ejercicio++;
210 }while(basedatos.existeEjercicio(id_ejercicio) && !(
basedatos.obtenerEstadoEjercicio(id_ejercicio).equals(
estadoformausuario)));
211
212 if (basedatos.existeEjercicio(id_ejercicio)){
213 uri_ejercicio= Uri.parse(basedatos.
obtenerPathEjercicio(id_ejercicio));
214 String _uri= uri_ejercicio.toString();
215 //Comprobamos que el video pertenezca a la rutina
actual
216 if (id_rutina==basedatos.obtenerIdRutEjercicio(
id_ejercicio)){
217 finish();
218 Intent i= new Intent(getApplicationContext(),
ReproductorEjercicio.class);
219 i.putExtra("uri", _uri);
220 startActivity(i);
221 }
222 else {
223 Toast toast= Toast.makeText(getApplicationContext()
,
224 "Ya no hay más videos en esta rutina.",
Toast.LENGTH_SHORT);
225 toast.show();
226 }
227 }
228 else {
229 Toast toast= Toast.makeText(getApplicationContext(),
230 "Ya no hay más videos en esta rutina.", Toast.
LENGTH_SHORT);
231 toast.show();
232 }
233 }
234 });
235
236 }//onCreate
237
239 @Override
240 protected void onSaveInstanceState(Bundle outState){
241 super.onSaveInstanceState(outState);
242 //Almacenamos el tiempo del cronometro.
243 long elapsedMillis = (SystemClock.elapsedRealtime() - cronometro.
getBase());
244 //Almacenamos el estado del boton "Comenzar".
245 boolean pul_comenzar= estado_bcomenzar;
246 boolean pul_play= estado_bplay;
247 //Almacenamos la posicion de reproduccion del ejercicio.
248 int posicion= mVideoView.getCurrentPosition();
249 //Almacenamos el valor del contador de reproducciones.
250 int cont= contador_repet;
251 outState.putLong("crono", elapsedMillis);
252 outState.putInt("posicion", posicion);
253 outState.putInt("contador", cont);
254 outState.putBoolean("pulsado_comenzar", pul_comenzar);
255 outState.putBoolean("pulsado_play", pul_play);
256 }
257
258 }
C.7.2 reproductor_ejercicio.xml
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/
android"
2 android:id="@+id/RelativeLayout1"
3 android:layout_width="fill_parent"
4 android:layout_height="fill_parent" >
5
6 <VideoView
7 android:id="@+id/surface_view"
8 android:layout_width="match_parent"
9 android:layout_height="match_parent"
10 android:layout_alignParentTop="true" />
11
12 <LinearLayout
13 android:id="@+id/botones1"
14 android:layout_width="match_parent"
15 android:layout_height="50dp"
16 android:layout_below="@id/surface_view"
17 android:background="#FFF"
18 android:orientation="horizontal" >
19
20 <ImageButton
21 android:id="@+id/play"
22 android:layout_width="wrap_content"
23 android:layout_height="match_parent"
24 android:layout_weight="1"
25 android:src="@drawable/play" />
134 Capítulo C. Códigos Java y xml de la aplicación Android
26
27 <ImageButton
28 android:id="@+id/stop"
29 android:layout_width="wrap_content"
30 android:layout_height="match_parent"
31 android:layout_weight="1"
32 android:src="@drawable/stop" />
33
34 <Button
35 android:id="@+id/siguiente"
36 android:layout_width="wrap_content"
37 android:layout_height="match_parent"
38 android:layout_weight="2"
39 android:text="@string/sig"
40 android:textColor="#00F"
41 android:textStyle="bold" />
42 </LinearLayout>
43
44 <LinearLayout
45 android:id="@+id/botones2"
46 android:layout_width="match_parent"
47 android:layout_height="wrap_content"
48 android:layout_below="@id/botones1"
49 android:background="#FFF"
50 android:orientation="horizontal" >
51
52 <Button
53 android:id="@+id/comenzar"
54 android:layout_width="wrap_content"
55 android:layout_height="wrap_content"
56 android:layout_weight="1"
57 android:background="#0F0"
58 android:text="@string/comenzar"
59 android:textStyle="bold" />
60
61 <Button
62 android:id="@+id/parar"
63 android:layout_width="wrap_content"
64 android:layout_height="wrap_content"
65 android:layout_weight="1"
66 android:background="#F00"
67 android:text="@string/terminado"
68 android:textStyle="bold" />
69 </LinearLayout>
70
71 <LinearLayout
72 android:id="@+id/crono"
73 android:layout_width="match_parent"
74 android:layout_height="wrap_content"
75 android:layout_below="@id/botones2"
76 android:layout_margin="4dp"
77 android:background="@drawable/fondolistrutinas"
C.7 Familia ReproductorEjercicio 135
78 android:orientation="horizontal" >
79
80 <Chronometer
81 android:id="@+id/cronometro"
82 android:layout_width="wrap_content"
83 android:layout_height="wrap_content"
84 android:layout_margin="10sp"
85 android:layout_weight="3"
86 android:format="%s"
87 android:textSize="25sp" />
88
89 <TextView
90 android:id="@+id/repeticiones"
91 android:layout_width="wrap_content"
92 android:layout_height="wrap_content"
93 android:layout_margin="10sp"
94 android:layout_weight="1"
95 android:text="@string/repet"
96 android:textColor="#F00"
97 android:textSize="20sp"
98 android:textStyle="bold" />
99 </LinearLayout>
100
101 <LinearLayout
102 android:layout_width="match_parent"
103 android:layout_height="wrap_content"
104 android:layout_alignParentLeft="true"
105 android:layout_below="@id/crono"
106 android:layout_marginBottom="4dp"
107 android:layout_marginLeft="4dp"
108 android:layout_marginRight="4dp"
109 android:background="@drawable/fondolistrutinas"
110 android:orientation="vertical" >
111
112 <TextView
113 android:id="@+id/descripcion"
114 android:layout_width="match_parent"
115 android:layout_height="wrap_content"
116 android:layout_marginBottom="10sp"
117 android:layout_marginLeft="10sp"
118 android:layout_marginRight="10sp"
119 android:text="@string/descripcion"
120 android:textStyle="bold"
121 android:textSize="18sp" />
122
123 <ScrollView
124 android:layout_width="wrap_content"
125 android:layout_height="wrap_content" >
126
127 <TextView
128 android:id="@+id/_descripcion"
129 android:layout_width="wrap_content"
136 Capítulo C. Códigos Java y xml de la aplicación Android
130 android:layout_height="wrap_content"
131 android:layout_marginBottom="10sp"
132 android:layout_marginLeft="10sp"
133 android:layout_marginRight="10sp" />
134 </ScrollView>
135 </LinearLayout>
136
137 </RelativeLayout>
C.8.1 AcercaDe.java
1 package com.deporte.proyectofc;
2
3 /*
4 * Clase que muestra la actividad AcercaDe, que muestra informacion de
interes para el usuario sobre la aplicacion.
5 */
6
7 import android.app.Activity;
8 import android.os.Bundle;
9
12 @Override
13 protected void onCreate(Bundle savedInstanceState){
14 super.onCreate(savedInstanceState);
15 setContentView(R.layout.acerca_de);
16 }
17 }
C.8.2 acerca_de
12 android:text="@string/acerca_de"
13 android:textAppearance="?android:attr/textAppearanceMedium" />
14
15 <TextView
16 android:id="@+id/textView2"
17 android:layout_width="wrap_content"
18 android:layout_height="wrap_content"
19 android:layout_marginBottom="20sp"
20 android:layout_marginLeft="20sp"
21 android:layout_marginRight="20sp"
22 android:text="@string/acercade_mensaje" />
23
24 </LinearLayout>
C.9.1 GraficasRendimiento.java
GraficasRendimiento.java
1 package com.deporte.proyectofc;
2
3 /*
4 * Clase encargada de representar la gráfica sobre el tiempo dedicado a
hacer ejercicio durante la semana, medido en minutos.
5 */
6
7 import java.text.DateFormat;
8 import java.text.FieldPosition;
9 import java.text.Format;
10 import java.text.ParsePosition;
11 import java.text.SimpleDateFormat;
12 import java.util.Arrays;
13 import java.util.Calendar;
14 import java.util.Date;
15 import java.util.GregorianCalendar;
16
17 import com.androidplot.xy.LineAndPointFormatter;
138 Capítulo C. Códigos Java y xml de la aplicación Android
18 import com.androidplot.xy.SimpleXYSeries;
19 import com.androidplot.xy.XYPlot;
20 import com.androidplot.xy.XYSeries;
21 import com.androidplot.xy.XYStepMode;
22
23 import android.app.ListActivity;
24 import android.graphics.Color;
25 import android.os.Bundle;
26 import android.widget.ListAdapter;
27 import android.widget.ListView;
28
35 @Override
36 protected void onCreate(Bundle savedInstanceState) {
37 //Mostramos el layout
38 super.onCreate(savedInstanceState);
39 setContentView(R.layout.graficas_rendimiento);
40 migrafica= (XYPlot) findViewById(R.id.migrafica);
41
57
58 XYSeries serie;
59 BasedatosSQLite basedatos= new BasedatosSQLite(this);
60 //id del usuario
61 AlmacenId id= new AlmacenId(this);
62 int id_usuario= id.obtenerId();
63 //fecha de hoy
64 dia= new Date();
65 GregorianCalendar cal= new GregorianCalendar();
C.9 Familia GraficasRemdimiento 139
66 cal.setTime(dia);
67
112 migrafica.calculateMinMaxVals();
113
140 Capítulo C. Códigos Java y xml de la aplicación Android
122
123 //Pasa el tiempo de segundos a minutos con decimales.
124 public double tiempo_transcurrido(int segundos){
125 int seg=0, añadir=0;
126 double min= 0;
127 if (segundos>=60){
128 min= segundos/60;
129 seg= segundos%60;
130 añadir= seg/60;
131 min= (double)(min+añadir);
132 }
133 else {
134 min= (double)segundos/60;
135 }
136 System.out.println("min " + min);
137 return min;
138 }
139
140
182 @Override
183 public StringBuffer format(Object object, StringBuffer buffer,
FieldPosition field) {
184 int parsedInt = Math.round(Float.parseFloat(object.toString())
);
185 String labelString = LABELS[parsedInt-1];
186
187 buffer.append(labelString);
188 return buffer;
189 }
190
191 @Override
192 public Object parseObject(String string, ParsePosition position) {
193 return java.util.Arrays.asList(LABELS).indexOf(string);
194 }
195 }
196 }
AdaptadorListRendimiento.java
1 package com.deporte.proyectofc;
2
5 import android.app.Activity;
6 import android.view.LayoutInflater;
7 import android.view.View;
8 import android.view.ViewGroup;
9 import android.widget.BaseAdapter;
10 import android.widget.TextView;
142 Capítulo C. Códigos Java y xml de la aplicación Android
11
12 public class AdaptadorListRendimiento extends BaseAdapter{
13
14 private Activity actividad;
15 private String[] dias_semana;
16 private Number[] minutos;
17 private Number[] segundos;
18 private TextView dia_sem;
19 private TextView min;
20
21 public AdaptadorListRendimiento(Activity actividad, String[]
dias_semana, Number[] minutos, Number[] segundos){
22 super();
23 this .actividad= actividad;
24 this .dias_semana= dias_semana;
25 this .minutos= minutos;
26 this .segundos= segundos;
27 }
28
29 //Genera la vista de cada item
30 public View getView(int position, View convertView, ViewGroup parent){
31
32 if (convertView==null){
33 LayoutInflater inflater = actividad.getLayoutInflater();
34 convertView= inflater.inflate(R.layout.entrada_grafica_rend,
null, true);
35 }
36
45 return convertView;
46 }
47
48 public int getCount() {
49 return dias_semana.length;
50 }
51
C.9.2 listarendimiento.xml
De nuevo, como la vista incorpora una lista, precisaremos de dos XML, graficas_-
rendimiento.xml y entrada_grafica_rend.xml.
graficas_rendimiento.xml
7 <TextView
8 android:id="@+id/textView1"
9 android:layout_width="wrap_content"
10 android:layout_height="wrap_content"
11 android:layout_marginTop="5dp"
12 android:layout_marginLeft="5dp"
13 android:layout_marginRight="5dp"
14 android:layout_gravity="left"
15 android:textSize="18sp"
16 android:text="@string/mis_estadisticas" />
17
18 <com.androidplot.xy.XYPlot
19 android:id="@+id/migrafica"
20 android:layout_width="match_parent"
21 android:layout_height="260dp"
22 android:layout_marginLeft="5dp"
23 android:layout_marginRight="5dp"
24 android:layout_marginTop="5dp"
25 title="Rendimiento semanal"
26 android:background="@android:color/transparent">
27 </com.androidplot.xy.XYPlot>
28
29 <TextView
30 android:layout_width="match_parent"
31 android:layout_height="wrap_content"
32 android:textSize="20sp"
33 android:textStyle="bold"
34 android:text="@string/t_entreno"
35 android:gravity="center"/>
36 <FrameLayout
37 android:layout_width="match_parent"
38 android:layout_height="wrap_content"
39 android:focusable="false"
40 android:background="@drawable/fondolistrutinas"
41 android:layout_margin="6dp"
42 android:padding="6dp">
43 <ListView
144 Capítulo C. Códigos Java y xml de la aplicación Android
44 android:id="@android:id/list"
45 android:layout_width="match_parent"
46 android:layout_height="wrap_content"
47 android:drawSelectorOnTop="false">
48
49 </ListView>
50 <TextView
51 android:id="@android:id/empty"
52 android:layout_width="match_parent"
53 android:layout_height="50dp"
54 android:textSize="16sp"
55 android:textStyle="bold"
56 android:text="@string/error_list_g" />
57
58 </FrameLayout>
59 </LinearLayout>
entrada_grafica_rend.xml
15 <LinearLayout
16 android:layout_width="match_parent"
17 android:layout_height="wrap_content"
18 android:orientation="horizontal" >
19
20 <TextView
21 android:id="@+id/minutos"
22 android:layout_width="match_parent"
23 android:layout_height="wrap_content"
24 android:layout_margin="3dp"
25 android:layout_marginTop="6dp"
26 android:text="@string/tiempo"
27 android:textSize="20sp" />
28 </LinearLayout>
29
30 </LinearLayout>
C.10 Familia ConfiguracionCuenta 145
Pantalla que muestra las opciones que hay sobre configuración de la cuenta.
C.10.1 ConfiguracionCuenta.java
1 package com.deporte.proyectofc;
2
3 /*
4 * Clase encargada de visualizar la actividad de configuracion. Se podra
modificar varios aspectos relacionados con los datos de usuario, que
se habian introducido en su registro.
5 */
6
7 import android.app.Activity;
8 import android.content.Intent;
9 import android.os.Bundle;
10 import android.view.View;
11
14 @Override
15 protected void onCreate(Bundle savedInstanceState) {
16 super.onCreate(savedInstanceState);
17 setContentView(R.layout.configuracion_cuenta);
18 }
19
38 basedatos.eliminarUsuario(valor_id.obtenerId());
39 //Una vez eliminado el usuario, volvemos a la actividad principal,
46 }
C.10.2 configuracion_cuenta.xml
17 <TextView
18 android:id="@+id/textView2"
19 android:layout_width="wrap_content"
20 android:layout_height="wrap_content"
21 android:layout_margin="12sp"
22 android:text="@string/modifica_info"
23 android:textAppearance="?android:attr/textAppearanceSmall" />
24
25 <Button
26 android:id="@+id/button1"
27 android:layout_width="match_parent"
28 android:layout_height="wrap_content"
29 android:layout_gravity="center"
30 android:layout_marginLeft="10dp"
31 android:layout_marginRight="10dp"
32 android:background="@drawable/buttongen"
33 android:onClick="lanzarModificacion"
34 android:text="@string/bmodificar_info" />
35
36 <TextView
37 android:id="@+id/textView3"
C.11 Familia ModificacionCuenta 147
38 android:layout_width="wrap_content"
39 android:layout_height="wrap_content"
40 android:layout_margin="12sp"
41 android:text="@string/cambia_pass"
42 android:textAppearance="?android:attr/textAppearanceSmall" />
43
44 <Button
45 android:id="@+id/button3"
46 android:layout_width="match_parent"
47 android:layout_height="wrap_content"
48 android:layout_marginLeft="10dp"
49 android:layout_marginRight="10dp"
50 android:background="@drawable/buttongen"
51 android:onClick="lanzarModificaContrasenia"
52 android:text="@string/bcambiar_pass" />
53
54 <TextView
55 android:id="@+id/textView4"
56 android:layout_width="wrap_content"
57 android:layout_height="wrap_content"
58 android:layout_margin="12sp"
59 android:text="@string/elimina_cuenta"
60 android:textAppearance="?android:attr/textAppearanceSmall" />
61
62 <Button
63 android:id="@+id/button2"
64 android:layout_width="match_parent"
65 android:layout_height="wrap_content"
66 android:layout_gravity="center"
67 android:layout_marginLeft="10dp"
68 android:layout_marginRight="10dp"
69 android:background="@drawable/buttongen"
70 android:onClick="eliminarCuenta"
71 android:text="@string/beliminar_cuenta" />
72
73 </LinearLayout>
C.11.1 ModificacionCuenta.java
1 package com.deporte.proyectofc;
2
5 import java.util.ArrayList;
6 import android.app.Activity;
148 Capítulo C. Códigos Java y xml de la aplicación Android
7 import android.os.Bundle;
8 import android.view.View;
9 import android.widget.ArrayAdapter;
10 import android.widget.EditText;
11 import android.widget.Spinner;
12 import android.widget.Toast;
13
79 }
C.11.2 modificacion_usuario.xml
21 android:background="@drawable/edittextgen"
22 android:ems="10"
23 android:paddingLeft="8dp"
24 android:paddingRight="8dp" >
25
26 <requestFocus />
27 </EditText>
28
29 <TextView
30 android:id="@+id/apellidos"
31 android:layout_width="wrap_content"
32 android:layout_height="wrap_content"
33 android:layout_margin="8dp"
34 android:text="@string/apellidos" />
35
36 <EditText
37 android:id="@+id/_apellidos"
38 android:layout_width="match_parent"
39 android:layout_height="40dp"
40 android:layout_marginBottom="6dp"
41 android:layout_marginLeft="8dp"
42 android:layout_marginRight="8dp"
43 android:background="@drawable/edittextgen"
44 android:ems="10"
45 android:paddingLeft="8dp"
46 android:paddingRight="8dp" />
47
48 <TextView
49 android:id="@+id/e_mail"
50 android:layout_width="wrap_content"
51 android:layout_height="wrap_content"
52 android:layout_margin="8dp"
53 android:text="@string/e_mail" />
54
55 <EditText
56 android:id="@+id/_e_mail"
57 android:layout_width="match_parent"
58 android:layout_height="40dp"
59 android:layout_marginBottom="6dp"
60 android:layout_marginLeft="8dp"
61 android:layout_marginRight="8dp"
62 android:background="@drawable/edittextgen"
63 android:ems="10"
64 android:inputType="textEmailAddress"
65 android:paddingLeft="8dp"
66 android:paddingRight="8dp" />
67
68 <LinearLayout
69 android:layout_width="match_parent"
70 android:layout_height="wrap_content"
71 android:layout_margin="6dp"
72 android:orientation="horizontal" >
C.12 Familia ModificaciónContraseña 151
73
74 <TextView
75 android:id="@+id/estado"
76 android:layout_width="wrap_content"
77 android:layout_height="wrap_content"
78 android:layout_margin="6dp"
79 android:text="@string/nivel"
80 android:textSize="16sp" />
81
82 <Spinner
83 android:id="@+id/_estado"
84 android:layout_width="0dp"
85 android:layout_height="wrap_content"
86 android:layout_marginLeft="6dp"
87 android:layout_marginRight="6dp"
88 android:layout_marginTop="12dp"
89 android:layout_weight="1"
90 android:background="@android:drawable/btn_dropdown" />
91 </LinearLayout>
92
93 <Button
94 android:id="@+id/button1"
95 android:layout_width="match_parent"
96 android:layout_height="wrap_content"
97 android:layout_margin="10dp"
98 android:onClick="guardarModificacion"
99 android:text="@string/modificar" />
100
101 </LinearLayout>
C.12.1 ModificacionContrasenia.java
1 package com.deporte.proyectofc;
2
5 import java.util.ArrayList;
6 import android.app.Activity;
7 import android.os.Bundle;
8 import android.view.View;
9 import android.widget.EditText;
10 import android.widget.Toast;
11
55 }
C.12.2 modificacion_contrasenia.xml
23 <TextView
24 android:id="@+id/NContrasenia2"
25 android:layout_width="wrap_content"
26 android:layout_height="wrap_content"
27 android:text="@string/repita_c" />
28
29 <EditText
30 android:id="@+id/_NContrasenia2"
31 android:layout_width="match_parent"
32 android:layout_height="wrap_content"
33 android:ems="10"
34 android:inputType="textPassword" />
35
36 <Button
37 android:id="@+id/guardar"
38 android:layout_width="match_parent"
39 android:layout_height="wrap_content"
40 android:onClick="guardarNuevaContrasenia"
41 android:text="@string/modificar_contrasenia" />
42
43 </LinearLayout>
154 Capítulo C. Códigos Java y xml de la aplicación Android
En esta caso, aunque se trate de una lista, solo tendremos un código java y otro
XML, porque utilizaremos un Adaptador ya definido por el sistema, creando un
objeto ArrayAdapter<String>.
C.13.1 ListMasRutinas.java
1 package com.deporte.proyectofc;
2
3 /*
4 * Clase que se encarga de mostrar la lista de rutinas que el usuario aun
no ha cargado pero que otro/s sí lo han hecho. Tambien se podra
buscar nuevas rutinas que aun no han sido cargadas por nadie.
5 */
6
7 import java.io.File;
8 import java.util.ArrayList;
9 import android.app.ListActivity;
10 import android.content.Intent;
11 import android.database.sqlite.SQLiteDatabase;
12 import android.os.Bundle;
13 import android.os.Environment;
14 import android.view.View;
15 import android.widget.AdapterView;
16 import android.widget.ArrayAdapter;
17 import android.widget.ListView;
18
25
26 @Override
27 public void onCreate(Bundle savedInstanceState){
28
29 super.onCreate(savedInstanceState);
30 setContentView(R.layout.list_mas_rutinas);
31
47 }
48
49
50 //Metodo que incluye en la lista del usuario la nueva rutina
seleccionada
51 public void lanzarAceptar(View view){
52
83 if (ficheros[cont].getName().endsWith(".zip")){
84 int pos= ficheros[cont].getName().indexOf(".zip");
85 String cargada= ficheros[cont].getName().substring(0, pos)
;
86 //Comprobamos que esta rutina no esta cargada ya
87 if (!basedatos.existeRutina(cargada)){
88 path_rutinas_zip.add(ficheros[cont].getAbsolutePath());
89 nom_rutinas_zip.add(ficheros[cont].getName());
90 }
91 }
92 }
93 Intent i= new Intent(this, ListNuevasRutinas.class);
94 //Comunicamos a la siguiente actividad los nombre y rutas de los .
zip
95 i.putStringArrayListExtra("pathrutinas", path_rutinas_zip);
96 i.putStringArrayListExtra("nombresrutinas", nom_rutinas_zip);
97 startActivity(i);
98
99 }
100
101 //Metodo para recargar la actividad
102 @Override public void onRestart(){
103 super.onRestart();
104 finish();
105 startActivity(getIntent());
106 }
107
108 }
C.13.2 list_mas_rutinas.xml
19 android:layout_marginLeft="20sp"
20 android:layout_marginRight="20sp"
21 android:layout_marginBottom="20sp"
22 android:text="@string/otras_rutinas"
23 android:textSize="18sp"/>
24
25 <FrameLayout
26 android:layout_width="match_parent"
27 android:layout_height="wrap_content"
28 android:layout_margin="6dp"
29 android:background="@drawable/fondolistrutinas"
30 android:focusable="false"
31 android:padding="6dp" >
32
33 <ListView
34 android:id="@android:id/list"
35 android:layout_width="match_parent"
36 android:layout_height="wrap_content"
37 android:drawSelectorOnTop="false" >
38 </ListView>
39
40 <TextView
41 android:id="@android:id/empty"
42 android:layout_width="match_parent"
43 android:layout_height="50dp"
44 android:text="@string/error_list_o"
45 android:textSize="16sp"
46 android:textStyle="bold" />
47 </FrameLayout>
48
49 <Button
50 android:id="@+id/aceptar"
51 android:layout_width="match_parent"
52 android:layout_height="wrap_content"
53 android:layout_margin="6dp"
54 android:background="@drawable/buttongen"
55 android:onClick="lanzarAceptar"
56 android:text="@string/aceptar" />
57
58 <Button
59 android:id="@+id/cargarmas"
60 android:layout_width="match_parent"
61 android:layout_height="wrap_content"
62 android:layout_margin="6dp"
63 android:background="@drawable/buttongen"
64 android:onClick="lanzarCargarNuevas"
65 android:text="@string/bcargar_nuevas" />
66
67 </LinearLayout>
158 Capítulo C. Códigos Java y xml de la aplicación Android
C.14.1 ListNuevasRutinas.java
1 package com.deporte.proyectofc;
2
3 /* Clase que muestra una lista de nuevas rutinas comprimidas en la
carpeta Download. */
4
5 import java.io.File;
6 import java.io.FileInputStream;
7 import java.io.FileNotFoundException;
8 import java.io.FileOutputStream;
9 import java.io.IOException;
10 import java.util.ArrayList;
11 import java.util.zip.ZipEntry;
12 import java.util.zip.ZipInputStream;
13
14 import android.app.Activity;
15 import android.app.ListActivity;
16 import android.app.ProgressDialog;
17 import android.content.Context;
18 import android.database.sqlite.SQLiteDatabase;
19 import android.os.AsyncTask;
20 import android.os.Bundle;
21 import android.os.Environment;
22 import android.view.View;
23 import android.widget.AdapterView;
24 import android.widget.AdapterView.OnItemClickListener;
25 import android.widget.ArrayAdapter;
26 import android.widget.ListView;
27 import android.widget.Toast;
28
36 //Metodo que muestra una lista con los .zip que podemos incluir en
nuestra app
37 @Override
38 public void onCreate(Bundle savedInstanceState){
39
40 super.onCreate(savedInstanceState);
C.14 Familia ListaNuevasRutinas de Download 159
41 setContentView(R.layout.list_nuevas_rutinas);
42 Bundle extras= getIntent().getExtras();
43 path_rutinas_zip= extras.getStringArrayList("pathrutinas");
44 nom_rutinas_zip= extras.getStringArrayList("nombresrutinas");
45
51 lista.setOnItemClickListener(new OnItemClickListener() {
52 @Override
53 public void onItemClick(AdapterView<?> a, View view, int
position, long id){
54 //ruta del .zip seleccionado
55 path= path_rutinas_zip.get(position);
56 }
57 });
58 }
59
60
68
75
76 /*Clase que se encarga de descomprimir la carpeta zip cuya ruta se pasa
como parametro. */
77 class CargaZip extends AsyncTask<String, Integer, String>{
78 private Context contexto;
79 private ProgressDialog progreso;
80 private BasedatosSQLite basedatos;
81 private String path_rutina_desc;
82
83
84 public CargaZip(Context contexto){
85 this .contexto= contexto;
86 }
87
88
160 Capítulo C. Códigos Java y xml de la aplicación Android
95 }
96
97
98 //Metodo que contiene las acciones a realizar por el hilo secundario.
99 @Override protected String doInBackground(String... n){
100
170 }
171
172
173 //Extrae un fichero del .zip a el destino indicado por el String path.
179 }
180 path_rutina_desc= newfile.getParentFile().getAbsolutePath();
181 FileOutputStream fos = new FileOutputStream(newfile);
182 byte[] bytesIn = new byte[1024];
183 int len;
184 while ((len = inputzip.read(bytesIn)) > 0) {
185 fos.write(bytesIn, 0, len);
186 }
187 fos.close();
188
189 }
190
191 public boolean eliminaCarpeta(String path_rutina){
192 File file= new File(path_rutina);
193 //Si existe lo borramos
194 if (file.exists()){
195 borrarfic(file);
196 if (file.delete()){
197 return true;
198 } else return false ;
199 } else return false ;
200 }
201
202 public void borrarfic(File directorio){
203 File[] ficheros= directorio.listFiles();
204 for(int i=0 ; i<ficheros.length ; i++){
205 if (ficheros[i].isDirectory()){
206 borrarfic(ficheros[i]);
207 }
208 ficheros[i].delete();
209 }
210 }
211
212 }
C.14.2 list_nuevas_rutinas.xml
14 android:textStyle="bold"/>
15
16 <TextView
17 android:layout_width="match_parent"
18 android:layout_height="wrap_content"
19 android:layout_marginLeft="20dp"
20 android:layout_marginRight="20dp"
21 android:layout_marginBottom="20dp"
22 android:textSize="18sp"
23 android:text="@string/nuevas_rutinas"/>
24
25 <FrameLayout
26 android:layout_width="match_parent"
27 android:layout_height="wrap_content"
28 android:focusable="false" >
29
30 <ListView
31 android:id="@android:id/list"
32 android:layout_width="match_parent"
33 android:layout_height="wrap_content" >
34 </ListView>
35
36 <TextView
37 android:id="@android:id/empty"
38 android:layout_width="match_parent"
39 android:layout_height="wrap_content"
40 android:text="@string/error_list_n" />
41 </FrameLayout>
42
43 <LinearLayout
44 android:layout_width="match_parent"
45 android:layout_height="wrap_content"
46 android:orientation="horizontal" >
47
48 <Button
49 android:id="@+id/cargar"
50 android:layout_width="match_parent"
51 android:layout_height="wrap_content"
52 android:layout_margin="2dp"
53 android:layout_weight="1"
54 android:background="@drawable/buttongen"
55 android:onClick="cargarSeleccion"
56 android:text="@string/bcargar" />
57
58 <Button
59 android:id="@+id/cancelar"
60 android:layout_width="match_parent"
61 android:layout_height="wrap_content"
62 android:layout_margin="2dp"
63 android:layout_weight="1"
64 android:background="@drawable/buttongen"
65 android:onClick="cancelarSeleccion"
164 Capítulo C. Códigos Java y xml de la aplicación Android
66 android:text="@string/cancelar" />
67 </LinearLayout>
68
69 </LinearLayout>
C.15.1 AlmacenId.java
1 package com.deporte.proyectofc;
2
3 /*
4 * Clase que guarda de manera permanente el valor id del usuario que ha
iniciado sesion en un archivo de preferencias. Este valor es muy
usado durante toda la sesion de la aplicacion. Cuando se cierre
sesion, se eliminara el archivo que lo contiene.
5 */
6
7 import android.content.Context;
8 import android.content.SharedPreferences;
9
32 return id;
33 }
34
35 }
C.16.1 BasedatosSQLite.java
1 package com.deporte.proyectofc;
2
34
35 /* Metodo llamado automaticamente cuando se crea el primer objeto de
esta clase. Se encarga de crear todas las tablas necesarias para
el manejo de nuestra app, y de copiar la rutina que viene
incluida en el paquete de la aplicacion (en la carpeta raw) a la
carpeta app_deportes de nuestro dispositivo, para asi tener todas
las rutinas en un solo sitio. */
36 @Override public void onCreate(SQLiteDatabase bd){
37
103 }
104 }catch(FileNotFoundException e){
105 System.err.println(e);
106 }
107
108 }
109 try{
110 inputstream.close();
111 dest.close();
112 }catch(IOException e){
113 System.err.println(e);
114 }
115 }
168 Capítulo C. Códigos Java y xml de la aplicación Android
116 }
117 else {
118 System.err.println("No se ha podido crear el directorio /
app_deportes/rutinas/gap/");
119 }
120
121 String path_rutina= Environment.getExternalStorageDirectory().
getAbsolutePath().toString() + "/app_deportes/rutinas/gap/";
122 insertarRutina(bd, null, "GAP", path_rutina);
123 int id_rutina= obtenerIdRutina(bd, "GAP");
124 //Rellenamos la tabla ejercicios, con los ejercicios que venian
en la app en raw folder, los de la rutina GAP.
125 String descripcion_rutina= introduceEjercicios(bd, id_rutina);
126 insertarDescRut(bd, id_rutina, descripcion_rutina);
127
130
131
132 @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int
newVersion) {
133 // En caso de una nueva version habra que actualizar las tablas.
134 //Se elimina la version anterior de la tabla
135 db.execSQL("DROP TABLE IF EXISTS usuarios");
136 db.execSQL("DROP TABLE IF EXISTS ejercicios");
137 db.execSQL("DROP TABLE IF EXISTS rutinas");
138 db.execSQL("DROP TABLE IF EXISTS ejercicios_visualizados");
139 db.execSQL("DROP TABLE IF EXISTS usuario_rutina");
140
141 //Se crea la nueva version de la tabla
142 onCreate(db);
143 }
144
224 //Obtenemos las rutas de todos los ejercicios que pertenecen a una
rutina.
225 public ArrayList<String> listaEjerciciosRutina(int id_rutina){
226
252 }
253
344 }
345
471 bd.beginTransaction();
472 ContentValues valores = new ContentValues();
473 valores.put("descripcion_rutina", descripcion_rutina);
474 bd.update("rutinas", valores, "id_rutina=" + id_rutina, null);
475 bd.setTransactionSuccessful();
476 bd.endTransaction();
477 }
478
488
489 return path;
490 }
491
629 bd.setTransactionSuccessful();
630 bd.endTransaction();
631 bd.close();
632 }
633
680 datos.add(cursor.getString(indicecol));
681 indicecol = cursor.getColumnIndex("apellidos");
682 datos.add(cursor.getString(indicecol));
683 indicecol = cursor.getColumnIndex("e_mail");
684 datos.add(cursor.getString(indicecol));
685 indicecol = cursor.getColumnIndex("estadodeforma");
686 datos.add(cursor.getString(indicecol));
687 cursor.close();
688 bd.close();
689
690 return datos;
691 }
692 else {
693 return null;
694 }
695 }
696
822 visualizados.add(cursor.getInt(0));
823 }
824 cursor.close();
825 bd.close();
826 Collections.sort(visualizados);
827
828 return visualizados;
829 }
830 //Obtenemos la fecha y la hora más reciente de un video visto por un
usuario
831 public String[] obtenerFechayHora (int id_usuario, int id_ejercicio){
832
833 SQLiteDatabase bd= getReadableDatabase();
834 Cursor cursor= bd.rawQuery("SELECT fecha, hora FROM
ejercicios_visualizados WHERE " +
835 "id_usuario= " + id_usuario + " AND id_ejercicio= " +
id_ejercicio +
836 " ORDER BY fecha, hora DESC", null);
837 String[] fechayhora= new String[2];
838 if (cursor.moveToFirst()){
839 fechayhora[0]= cursor.getString(0);
840 fechayhora[1]= cursor.getString(1);
841 }
842 cursor.close();
843 bd.close();
844
C.17.1 LecturaJson.java
1 package com.deporte.proyectofc;
2
3 /* Clase que se encarga de leer el archivo json que incluirán todas las
carpetas de rutinas.*/
4
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.io.InputStreamReader;
8 import java.io.UnsupportedEncodingException;
9 import java.util.ArrayList;
10
11 import android.util.JsonReader;
12
13 public class LecturaJson {
14
15 private InputStream input;
16 private String descripcion_rutina;
17
40 reader.beginArray();
41 while(reader.hasNext()){
42 infoejercicios.add(leer_info_ejercicios(reader)
);
43 }
44 reader.endArray();
45 }
46
47 reader.endObject();
48 }
49 reader.endArray();
50 reader.close();
51 return infoejercicios;
52
53 }catch(IOException e){
54 return null;
55 }
56 }catch(UnsupportedEncodingException e){
57 return null;
58 }
59 }
60
61 //Metodo encargado de leer cada uno de los objetos del array que
contiene el objeto "informacion ejercicios" del archivo JSON y
crear un objeto de la clase Ejercicio para cada uno de ellos.
62 public Ejercicio leer_info_ejercicios(JsonReader reader) throws
IOException{
63 String nombre= null;
64 String titulo= null;
65 String subtitulo= null;
66 String descripcion= null;
67 String estadodeforma= null;
68 int repeticiones= 0;
69
70 reader.beginObject();
71 while(reader.hasNext()){
72 String campo= reader.nextName();
73 if (campo.equals("Nombre")){
74 nombre= reader.nextString();
75 }
76 else if (campo.equals("Titulo")){
77 titulo= reader.nextString();
78 }
79 else if (campo.equals("Subtitulo")){
80 subtitulo= reader.nextString();
81 }
82 else if (campo.equals("Descripcion")){
83 descripcion= reader.nextString();
84 }
85 else if (campo.equals("Estado de forma")){
86 estadodeforma= reader.nextString();
87 }
186 Capítulo C. Códigos Java y xml de la aplicación Android
88 else if (campo.equals("Repeticiones")){
89 repeticiones= reader.nextInt();
90 }
91 }
92 reader.endObject();
93
94 //Rellenamos con los datos un objeto de la clase Video
95 return new Ejercicio(nombre, titulo, subtitulo, descripcion,
estadodeforma, repeticiones);
96 }
97
C.17.2 Ejercicio.java
1 package com.deporte.proyectofc;
2
29
30 public String getTitulo() {
31 return titulo;
32 }
33
49 }
C.18 AndroidManifest.xml
7 <uses-sdk
8 android:minSdkVersion="16"
9 android:targetSdkVersion="21" />
10
11 <uses-permission android:name="android.permission.
WRITE_EXTERNAL_STORAGE" />
12 <uses-permission android:name="android.permission.INTERNET"/>
13
14 <application
15 android:allowBackup="true"
16 android:icon="@drawable/ic_launcher"
17 android:label="@string/app_name"
18 android:theme="@style/EstiloApp" >
19 <activity
20 android:name=".MainActivity"
21 android:label="@string/app_name"
22 android:screenOrientation="portrait" >
23 <intent-filter>
24 <action android:name="android.intent.action.MAIN" />
188 Capítulo C. Códigos Java y xml de la aplicación Android
25
26 <category android:name="android.intent.category.LAUNCHER"
/>
27 </intent-filter>
28 </activity>
29 <activity android:name="Inicio"
30 android:screenOrientation="portrait">
31 </activity>
32 <activity android:name="NuevoUsuario"
33 android:screenOrientation="portrait">
34 </activity>
35 <activity android:name="ConfiguracionCuenta" >
36 </activity>
37 <activity android:name="ModificacionCuenta" >
38 </activity>
39 <activity android:name="ReproductorEjercicio" >
40 </activity>
41 <activity
42 android:name="ModificacionContrasenia"
43 android:theme="@android:style/Theme.Dialog" >
44 </activity>
45 <activity android:name="ContraseniaOlvidada" >
46 </activity>
47 <activity android:name="ListEjercicios" >
48 </activity>
49 <activity
50 android:name="AcercaDe"
51 android:theme="@android:style/Theme.Dialog" >
52 </activity>
53 <activity android:name="GraficasRendimiento" >
54 </activity>
55 <activity android:name="ListMasRutinas" >
56 </activity>
57 <activity android:name="ListNuevasRutinas" >
58 </activity>
59 <activity
60 android:name="EliminacionRutina"
61 android:theme="@android:style/Theme.Dialog" >
62 </activity>
63
64 </application>
65
66 </manifest>
C.19 String.xml
4 <string name="app_name">RUTINAPP</string>
5
98 </resources>
Bibliografía
[5] S.Lee. «Creating and Using Databases for Android Applications», International
Journal of Database Theory and ApplicationVol. 5,No. 2, June, 2012.
[9] «AprendeAndroid.com - Crear una Base de Batos SQLite Android» [En lí-
nea]. Available: http://www.aprendeandroid.com/l5/sql1.htm. [Último acceso:
2015].
[10] «Bases de Datos en Android (I): Primeros pasos | sgoliver.net» [En línea].
191
192 Bibliografía
Available: http://www.sgoliver.net/blog/bases-de-datos-en-android-i-primeros-
pasos/. [Último acceso: 2015].
[14] «Programmatically extract a zip file using Java» [En línea]. Availa-
ble: http://www.codejava.net/java-se/file-io/programmatically-extract-a-zip-
file-using-java. [Último acceso: 2015].
[16] «¿Qué son y para qué sirven los hash?: funciones de resumen y firmas digitales»
[En línea]. Available: http://www.genbetadev.com/seguridad-informatica/que-
son-y-para-que-sirven-los-hash-funciones-de-resumen-y-firmas-digitales. [Úl-
timo acceso: 2015].
[19] «Android Chart using AndroidPlot - Java Tutorial Blog» [En línea]. Availa-
ble: http://javapapers.com/android/android-chart-using-androidplot/. [Último
acceso: 2015].
[22] «API de Java para procesamiento JSON: Introducción a JSON» [En línea].
Available: http://www.oracle.com/technetwork/es/articles/java/api-java-para-
Bibliografía 193
[31] «Desarrollo seguro de aplicaciones para dispositivos móviles» [En línea]. Avai-
lable: https://www.incibe.es/blogs/post/Seguridad/BlogSeguridad/Articulo_-
y_comentarios/desarrollo_seguro_de_aplicaciones_para_dispositivos_movi-
les. [Último acceso: 2015].
[35] «18 best Android fitness apps and workout apps» [En línea]. Availa-
ble: http://www.androidauthority.com/best-android-fitness-apps-and-workout-
apps-567999/. [Último acceso: 2015].
[38] «Leer json desde directorio raw en android | Mis Snippets» [En línea]. Availa-
ble: https://carmazone.wordpress.com/2014/09/07/leer-json-desde-directorio-
raw-en-android/. [Último acceso: 2015].
[39] «Parsear Datos JSON En Android Con JsonReader Y Gson» [En línea]. Avai-
lable: http://www.hermosaprogramacion.com/2015/01/android-json-parsing/.
[Último acceso: 2015].
[42] «Android: Loading files from the Assets and Raw folders | 41 Post» [En línea].
Available: http://www.41post.com/3985/programming/android-loading-files-
from-the-assets-and-raw-folders. [Último acceso: 2015].