Xwork's Blog

The lord is waiting to take your hand.

Mostrando las entradas con la etiqueta Android studio. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Android studio. Mostrar todas las entradas

Learning Machine #9 - Conociendo a los archivos GPX.

Hola a todos, el día de hoy vamos a ver cómo es que funcionan los archivos GPX.
Hace poco tiempo, cansado de no hacer nada por la mañana, decidí que sería buena idea volver a hacer un poco de ejercicio. Correr no es una opcion por ahora, estoy tan fuera de forma que probablemente muera de un infarto al primer kilometro, pero no dejare que eso sea un impedimento, así que opte por hacer un recorrido en bicicleta.
Yo soy de esas persona a las que le gusta tener cuantificado todo, asi que si por ejemplo, decidiera correr, necesitaria algo para medir los pasos y en el caso de la bicicleta, algo para medir la distancia.
Una de las maneras más sencillas para hacerlo, es usar mi telefono, al disponer de tantos sensores, debia de haber uno que me ayudara en lo que queria hacer. El GPS que tiene integrado, ayuda a obtener las coordenadas del trayecto.
Actualmente existen muchas aplicaciones que sirven para medir la distancia, velocidad y altitud de un trayecto, la mayoria de las aplicaciones que probé, guardaban toda la informacion en un archivo de extension *.GPX.

Los archivos GPX.

GPX, o GPS eXchange Format (Formato de Intercambio GPS) es un esquema XML pensado para transferir datos GPS entre aplicaciones. Se puede usar para describir puntos (waypoints), recorridos (tracks), y rutas (routes). (Wikipedia/GPX)
En resumidas cuentas, un archivo GPX es solamente un archivo XML en el cual se guardan los datos que se obtienen del GPS (u otras fuentes), pero ¿cómo saber que etiquetas se deben de usar? Bueno, las mas importantes (y que usaremos en futuros proyectos) son las siguientes:

  • <gpx> Indica el inicio del archivo, aqui se definen los esquemas a utilizar
    • <metadata> Indica el inicio de los meta datos.
      • <createtime> Fecha de creación.
      • <starttime> Fecha y hora de inicio del registro.
      •  <endtime> Fecha y hora del final del registro.
      • <distance> Distancia (en metros).
      • <duration> Duración del registro.
      • <maxspeed> Velocidad máxima (Km/h).
      • <avgspeed> Velocidad promedio (Km/h).
      • <vehicle> Tipo de vehículo.
      • <description> Descripción.
    • <trk> Indica el inicio de una pista.
    • <trkseg> Indica el inicio de un segmento de la pista.
    • <trkpt lat="00.0000000" lon="-00.0000000"> Un punto de la pista, cómo atributos se usa "lat" para almacenar la latitud y "lon" para almacenar la longitud.
    • <ele> Indica la elevación en este punto.
    • <speed> Indica la velocidad en este punto.
    • <currentdistance> Indica la distancia total actual.
    • <timeelapsed> Indica el tiempo transcurrido en este punto.
    • <time> Fecha y hora en la que se registro este punto.
Con lo anterior, entonces podemos crear un archivo cómo el que sigue:

<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1" creator="Speed View GPS" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
   <metadata>
	  <createtime>2017-10-13T09:07:03Z</createtime>
	  <starttime>2017-10-13 08:32:23</starttime>
	  <endtime>2017-10-13 09:07:02</endtime>
	  <distance>7081.146</distance>
	  <duration>00:34:38</duration>
	  <maxspeed>7.91</maxspeed>
	  <avgspeed>3.406369</avgspeed>
	  <vehicle>car</vehicle>
	  <description>oct. 13, 2017 8:32:23</description>
   </metadata>
   <trk>
	  <trkseg>
		 <trkpt lat="19.2873547" lon="-98.9054489">
			<ele>2229.5</ele>
			<speed>4.17</speed>
			<currentdistance>4890.514</currentdistance>
			<timeelapsed>00:17:36</timeelapsed>
			<time>2017-10-13T08:49:59Z</time>
		 </trkpt>
		 <trkpt lat="19.2873004" lon="-98.9052929">
			<ele>2229.6</ele>
			<speed>4.68</speed>
			<currentdistance>4908.041</currentdistance>
			<timeelapsed>00:17:40</timeelapsed>
			<time>2017-10-13T08:50:03Z</time>
		 </trkpt>
		 <trkpt lat="19.2872414" lon="-98.9051528">
			<ele>2229.2</ele>
			<speed>5.6099997</speed>
			<currentdistance>4924.208</currentdistance>
			<timeelapsed>00:17:43</timeelapsed>
			<time>2017-10-13T08:50:06Z</time>
		 </trkpt>
		 <trkpt lat="19.2871614" lon="-98.9049907">
			<ele>2228.5</ele>
			<speed>6.16</speed>
			<currentdistance>4943.435</currentdistance>
			<timeelapsed>00:17:46</timeelapsed>
			<time>2017-10-13T08:50:09Z</time>
		 </trkpt>
		 <trkpt lat="19.2871065" lon="-98.9048134">
			<ele>2227.8</ele>
			<speed>6.6099997</speed>
			<currentdistance>4963.037</currentdistance>
			<timeelapsed>00:17:49</timeelapsed>
			<time>2017-10-13T08:50:12Z</time>
		 </trkpt>
		 <trkpt lat="19.2870293" lon="-98.9046317">
			<ele>2228.4</ele>
			<speed>6.8599997</speed>
			<currentdistance>4983.9917</currentdistance>
			<timeelapsed>00:17:52</timeelapsed>
			<time>2017-10-13T08:50:15Z</time>
		 </trkpt>
		 <trkpt lat="19.2869668" lon="-98.9044324">
			<ele>2229.5</ele>
			<speed>7.0699997</speed>
			<currentdistance>5006.054</currentdistance>
			<timeelapsed>00:17:55</timeelapsed>
			<time>2017-10-13T08:50:18Z</time>
		 </trkpt>
		 <trkpt lat="19.2868996" lon="-98.9042383">
			<ele>2229.0</ele>
			<speed>6.22</speed>
			<currentdistance>5027.7915</currentdistance>
			<timeelapsed>00:17:58</timeelapsed>
			<time>2017-10-13T08:50:21Z</time>
		 </trkpt>
		 <trkpt lat="19.2868314" lon="-98.9040744">
			<ele>2229.4</ele>
			<speed>5.42</speed>
			<currentdistance>5046.6025</currentdistance>
			<timeelapsed>00:18:01</timeelapsed>
			<time>2017-10-13T08:50:24Z</time>
		 </trkpt>
		 <trkpt lat="19.2867868" lon="-98.9039388">
			<ele>2229.3</ele>
			<speed>5.17</speed>
			<currentdistance>5061.6934</currentdistance>
			<timeelapsed>00:18:04</timeelapsed>
			<time>2017-10-13T08:50:27Z</time>
		 </trkpt>
		 <trkpt lat="19.2867203" lon="-98.9037992">
			<ele>2229.0</ele>
			<speed>5.42</speed>
			<currentdistance>5078.158</currentdistance>
			<timeelapsed>00:18:07</timeelapsed>
			<time>2017-10-13T08:50:30Z</time>
		 </trkpt>
		 <trkpt lat="19.2866701" lon="-98.9036398">
			<ele>2229.2</ele>
			<speed>5.75</speed>
			<currentdistance>5095.8345</currentdistance>
			<timeelapsed>00:18:10</timeelapsed>
			<time>2017-10-13T08:50:33Z</time>
		 </trkpt>
		 <trkpt lat="19.2866279" lon="-98.9035024">
			<ele>2229.4</ele>
			<speed>4.94</speed>
			<currentdistance>5111.036</currentdistance>
			<timeelapsed>00:18:13</timeelapsed>
			<time>2017-10-13T08:50:36Z</time>
		 </trkpt>
		 <trkpt lat="19.2865573" lon="-98.903338">
			<ele>2230.8</ele>
			<speed>5.35</speed>
			<currentdistance>5130.0107</currentdistance>
			<timeelapsed>00:18:17</timeelapsed>
			<time>2017-10-13T08:50:40Z</time>
		 </trkpt>
		 <trkpt lat="19.2865119" lon="-98.9031719">
			<ele>2232.1</ele>
			<speed>5.7</speed>
			<currentdistance>5148.191</currentdistance>
			<timeelapsed>00:18:20</timeelapsed>
			<time>2017-10-13T08:50:43Z</time>
		 </trkpt>
		 <trkpt lat="19.2864684" lon="-98.9030063">
			<ele>2231.6</ele>
			<speed>5.2999997</speed>
			<currentdistance>5166.3203</currentdistance>
			<timeelapsed>00:18:23</timeelapsed>
			<time>2017-10-13T08:50:46Z</time>
		 </trkpt>
		 <trkpt lat="19.2864122" lon="-98.9028658">
			<ele>2231.3</ele>
			<speed>4.96</speed>
			<currentdistance>5182.3594</currentdistance>
			<timeelapsed>00:18:26</timeelapsed>
			<time>2017-10-13T08:50:49Z</time>
		 </trkpt>
		 <trkpt lat="19.2863534" lon="-98.9026856">
			<ele>2231.1</ele>
			<speed>5.0899997</speed>
			<currentdistance>5202.404</currentdistance>
			<timeelapsed>00:18:30</timeelapsed>
			<time>2017-10-13T08:50:53Z</time>
		 </trkpt>
		 <trkpt lat="19.286309" lon="-98.9025323">
			<ele>2230.1</ele>
			<speed>5.73</speed>
			<currentdistance>5219.2666</currentdistance>
			<timeelapsed>00:18:33</timeelapsed>
			<time>2017-10-13T08:50:56Z</time>
		 </trkpt>
		 <trkpt lat="19.2862376" lon="-98.9023771">
			<ele>2230.8</ele>
			<speed>5.62</speed>
			<currentdistance>5237.3994</currentdistance>
			<timeelapsed>00:18:36</timeelapsed>
			<time>2017-10-13T08:50:59Z</time>
		 </trkpt>
		 <trkpt lat="19.286167" lon="-98.902229">
			<ele>2230.1</ele>
			<speed>5.7999997</speed>
			<currentdistance>5254.8184</currentdistance>
			<timeelapsed>00:18:39</timeelapsed>
			<time>2017-10-13T08:51:02Z</time>
		 </trkpt>
		 <trkpt lat="19.2861174" lon="-98.9020688">
			<ele>2229.7</ele>
			<speed>5.19</speed>
			<currentdistance>5272.635</currentdistance>
			<timeelapsed>00:18:42</timeelapsed>
			<time>2017-10-13T08:51:05Z</time>
		 </trkpt>
		 <trkpt lat="19.2860754" lon="-98.9019254">
			<ele>2229.5</ele>
			<speed>5.14</speed>
			<currentdistance>5288.4106</currentdistance>
			<timeelapsed>00:18:45</timeelapsed>
			<time>2017-10-13T08:51:08Z</time>
		 </trkpt>
		 <trkpt lat="19.2860339" lon="-98.9017654">
			<ele>2228.9</ele>
			<speed>4.89</speed>
			<currentdistance>5306.137</currentdistance>
			<timeelapsed>00:18:48</timeelapsed>
			<time>2017-10-13T08:51:11Z</time>
		 </trkpt>
		 <trkpt lat="19.2859513" lon="-98.9016034">
			<ele>2227.7</ele>
			<speed>4.52</speed>
			<currentdistance>5325.536</currentdistance>
			<timeelapsed>00:18:52</timeelapsed>
			<time>2017-10-13T08:51:15Z</time>
		 </trkpt>
		 <trkpt lat="19.285881" lon="-98.901449">
			<ele>2227.4</ele>
			<speed>4.81</speed>
			<currentdistance>5343.5986</currentdistance>
			<timeelapsed>00:18:56</timeelapsed>
			<time>2017-10-13T08:51:19Z</time>
		 </trkpt>
		 <trkpt lat="19.2858317" lon="-98.9013101">
			<ele>2228.0</ele>
			<speed>4.54</speed>
			<currentdistance>5359.328</currentdistance>
			<timeelapsed>00:18:59</timeelapsed>
			<time>2017-10-13T08:51:22Z</time>
		 </trkpt>
		 <trkpt lat="19.2857715" lon="-98.9011595">
			<ele>2228.3</ele>
			<speed>4.48</speed>
			<currentdistance>5376.5923</currentdistance>
			<timeelapsed>00:19:03</timeelapsed>
			<time>2017-10-13T08:51:26Z</time>
		 </trkpt>
		 <trkpt lat="19.2857089" lon="-98.9010114">
			<ele>2228.3</ele>
			<speed>4.35</speed>
			<currentdistance>5393.6396</currentdistance>
			<timeelapsed>00:19:07</timeelapsed>
			<time>2017-10-13T08:51:30Z</time>
		 </trkpt>
		 <trkpt lat="19.2856641" lon="-98.9008596">
			<ele>2228.6</ele>
			<speed>3.6799998</speed>
			<currentdistance>5410.394</currentdistance>
			<timeelapsed>00:19:11</timeelapsed>
			<time>2017-10-13T08:51:34Z</time>
		 </trkpt>
		 <trkpt lat="19.2855949" lon="-98.9006918">
			<ele>2228.4</ele>
			<speed>4.63</speed>
			<currentdistance>5429.6284</currentdistance>
			<timeelapsed>00:19:15</timeelapsed>
			<time>2017-10-13T08:51:38Z</time>
		 </trkpt>
		 <trkpt lat="19.2855428" lon="-98.9005373">
			<ele>2228.0</ele>
			<speed>4.48</speed>
			<currentdistance>5446.902</currentdistance>
			<timeelapsed>00:19:19</timeelapsed>
			<time>2017-10-13T08:51:42Z</time>
		 </trkpt>
		 <trkpt lat="19.2855065" lon="-98.9003938">
			<ele>2227.8</ele>
			<speed>5.7</speed>
			<currentdistance>5462.5405</currentdistance>
			<timeelapsed>00:19:22</timeelapsed>
			<time>2017-10-13T08:51:45Z</time>
		 </trkpt>
		 <trkpt lat="19.2854442" lon="-98.9002132">
			<ele>2227.5</ele>
			<speed>6.69</speed>
			<currentdistance>5482.7666</currentdistance>
			<timeelapsed>00:19:25</timeelapsed>
			<time>2017-10-13T08:51:48Z</time>
		 </trkpt>
		 <trkpt lat="19.2853641" lon="-98.9000407">
			<ele>2227.6</ele>
			<speed>6.8399997</speed>
			<currentdistance>5502.969</currentdistance>
			<timeelapsed>00:19:28</timeelapsed>
			<time>2017-10-13T08:51:51Z</time>
		 </trkpt>
		 <trkpt lat="19.2853049" lon="-98.8998464">
			<ele>2228.1</ele>
			<speed>6.7999997</speed>
			<currentdistance>5524.433</currentdistance>
			<timeelapsed>00:19:31</timeelapsed>
			<time>2017-10-13T08:51:54Z</time>
		 </trkpt>
		 <trkpt lat="19.2852443" lon="-98.8996762">
			<ele>2228.1</ele>
			<speed>6.25</speed>
			<currentdistance>5543.546</currentdistance>
			<timeelapsed>00:19:34</timeelapsed>
			<time>2017-10-13T08:51:57Z</time>
		 </trkpt>
		 <trkpt lat="19.2851887" lon="-98.8994991">
			<ele>2229.2</ele>
			<speed>6.6</speed>
			<currentdistance>5563.195</currentdistance>
			<timeelapsed>00:19:37</timeelapsed>
			<time>2017-10-13T08:52:00Z</time>
		 </trkpt>
		 <trkpt lat="19.2851245" lon="-98.8993265">
			<ele>2229.4</ele>
			<speed>6.25</speed>
			<currentdistance>5582.688</currentdistance>
			<timeelapsed>00:19:40</timeelapsed>
			<time>2017-10-13T08:52:03Z</time>
		 </trkpt>
		 <trkpt lat="19.2850556" lon="-98.8991673">
			<ele>2230.2</ele>
			<speed>6.44</speed>
			<currentdistance>5601.079</currentdistance>
			<timeelapsed>00:19:43</timeelapsed>
			<time>2017-10-13T08:52:06Z</time>
		 </trkpt>
		 <trkpt lat="19.2850039" lon="-98.8989882">
			<ele>2231.2</ele>
			<speed>6.43</speed>
			<currentdistance>5620.7734</currentdistance>
			<timeelapsed>00:19:46</timeelapsed>
			<time>2017-10-13T08:52:09Z</time>
		 </trkpt>
		 <trkpt lat="19.284942" lon="-98.8988195">
			<ele>2230.0</ele>
			<speed>6.42</speed>
			<currentdistance>5639.808</currentdistance>
			<timeelapsed>00:19:49</timeelapsed>
			<time>2017-10-13T08:52:12Z</time>
		 </trkpt>
		 <trkpt lat="19.2848768" lon="-98.8986502">
			<ele>2229.3</ele>
			<speed>6.33</speed>
			<currentdistance>5659.0356</currentdistance>
			<timeelapsed>00:19:52</timeelapsed>
			<time>2017-10-13T08:52:15Z</time>
		 </trkpt>
		 <trkpt lat="19.2848074" lon="-98.8984819">
			<ele>2228.3</ele>
			<speed>6.2999997</speed>
			<currentdistance>5678.3735</currentdistance>
			<timeelapsed>00:19:55</timeelapsed>
			<time>2017-10-13T08:52:18Z</time>
		 </trkpt>
		 <trkpt lat="19.2847355" lon="-98.8983052">
			<ele>2227.7</ele>
			<speed>6.24</speed>
			<currentdistance>5698.695</currentdistance>
			<timeelapsed>00:19:58</timeelapsed>
			<time>2017-10-13T08:52:21Z</time>
		 </trkpt>
		 <trkpt lat="19.2846779" lon="-98.8981337">
			<ele>2227.4</ele>
			<speed>6.24</speed>
			<currentdistance>5717.8203</currentdistance>
			<timeelapsed>00:20:01</timeelapsed>
			<time>2017-10-13T08:52:24Z</time>
		 </trkpt>
		 <trkpt lat="19.2846228" lon="-98.8979652">
			<ele>2227.3</ele>
			<speed>6.0899997</speed>
			<currentdistance>5736.634</currentdistance>
			<timeelapsed>00:20:04</timeelapsed>
			<time>2017-10-13T08:52:27Z</time>
		 </trkpt>
		 <trkpt lat="19.2845703" lon="-98.8978222">
			<ele>2228.1</ele>
			<speed>5.62</speed>
			<currentdistance>5752.7866</currentdistance>
			<timeelapsed>00:20:07</timeelapsed>
			<time>2017-10-13T08:52:30Z</time>
		 </trkpt>
		 <trkpt lat="19.2845295" lon="-98.8976665">
			<ele>2228.6</ele>
			<speed>5.0299997</speed>
			<currentdistance>5769.9194</currentdistance>
			<timeelapsed>00:20:10</timeelapsed>
			<time>2017-10-13T08:52:33Z</time>
		 </trkpt>
		 <trkpt lat="19.2844665" lon="-98.8974973">
			<ele>2228.4</ele>
			<speed>5.3199997</speed>
			<currentdistance>5789.093</currentdistance>
			<timeelapsed>00:20:14</timeelapsed>
			<time>2017-10-13T08:52:37Z</time>
		 </trkpt>
		 <trkpt lat="19.2844181" lon="-98.8973579">
			<ele>2228.4</ele>
			<speed>5.3199997</speed>
			<currentdistance>5804.718</currentdistance>
			<timeelapsed>00:20:17</timeelapsed>
			<time>2017-10-13T08:52:40Z</time>
		 </trkpt>
		 <trkpt lat="19.284345" lon="-98.8971953">
			<ele>2228.7</ele>
			<speed>5.46</speed>
			<currentdistance>5823.6953</currentdistance>
			<timeelapsed>00:20:20</timeelapsed>
			<time>2017-10-13T08:52:43Z</time>
		 </trkpt>
		 <trkpt lat="19.2843003" lon="-98.8970488">
			<ele>2228.7</ele>
			<speed>5.6099997</speed>
			<currentdistance>5839.8765</currentdistance>
			<timeelapsed>00:20:23</timeelapsed>
			<time>2017-10-13T08:52:46Z</time>
		 </trkpt>
		 <trkpt lat="19.2842265" lon="-98.8968865">
			<ele>2230.9</ele>
			<speed>5.7599998</speed>
			<currentdistance>5858.798</currentdistance>
			<timeelapsed>00:20:26</timeelapsed>
			<time>2017-10-13T08:52:49Z</time>
		 </trkpt>
		 <trkpt lat="19.2841685" lon="-98.8967161">
			<ele>2229.8</ele>
			<speed>5.7599998</speed>
			<currentdistance>5877.8633</currentdistance>
			<timeelapsed>00:20:29</timeelapsed>
			<time>2017-10-13T08:52:52Z</time>
		 </trkpt>
		 <trkpt lat="19.2840928" lon="-98.8965554">
			<ele>2230.3</ele>
			<speed>5.52</speed>
			<currentdistance>5896.795</currentdistance>
			<timeelapsed>00:20:32</timeelapsed>
			<time>2017-10-13T08:52:55Z</time>
		 </trkpt>
		 <trkpt lat="19.2840414" lon="-98.8964041">
			<ele>2230.1</ele>
			<speed>5.83</speed>
			<currentdistance>5913.686</currentdistance>
			<timeelapsed>00:20:35</timeelapsed>
			<time>2017-10-13T08:52:58Z</time>
		 </trkpt>
		 <trkpt lat="19.2839835" lon="-98.8962396">
			<ele>2230.4</ele>
			<speed>6.31</speed>
			<currentdistance>5932.1357</currentdistance>
			<timeelapsed>00:20:38</timeelapsed>
			<time>2017-10-13T08:53:01Z</time>
		 </trkpt>
		 <trkpt lat="19.2839417" lon="-98.8960511">
			<ele>2230.1</ele>
			<speed>5.81</speed>
			<currentdistance>5952.5576</currentdistance>
			<timeelapsed>00:20:41</timeelapsed>
			<time>2017-10-13T08:53:04Z</time>
		 </trkpt>
		 <trkpt lat="19.2838761" lon="-98.8958997">
			<ele>2230.2</ele>
			<speed>5.79</speed>
			<currentdistance>5970.072</currentdistance>
			<timeelapsed>00:20:44</timeelapsed>
			<time>2017-10-13T08:53:07Z</time>
		 </trkpt>
		 <trkpt lat="19.283805" lon="-98.8957721">
			<ele>2229.7</ele>
			<speed>5.31</speed>
			<currentdistance>5985.7344</currentdistance>
			<timeelapsed>00:20:47</timeelapsed>
			<time>2017-10-13T08:53:10Z</time>
		 </trkpt>
		 <trkpt lat="19.2837329" lon="-98.8956155">
			<ele>2228.8</ele>
			<speed>4.69</speed>
			<currentdistance>6004.08</currentdistance>
			<timeelapsed>00:20:51</timeelapsed>
			<time>2017-10-13T08:53:14Z</time>
		 </trkpt>
		 <trkpt lat="19.2836277" lon="-98.8955173">
			<ele>2229.0</ele>
			<speed>5.2599998</speed>
			<currentdistance>6019.7734</currentdistance>
			<timeelapsed>00:20:54</timeelapsed>
			<time>2017-10-13T08:53:17Z</time>
		 </trkpt>
		 <trkpt lat="19.2835321" lon="-98.8953833">
			<ele>2229.3</ele>
			<speed>5.47</speed>
			<currentdistance>6037.611</currentdistance>
			<timeelapsed>00:20:57</timeelapsed>
			<time>2017-10-13T08:53:20Z</time>
		 </trkpt>
		 <trkpt lat="19.2834933" lon="-98.8952368">
			<ele>2229.5</ele>
			<speed>5.35</speed>
			<currentdistance>6053.6636</currentdistance>
			<timeelapsed>00:21:00</timeelapsed>
			<time>2017-10-13T08:53:23Z</time>
		 </trkpt>
		 <trkpt lat="19.2834481" lon="-98.8950919">
			<ele>2228.7</ele>
			<speed>5.22</speed>
			<currentdistance>6069.696</currentdistance>
			<timeelapsed>00:21:03</timeelapsed>
			<time>2017-10-13T08:53:26Z</time>
		 </trkpt>
		 <trkpt lat="19.283385" lon="-98.8949165">
			<ele>2228.7</ele>
			<speed>4.7799997</speed>
			<currentdistance>6089.425</currentdistance>
			<timeelapsed>00:21:07</timeelapsed>
			<time>2017-10-13T08:53:30Z</time>
		 </trkpt>
		 <trkpt lat="19.2833551" lon="-98.8947334">
			<ele>2229.2</ele>
			<speed>4.8399997</speed>
			<currentdistance>6108.9653</currentdistance>
			<timeelapsed>00:21:11</timeelapsed>
			<time>2017-10-13T08:53:34Z</time>
		 </trkpt>
	  </trkseg>
   </trk>
</gpx>

Que tendrá cómo resultado algo cómo lo de la imagen siguiente:
Puedes ver el archivo GPX en http://maplorer.com/
No todos las etiquetas son necesarias para que el archivo funcione, pero por default la aplicación que use crea todas las etiquetas anteriores. Cómo eso se me hace un desperdicio, en el siguiente post, vamos ver cómo hacer uso del GPS en un dispositivo android (con GPS, obviamente) y crear nuestros propios archivos GPX para poder llevar un registro de los recorridos en bicicleta que haga.

Por ahora es todo, los leo luego.

Vamos a programar #41 - Números palindrómicos (ver. Java for android)

Hola de nuevo a todos, el día de hoy y despúes de unas largas y mal merecidas "vacaciones", vamos a continuar con más temas de programación.
La última vez que vimos un post sobre programación, vimos la conjetura de los números palíndrómicos e hicimos un programa en C# para windows. Hoy y cómo en ocasiones anteriores, vamos a ver cómo hacer una versión para android.

El código.

El código en java que hace funcionar las cosas es el siguiente:

package com.mdev.numerospalindromos;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import java.lang.String;
import java.math.BigInteger;
import android.widget.ArrayAdapter;
import java.util.ArrayList;
public class MainActivity extends Activity {
	Button BtnCalculate;
	TextView TxtNumberIn;
	TextView TxtIterations;
	ListView LVResultados;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		BtnCalculate = (Button)findViewById(R.id.BtnCalculate);
		TxtNumberIn = (TextView)findViewById(R.id.TXTNumberIn);
		TxtIterations = (TextView)findViewById(R.id.TxtIterations);
		LVResultados = (ListView)findViewById(R.id.LVResults);
		BtnCalculate.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				GetNumber(TxtNumberIn.getText().toString(), Integer.parseInt(TxtIterations.getText().toString()));
			}
		});
	}
	private boolean IsPalindrome(String Number)
	{
		String Value = ReverseNumber(Number);
		if (Number.equals(Value))
			return true;
		else
			return false;
	}
	private String ReverseNumber(String NumberIn)
	{
		char[] charArray = NumberIn.toCharArray();
		char[] OutArray = new char[charArray.length];
		for (int i = charArray.length-1; i >= 0 ; i--){
			OutArray[charArray.length - 1 - i] = charArray[i];
		}
		return new String(OutArray);
	}
	private void GetNumber(String Number1, Integer MaxIterations)
	{
		ArrayList Items = new ArrayList();
		int Iterator = 1;
		if (IsPalindrome(Number1))
		{
			Items.add("0 iteraciones " + Number1 + " cumple la conjetura");
		}
		else
		{
			BigInteger Result = new BigInteger(Number1);

			while (!IsPalindrome(Result.toString()))
			{
				BigInteger NewNumber = new BigInteger(ReverseNumber(Result.toString()));
				Result = Result.add(NewNumber);
				if (Iterator >= MaxIterations)
				{
					break;
				}
				Iterator += 1;
				Items.add(Iterator + " iteraciones " + Result);
			}

			if (IsPalindrome(Result.toString()))
				Items.add("La conjetura se cumple");
			else
				Items.add("La conjetura no se cumple.");
		}
		final ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1,Items);
		LVResultados.setAdapter(adapter);
	}
}

El código consta de 3 funciones. La primera de ellas en orden de aparición, es: IsPalindrome y aunque su nombre lo diga o lo haga parecer, esta función no es la encargada de hacer los cálculos. La función simplemente comprueba que el valor que se pasa cómo parámetro, cumpla con la condición: "es igual si se lee al derecho que al revés", usando la función "ReverseNumber". Si la condición se cumple, la función regresa un valor booleano "true" en caso contrario regresa "false".

La función "ReverseNumber", sirve para invertir el orden de los dígitos de un número, es decir; el primer número pasa a ser el último y viceversa. hay que recordar que la conjetura nos dice que se debe de obtener un palíndromo si se suma un número que sea el mismo número pero invertido. Esta función recibe un parámetro del tipo "String" que representa el número que queremos invertir. La función usa dos "Arrays" que contienen los dígitos almacenados como caracteres, por lo que "2" es algo totalmente diferente a 2, el parámetro de entrada, es el primero en separarse, con eso obtenemos el tamaño que tendrá el otro "Array" y lo asignamos al momento de crearlo. Para formar el valor de salida, simplemente recorremos en orden inverso el "Array" que contiene los digitos del valor de entrada. La función regresa un valor del tipo "String" que representa el parámetro  de entrada en orden inverso.

Finalmente el procedimiento que se encarga de comprobar la conjetura es "GetNumber". Este procedimiento hace todos los pasos.  El procedimiento recibe dos parámetros, el primero del tipo "String", es el número al cual le queremos aplica la conjetura, el segundo, es in valor del tipo "Integer" que contiene el valor máximo de la iteraciones. Antes de si quiera empezar con los cálculos, comprobamos que el valor que se ingresa  cumpla la conjetura, si lo hace, simplemente informamos que el  número es palíndromo y terminamos el procedimiento. En  caso contrario, empezamos convirtiendo el valor de "String " a "BigInteger". Luego iniciamos un bucle "while" en el cual haremos uso de la función "IsPalindrome" mientras está sea falsa  o mientras el valor de la variable "Iterator" sea menor que  el parámetro "MaxIterations". Cuando cualquiera de las condiciones es verdadera, salimos del bucle; si salimos debido a que la función "IsPalindrome" resulta verdadera,  mostramos un mensaje indicando que el número cumplió con la conjetura; pero si la salida del bucle se debe a que el número de iteraciones alcanzo el máximo indicado por el usuario, mostramos un  mensaje que la conjetura no se cumplió. En este caso, que un número no cumpla la conjetura en X numero de iteraciones, no quieres decir que por ello este exento, algunas veces bastará con aumentar el número de iteraciones.

Y bien, por ahora es todo, al igual que las otra aplicaciones, el código fuente pronto la publicaré en la sección de descargas, el APK ya compilado, lo puedes descargar de mi dropbox.
Antes de terminar, debo aclarar que la aplicación aun no es a prueba de errores, y peor aun, no es a prueba de si misma. Todo es meramente ilustrativo, porque la aplicación va a hacer lo que el usuario le diga y por el momento no es recomendable pasar de las 1000 iteraciones; aunque la mayoría de los numero comprendidos entre 1 y 1000 la cumplen en unos cuantos pasos, puede pasar que haya un número que ni con 20000 iteraciones la cumpla, al hacer 20000 pasos la aplicación demandará memoria y capacidad de procesamiento por lo que las cosas se pueden volver inestables, además como todas las comprobaciones se hacen en el hilo principal, la aplicación se bloqueará hasta que termine (cualquiera de los dos casos, si se cumple o si se llego al limite establecido), en los siguiente post veremos cómo usar "threads" (C# y Java) y cómo manejarlas un poco, para; si por ejemplo; comenzamos un proceso largo, lo podamos detener en cualquier momento (si aun así quieres probar los alcances de tu dispositivo, puedes probar el número 196 con el límite que más gustes, pero quedas advertido que ni con 24000 iteraciones se cumple la conjetura).

Por ahora es todo. Los leo luego.

Vamos a programar #39 - La conjetura de Collatz (Números maravillosos) Versión Android.

Hola de nuevo a todos, el día de hoy vamos a continuar con la conjetura de Collatz.
En el post anterior de "Vamos a programar", hicimos la versión de C#, cosa que resulto realmente fácil ya que en C# se puede hacer uso del tipo "BigInteger". En java para android, también existe el tipo "BigInteger" y en esta ocasión veremos un poco de cómo usarlo.

El código.

El código que hace funcionar la aplicación de la conjetura de Collatz en java para android es el siguiente:
package com.mdev.collatz;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

import java.math.BigInteger;
import java.util.ArrayList;


import static android.R.attr.button;

public class MainActivity extends AppCompatActivity {

	Button BtnCalculate;
	TextView TxtNumberIn;
	ListView LVResultados;

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		BtnCalculate = (Button) findViewById(R.id.BtnCalculate);
		TxtNumberIn = (TextView) findViewById(R.id.TXTNumberIn);
		LVResultados = (ListView) findViewById(R.id.LVResults);

		BtnCalculate.setOnClickListener(new View.OnClickListener()
		{
			@Override
			public void onClick(View view) {
				CalculateCollatz(TxtNumberIn.getText().toString());
			}
		});

	}
	private void CalculateCollatz(String Number)
	{
		ArrayList Items = new ArrayList();
		BigInteger MyNumber = new BigInteger(Number);
		while (MyNumber.compareTo(BigInteger.ONE) > 0)
		{
			if (IsEven(MyNumber)) {
				MyNumber = MyNumber.divide(BigInteger.valueOf(2));
				Items.add(MyNumber.toString());
			}else{
				MyNumber = MyNumber.multiply(BigInteger.valueOf(3));
				MyNumber = MyNumber.add(BigInteger.valueOf(1));
				Items.add(MyNumber.toString());
			}
		}
		final ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1,Items);
		LVResultados.setAdapter(adapter);
	}

	private boolean IsEven(BigInteger Number){
		BigInteger Result = Number.remainder(BigInteger.valueOf(2));
		int CompareResult = Result.compareTo(BigInteger.ZERO);
			if(CompareResult == 0)
			{
				return true;
			}else{
				return false;
			}
	}
}

Para empezar crearemos los controles que vamos a necesitar, está aplicación consta únicamente de 4 controles (5 en realidad, puedes revisarlo en el código completo).

  • 1 Textview.
  • 1 EditText
  • 1 Button
  • 1 Listview
Solo vamos a interactuar con los últimos tres pos lo que solo esos son los que crearemos tambien en el código.
Para tratar de simplificar un poco el código, hice lo traté de hacer lo mismo que en c#. Primero, iba a pedir un numero en formato de texto, luego crear una funcion que calcule la conjetura de Collatz y mostrar cada paso que se dio en un "ListView".
La cosa es sencilla, y podemos repetir lo que ya habíamos hecho, pero en java, a diferencia de c#, el tipo BigInteger no es tan simple de manejar.
En Java, el tipo BigInteger dispone de sus operaciones por separado, las comparaciones se deben de hacer desde los métodos contenidos en el tipo BigInteger. Eso quiere decir que si queremos comparar el número "1" con el valor BigInteger "1", no podemos realizarlo porque a diferencia de C#, aquí no se hace una conversión implícita; nosotros debemos de hacerla de forma explicita haciendo uso de los métodos contenidos en el tipo "BigInteger".

Retomando nuestro código, primero veremos la función "IsEven". la función "IsEven" recibe un parámetro del tipo "BigInteger", este, es el numero de cual queremos determinar si es par o no. Para hacerlo, hacemos uso de dos variables; la primera, llamada "Result" del tipo "BigInteger", se crea y se le asigna el valor que resulta del uso del método del parámetro de entrada "Number", el método que se usa es ".remainder()", este método lo que hace, es devolver el residuo de la división entre el numero "Number" y el parámetro que se le asigne; para este caso, debemos de dividir entre dos, pero cómo no se puede usar directamente "2", usamos otro procedimiento del tipo "BigInteger". el método que usamos es "BigInteger.valueOf()", este procedimiento lo que hace, es crear una versión del número que se pasa cómo parámetro en tipo "BigInteger".
Lo anterior se hace para determinar si un número es par o no (hay que recordar que los número pares, son aquellos que al ser divididos entre dos, dan un número entero). El resultado que obtenemos es 1 o 0 (cualquiera de los dos del tipo BigInteger).
Solo nos queda hacer la comparación, si el resultado es "0" entonces el número es par, para hacer la comparación, usamos otro procedimiento más del tipo "BigInteger". Hacemos uso de la función ".compareTo", lo hacemos en la variable resultado y le pasamos cómo parámetro una constante llamada "BigInteger.ZERO"; la constante vale 0. la función regresa un valor del tipo int con cualquiera de los 3 valores posibles:

  1. -1 si el valor es menor que el BigInteger desde el cual se llama.
  2. 0 si el valor es igual que el BigInteger desde el cual se llama.
  3. 1 si el valor es mayor que el BigInteger desde el cual se llama.
Para determinar si la función "IsEven" devuelve "true" o "false", creamos una variable del tipo "int" llamada "CompareResult" a la que le asignamos el valor de la función "Result.compareTo" con parámetro "BigInteger.ZERO".
Si el valor de la variable "result" es "0", la función IsEven devuelve "true", en caso contrario, "false".

El procedimiento "CalculateCollatz", es el encargado de hacer los cálculos, para eso, primero creamos la variable del tipo "BigIntger" llamada "MyNumber". Al usar el constructor del tipo "BigInteger", le podemos pasar un valor del tipo "String", en este caso le asignamos el parámetro "Number", con eso creamos un valor del tipo "BigInteger" con valor del "String" del parámetro.
Después creamos un bucle "while" con la condición "Number" sea mayor que "BigInteger.ONE". Con la función "IsEven", podemos replicar lo que hicimos en c# y dentro del bucle, verificamos si el número es par o no y dependiendo del caso, hacer los cálculos.
Para multiplicar un valor "BigInteger", debemos de hacer uso del método "BigInteger.multiply", este recibe cómo parámetro un "BigInteger" que será el multiplicador y el resultado, sera un valor BigInteger con el valor de la multiplicación. Para dividir un valor "BigInteger", debemos de hacer uso del método "BigInteger.divide", este recibe cómo parámetro un "BigInteger" que será el divisor y el resultado, sera un valor "BigInteger" con el valor de la división. lo mismo para la suma, debemos de hacer uso de "BigInteger.add".
Finalmente creamos un "ArrayAdapter" al que agregaremos todos los pasos que se hicieron y este se asina al control "ListView".

El resultado. Puedes descargar la APK con la aplicación ya compilada, el código completo lo subiré en breve, agregare threads para que la interfaz no se bloquee (aunque así como esta funciona bien), aunque con el código anterior, se cubre toda la parte lógica de la aplicación.

Bien, por ahora es todo.
Los leo luego

Vamos a programar #37 - DecToAny, Versión java (Android).

Hola a todos, el día de hoy vamos a ver una tercera entrega de la aplicación DecToAny, en los últimos dos post, vimos cómo crear una versión para Windows (hecha en C#), una versión para la web (hecha en javascript) y finalmente el día de hoy vamos a ver una versión para Android (hecha en Java).

Creando la interfaz.

La interfaz principal está compuesta por 4 componentes:
  • 1 TextView.
  • 1 EditText (Numérico).
  • 1 Button.
  • 1 ListView.
Todos los componentes se pueden crear con el siguiente código:

 <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:app="http://schemas.android.com/apk/res-auto"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	tools:context="com.mdev.com.dectoany.MainActivity">

	<LinearLayout
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:orientation="vertical"
		tools:layout_editor_absoluteX="8dp"
		tools:layout_editor_absoluteY="8dp">

		<TextView
			android:id="@+id/lblNumberIn"
			android:layout_width="match_parent"
			android:layout_height="wrap_content"
			android:text="Número a convertir" />

		<EditText
			android:id="@+id/TXTNumberIn"
			android:layout_width="match_parent"
			android:layout_height="wrap_content"
			android:ems="10"
			android:inputType="number" />

		<Button
			android:id="@+id/BtnConvert"
			android:layout_width="match_parent"
			android:layout_height="wrap_content"
			android:text="Convertir" />

		<ListView
			android:id="@+id/LVResults"
			android:layout_width="match_parent"
			android:layout_height="match_parent" />
	</LinearLayout>
</android.support.constraint.ConstraintLayout>

Con esto, tendremos todos los controles necesarios para que el usuario interactue con la aplicación, ahora solo queda implementar el código que hace funcionar las cosas.

El Código que hace funcionar las cosas.

El código es muy parecido al que usamos para la versión de C#. Solo que en este caso, vamos a usar el control ListView para mostrar los resultados de las conversiones.
El código java de DecToAny es el siguiente:

package com.mdev.com.dectoany;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class MainActivity extends Activity {

	Button BtnConvert;
	ListView LVResults;
	TextView TxtNumberIn;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		BtnConvert = (Button) findViewById(R.id.BtnConvert);
		TxtNumberIn = (TextView) findViewById(R.id.TXTNumberIn);
		LVResults = (ListView) findViewById(R.id.LVResults);

		BtnConvert.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {
				DoConvert();
			}
		});
	}

	private void DoConvert(){
		ArrayList Items = new ArrayList();
		String CurrentNumber = TxtNumberIn.getText().toString();
		int MyNumber = Integer.parseInt(CurrentNumber);
		for (int I = 2; I < 30; I++){
			Items.add(ListToString(NumbersList(MyNumber, I))+"\n"+"Base "+ I);
		}
		final ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1,Items);
		LVResults.setAdapter(adapter);
	}
	private String ListToString(List<Integer> Numbers)
	{
		Collections.reverse(Numbers);
		String Result = "";
		for (int i = 0; i < Numbers.size(); i++){
			int CurrentNumber = Numbers.get(i);
			if (CurrentNumber < 10)
				Result += CurrentNumber;
			else
				Result += NumberToChar(Numbers.get(i));
		}
		return Result;
	}
	private char NumberToChar(int Number){
			return (char)(Number + 55);
	}

	private List<Integer> NumbersList(Integer InNumber, int Base) {
		Integer LastNumber = InNumber;
		Integer Garbage;
		List<Integer> result = new ArrayList<Integer>();
		if (LastNumber < Base) {
			result.add(LastNumber);
		}
		else         {
			if (LastNumber >= Base) {
				do {
					Garbage = LastNumber % Base;
					LastNumber = LastNumber / Base;
					if (LastNumber < Base) {
						result.add(Garbage);
						result.add(LastNumber);
					} else {
						result.add(Garbage);
					}
				} while (LastNumber >= Base);
			}
		}
		return result;
	}
}

Para esta aplicación vamos usar 4 funciones, la primera de ellas en orden de uso (no de aparicion) es "NumberList", esta es la función que va a realizar los cálculos; cómo parámetros de entrada, recibirá un parámetro del tipo "Integer" llamado "InNumber" que es el número decimal que queremos convertir; seguido de una valor del tipo "int" llamado "Base", este; cómo su nombre lo indica; es la base de destino a la cual queremos convertir nuestro número decimal. La función regresa una lista del tipo "List<Integer>" con los valores de los dígitos para formar el número, pero en orden inverso.
Después tenemos la función "ListToString" que será la encargada de formar un número y devolverlo en forma de "String". Esta función recibe cómo parámetro un lista del tipo "List<Integer>". Esta función se encarga primero; en invertir el orden de la lista que se ingresa cómo parámetro. Después comprueba si el valor es menor a 10 (decimal), si es así, simplemente agrega al string "Result" (variable interna de la función) el valor actual, en caso contrario, manda a llamar a la función "NumberToChar" que se encargara de asignar una letra de la A a la Z a los valores mayores a 10. Finalmente devuelve un "string" que contiene la representación de los resultados de la función "NumberToList" en el formato adecuado para que cualquier persona lo pueda entender.
La función "NumberToChar" simplemente devuelve el numero que se ingresa cómo parámetro + 55.
Para la aplicación de Android también usamos un procedimiento llamado "DoConvert"; en el cual se manda a llamar a las otras funciones dentro de un bucle que va del 2 al 29. En este procedimiento se crea un "ArrayAdapter" que se llenará con los valores que se crean en el ciclo "for" y finalmente se asignan al "ListView".
Con esto podemos obtener de forma fácil el valor de un numero decimal en 27 bases diferentes. En todas las aplicaciones (Android, Windows, Web)no he revisado los errores que puedan haber, no porque sea perezoso o algo similar), pero un error critico existe en la version Web, en esta al poder escoger cual es la base a la que queremos convertir nuestro numero, podemos introducir base 1, pero esta base es imposible de usar, ya que solo disponemos del numero 0, entonces al hacer las divisiones, entramos al bucle "while", pero nunca podremos salir de el, y en algún momento la memoria se acabará hasta que finalmente el navegador en el cual se usa falle. Aunque el sentido común dice que no se debe de hacer lo antes descrito, nunca falta aquel que lo haga solo para saber que pasa. Para evitarlo basta con comprobar las base de destino, si es menor a 2, simplemente hay que informarle al usuario y terminar la ejecución.
Y bien, por ahora es todo, la aplicación compilada la puedes descargar de mi dropbox, el código lo subiré un poco después, primero voy a corregir un error minusculo en el proyecto. (Debería de ser com.mdev.dectoany, en su lugar puse com.mdev.com.dectoany ).

Los leo luego

Vamos a programar #30 - El código de Clock View (Android)

Hola de nuevo a todos, el día de hoy vamos a explicar el código que hace funcionar la aplicación de android Clock View.


La interfaz.

Antes que nada vamos a empezar ´por la interfaz, para el caso de Clock View esta compuesta de 2 partes. La primera son todos los botones de "acción" y parte principal de la aplicacion; la segunda solo es la lista que se encarga de mostrar todos los dispositivos que estén emparejados al dispositivo.

Para mostrar la interfaz principal, en el Layout principal, debemos de agregar el siguiente código.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/activity_main"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:paddingBottom="@dimen/activity_vertical_margin"
 android:paddingLeft="@dimen/activity_horizontal_margin"
 android:paddingRight="@dimen/activity_horizontal_margin"
 android:paddingTop="@dimen/activity_vertical_margin"
 tools:context="com.mdev.clockview.MainActivity">

 <ScrollView
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:layout_alignParentTop="true"
  android:layout_alignParentLeft="true"
  android:layout_alignParentStart="true">

  <LinearLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:orientation="vertical">

   <TextView
    android:text="La hora actual es:"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/TXTView"
    tools:ignore="HardcodedText" />

   <TextClock
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="14dp"
    android:id="@+id/TXTClock"
    android:typeface="sans"
    android:textAlignment="center"
    android:textSize="30sp"
    android:fontFamily="monospace"/>

   <Button
    android:text="Conectar con arduino."
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/BtnConnect"
    android:textAlignment="center"
    android:textAllCaps="false" />

   <Button
    android:text="Cambiar 12/24 Horas."
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/BtnFormat" />

   <Button
    android:text="No/Mostrar segundos."
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/BtnShowS"/>

   <Button
    android:text="Apagar/Encender matrices"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/BtnOn"/>

   <Button
    android:text="Sincronizar hora."
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/BtnSync"/>

   <TextView
    android:text="Nivel de brillo:"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="11dp"
    android:layout_marginStart="11dp"
    android:layout_marginTop="10dp"
    android:id="@+id/textView2" />

   <SeekBar
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="12dp"
    android:id="@+id/SKBrillo"
    android:max="15"
    android:progress="5" />

   <TextView
    android:text="Brillo: 15"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/txtSeekValue"/>

   <TextView
    android:text="Esperando a ClockView."
    android:layout_width="match_parent"
    android:id="@+id/TxtArduinoReturn"
    android:layout_height="wrap_content" />

   <Button
    android:text="Desconectar"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/BtnDisconnect" />
  </LinearLayout>
 </ScrollView>
</RelativeLayout>

Tras implementar el código, la aplicacion lucira algo similar a la siguiente imagen:



Cómo verás, solo consta de 6 botones, una barra de seek y las etiquetas para mostrar texto.
La funcion de cada uno, la veremos en el código Java, pero antes, vamos a terminar con la interfaz. La lista que contiene los dispositivos emparejados, es un layout que solo contiene un list view y su código es el siguiente;

<LinearLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/bt_list"
 android:orientation="vertical"
 android:layout_width="match_parent"
 android:layout_height="match_parent">

 <ListView
  android:id="@+id/BTList"
  android:layout_width="match_parent"
  android:layout_height="200dp"
  android:headerDividersEnabled="true"
  android:footerDividersEnabled="false">
 </ListView>

</LinearLayout>

Con esto completamos la parte de la interfaz, además en el código completo he agregado el diseño para cuando la orientacion del dispositivo es vertical y para cuando es horizontal, en lo personal preferí forzar a que la aplicacion siempre este en horizontal, pero eres libre de cambiarla a tu gusto.

El código en java.

El código en Java, lo he modificado un poco desde la última vez, corregi algunos errores, uno de ellos era: cuando se intentaba hacer la conexion y esta fallaba, al hacer click en cualquier botón, este intentaba mandar algo a traves del socket que supestemente debio de haber sido creado, al no estar disponible, la aplicion "crasheaba" y salia; el error se debia que a pesar de no haber conexion, la condicion que se debia de comprobar era verdadera debido a que se verificaba si el bluetooth estaba activado; la condicion se podia cumplir, pero eso significaria necesariamente que el socket ya se hubiera creado. Para solucionarlo, simplemente use un flag boleano que solo se cambiaba cuando se abria o cerraba la conexion, ahora si no hay conexion, simplemente no se intenta enviar nada.

Ademas coerregi algunos detalles y bugs minimos. El código actualizado es el siguiente:

package com.mdev.clockview;

import android.app.Activity;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Handler;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.Calendar;

import static com.mdev.clockview.R.layout.paired_devices_list;

public class MainActivity extends Activity {

 BluetoothAdapter myBluetoothAdapter = null;
 Button BtnConnect, BtnOnOff, BtnSecondsShow, BtnSyncHour, BtnChangeFormat,BtnDisconnect;
 SeekBar SkBright;
 TextView TxtArduinoRec, TxtBrigthValue;

 String address = null;
 private ProgressDialog progress;
 BluetoothSocket btSocket = null;
 private boolean isBtConnected = false;
 private static final UUID myUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
 final int RECEIVE_MESSAGE = 1;
 private StringBuilder sb = new StringBuilder();
 private ConnectedThread mConnectedThread;
 Handler h;

 private Set<BluetoothDevice> pairedDevices;
 ListView myListView;
 ArrayAdapter BTArrayAdapter;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  //setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

  setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
  BtnConnect = (Button)findViewById(R.id.BtnConnect);
  myListView = (ListView)findViewById(R.id.BTList);
  TxtArduinoRec = (TextView)findViewById(R.id.TxtArduinoReturn);
  BtnOnOff = (Button)findViewById(R.id.BtnOn);
  BtnSecondsShow = (Button)findViewById(R.id.BtnShowS);
  BtnSyncHour = (Button)findViewById(R.id.BtnSync);
  BtnChangeFormat = (Button)findViewById(R.id.BtnFormat);
  BtnDisconnect = (Button)findViewById(R.id.BtnDisconnect);
  SkBright = (SeekBar)findViewById(R.id.SKBrillo);
  TxtBrigthValue = (TextView)findViewById(R.id.txtSeekValue);

  myBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

  if (myBluetoothAdapter == null) {
   msg("El bluetooth no es soportado por este dispositivo");
  }
  else {
   msg("Esperando la conexion");
  }
  SkBright.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener(){

   @Override
   public void onProgressChanged(SeekBar seekBar, int progress,
            boolean fromUser) {
    TxtBrigthValue.setText("Brillo: " + String.valueOf(progress));
    if (isBtConnected)
    {
     String result = String.format(">SETB%1$02d", progress);
     mConnectedThread.write(result);
    }
   }

   @Override
   public void onStartTrackingTouch(SeekBar seekBar) {
   }

   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {

   }
  });
  BtnConnect.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
    if (!myBluetoothAdapter.isEnabled()) {
     Intent turnOnIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
     startActivityForResult(turnOnIntent, 1);
    }else{
     showBTDialog();
    }
   }
  });
  BtnOnOff.setOnClickListener(new View.OnClickListener()
  {
   @Override
   public void onClick(View view){
    if (isBtConnected)
    {
     mConnectedThread.write(">SCRA");
    }
   }
  });

  BtnSecondsShow.setOnClickListener(new View.OnClickListener()
  {
   @Override
   public void onClick(View view){
    if (isBtConnected)
    {
     mConnectedThread.write(">DISS");
    }
   }
  });

  BtnSyncHour.setOnClickListener(new View.OnClickListener()
  {
   @Override
   public void onClick(View view){
    if (isBtConnected)
    {
     mConnectedThread.write(BuildHour(true));
    }
   }
  });

  BtnChangeFormat.setOnClickListener(new View.OnClickListener()
  {
   @Override
   public void onClick(View view){
    if (isBtConnected)
    {
     mConnectedThread.write(">SETF");
    }
   }
  });
  BtnDisconnect.setOnClickListener(new View.OnClickListener()
  {
   @Override
   public void onClick(View view){
    if (isBtConnected)
    {
     Disconnect();
    }
   }
  });
  //encargado de procesar los mensajes.
  h = new Handler() {
   public void handleMessage(android.os.Message msg) {
    switch (msg.what)
    {
     case RECEIVE_MESSAGE:
      byte[] readBuf = (byte[]) msg.obj;
      String strIncom = new String(readBuf, 0, msg.arg1);
      sb.append(strIncom);
      int endOfLineIndex = sb.indexOf("\r\n");
      if (endOfLineIndex > 0)
      {
       String sbprint = sb.substring(0, endOfLineIndex);
       sb.delete(0, sb.length());
       TxtArduinoRec.setText("Data from Arduino: " + sbprint);
      }
     break;
    }
   };
  };

 }
 private void msg(String s)
 {
  Toast.makeText(getApplicationContext(),s,Toast.LENGTH_LONG).show();
 }
 private String BuildHour(boolean ForArduino){
  //>SETH2016122119001004
  //>SETHAAAAMMDDHHMMSSWW
  Calendar c = Calendar.getInstance();
  int DiaSemana = c.get(Calendar.DAY_OF_WEEK);
  int Anio = c.get(Calendar.YEAR);
  int Mes = c.get(Calendar.MONTH);
  int Dia = c.get(Calendar.DAY_OF_MONTH);
  int Horas = c.get(Calendar.HOUR_OF_DAY);
  int Minutos = c.get(Calendar.MINUTE);
  int Segundos = c.get(Calendar.SECOND);
  String Fecha = null;
  if (ForArduino == true){
   Fecha = String.format(">SETH%1$04d%2$02d%3$02d%4$02d%5$02d%6$02d%7$02d",
     Anio, Mes + 1, Dia, Horas, Minutos,Segundos, DiaSema);
   msg(Fecha);
  }else {
   Fecha = String.format("%1$04d/%2$02d/%3$02d - %4$02d:%5$02d:%6$02d",
     Anio, Mes + 1, Dia, Horas, Minutos,Segundos);
  }
  return Fecha;
 }

 private void Disconnect()
 {
  if (btSocket!=null)
  {
   try
   {
    btSocket.close();
    isBtConnected = false;
   }
   catch (IOException e)
   { msg("Error");}
  }
 }

 private AdapterView.OnItemClickListener myListClickListener = new AdapterView.OnItemClickListener()
 {
  public void onItemClick (AdapterView<?> av, View v, int arg2, long arg3) {
   if (myBluetoothAdapter.isEnabled()) {
    String info = ((TextView) v).getText().toString();
    address = info.substring(info.length() - 17);
    msg(info);

   } else {
    msg("El Bluetooth debe de estar encendido.");
   }
  }
 };


 public void showBTDialog() {
  final AlertDialog.Builder popDialog = new AlertDialog.Builder(this);
  final LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE);
  final View Viewlayout = inflater.inflate(paired_devices_list, (ViewGroup) findViewById(R.id.bt_list));

  popDialog.setTitle("Dispositivos Bluetooth:");
  popDialog.setView(Viewlayout);

  myListView = (ListView) Viewlayout.findViewById(R.id.BTList);
  BTArrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
  myListView.setAdapter(BTArrayAdapter);
  pairedDevices = myBluetoothAdapter.getBondedDevices();

  for(BluetoothDevice device : pairedDevices)
   BTArrayAdapter.add(device.getName()+ "\n" + device.getAddress());

  myListView.setOnItemClickListener(myListClickListener);
  // Button OK
  popDialog.setPositiveButton("Conectar",
    new DialogInterface.OnClickListener() {
     public void onClick(DialogInterface dialog, int which) {
      if (address != null)
      {
       new ConnectBT().execute();
      }
      dialog.dismiss();
     }
    });
  popDialog.create();
  popDialog.show();

 }
 private class ConnectedThread extends Thread {
  private final InputStream mmInStream;
  private final OutputStream mmOutStream;

  public ConnectedThread(BluetoothSocket socket) {
   InputStream tmpIn = null;
   OutputStream tmpOut = null;
   try {
    tmpIn = socket.getInputStream();
    tmpOut = socket.getOutputStream();
   } catch (IOException e) { }

   mmInStream = tmpIn;
   mmOutStream = tmpOut;
  }

  public void run() {
   byte[] buffer = new byte[256];
   int bytes;
   while (true) {
    try {
     bytes = mmInStream.read(buffer);
     h.obtainMessage(RECEIVE_MESSAGE, bytes, -1, buffer).sendToTarget();
    } catch (IOException e) {
     break;
    }
   }
  }

  public void write(String message) {
   byte[] msgBuffer = message.getBytes();
   try {
    mmOutStream.write(msgBuffer);
   } catch (IOException e) {
    msg("Error al enviar: " + e.getMessage());

   }
  }
 }

 private class ConnectBT extends AsyncTask<Void, Void, Void>
 {
  private boolean ConnectSuccess = true;

  @Override
  protected void onPreExecute()
  {
   progress = ProgressDialog.show(MainActivity.this, "Conectando ("+ address +")...", "Espera...!!!");
  }

  @Override
  protected Void doInBackground(Void... devices)
  {
   try
   {
    if (btSocket == null || !isBtConnected)
    {
     myBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
     BluetoothAdapter.getDefaultAdapter().cancelDiscovery();
     BluetoothDevice dispositivo = myBluetoothAdapter.getRemoteDevice(address);
     btSocket = createBluetoothSocket(dispositivo);
     btSocket.connect();
    }
   }
   catch (IOException e)
   {
    ConnectSuccess = false;
   }
   return null;
  }

  private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException {
   if(Build.VERSION.SDK_INT >= 10){
    try {
     final Method  m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", new Class[] { UUID.class });
     return (BluetoothSocket) m.invoke(device, myUUID);
    } catch (Exception e) {
     msg("No se pudo crear la conexion");
    }
   }
   return  device.createRfcommSocketToServiceRecord(myUUID);
  }

  @Override
  protected void onPostExecute(Void result)
  {
   super.onPostExecute(result);

   if (!ConnectSuccess)
   {
    msg("La conexion falló, intentelo de nuevo");
    isBtConnected = false;
   }
   else
   {
    msg("Conectado.");
    isBtConnected = true;
    mConnectedThread = new ConnectedThread(btSocket);
    mConnectedThread.start();
   }
   progress.dismiss();
  }
 }
}

Ahora con el código actualizado vamos a ver que hace cada parte del el.

Clock view está compuesto por tres clases, la primera de ellas es la que se encarga de mostrar todos los componente de la interfaz principal (crear lo eventos para los botones, inflar las cosas necesarias, etc), la segunda es ConnectedThread.

La clase ConnectedThread, es la que se va a encarga de enviar y recibir los datos de y hacia la aplicación.
En ella hay dos métodos: run() y write(). En el método run es donde vamos a leer todos los datos que vienen desde el arduino. Lo pasaremos cómo mensaje y despues el manejado h, se encargará de re-direccionar el contenido y mostrarlo en un textview.
El método write(), servirá para enviar datos hacia el arduino, está es la función que se debe de usar para enviar los comandos, por ejemplo, para enviar el comando para mostrar los segundos, debemos de llamar a la función con el parámetro ">DISS" write(">DISS").
La tercera clase es la que se encarga de crear la conexion y mostrar un dialogo de progreso/espera, cuando la conexión se realiza, deja un socket bluetooth listo para usarse.

Cómo verás, la aplicación es realmente sencilla, pero aun queda mucho por agregar a ambas partes de Clock View, por ahora el código fuente completo lo reservo para cuando ya tenga todas la funciones listas, pero con lo anterior, ya puedes crear tu propia versión.

La parte del código para arduino se actualizará, pero ese en cuanto lo cambie, también actualizaré el vinculo en dropbox.

Por ahora es todo, pero a esto aun le resta bastante para que sea un reloj funcional (más funcional).
Los leo luego.

Vamos a programar #5 - Calculadora de resistencias para android.


Terminando lo anterior

El dia de hoy vamos a terminar de hacer una aplicacion para android que nos ayudara a obtener el valor de una resistencia basado en los colores que está posea.
En el post anterior vimos como implementar la interfaz.

Asi que ahora nos vamos a ocupar del código que hace funcionar las cosas.

Terminando la interfaz.

Para los datos que van a tener los controles Spinner, decidí cargarlos desde los recursos, Para eso en el panel "Project" buscaremos la siguiente ruta: app>res>values>strings.xml lo abriremos agregaremos entre las etiquetas <resources></resources> el siguiente codigo:

<resources>
    <string name="app_name">Calculador de resistencias</string>
    <string-array name="Colores1">
        <item>Negro</item>
        <item>cafe</item>
        <item>Rojo</item>
        <item>Naranja</item>
        <item>Amarillo</item>
        <item>Verde</item>
        <item>Azul</item>
        <item>Violeta</item>
        <item>Gris</item>
        <item>Blanco</item>
    </string-array>
    <string-array name="Colores2">
        <item>Dorado</item>
        <item>Plata</item>
    </string-array>
</resources>


Ahora solo queda implementar el codigo en java. lo puedes copiar asi como está en app>java
com.mdev.claculadoraderesistencia>mainactivity.java, se incluyen los comentarios pertinentes para poder implementarlo

package com.mdev.rcalc.calculadoraderesistencias;

import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import static java.lang.Math.pow;

public class MainActivity extends AppCompatActivity {
 //Spinners
 Spinner SpnC1,SpnC2,SpnC3,SpnC4;
 //Boton
 Button BtnCallIt;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  BtnCallIt = (Button)findViewById(R.id.BtnCalculateIt);
  //En esta seccion llenamos los Spinners con los datos de los recursos.
  //Asociamos los controles del layput a nustras variables de control (spinner)
  SpnC1 = (Spinner)findViewById(R.id.SpnColor1);
  SpnC2 = (Spinner)findViewById(R.id.SpnColor2);
  SpnC3 = (Spinner)findViewById(R.id.SpnColor3);
  //Usamos el mismo adaptador para los primeros 3 ya que contienen los mismos datos.
  ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
    R.array.Colores1, android.R.layout.simple_spinner_item);
  adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
  //Asignamos el contenido del adaptador a nuestros controles
  SpnC1.setAdapter(adapter);
  SpnC2.setAdapter(adapter);
  SpnC3.setAdapter(adapter);
  //Hacemos lo mismo para el cuarto, pero aqui usamos el recurso "Colores2" que contiene los
  //datos para la cuarta franja, solo en este caso.
  SpnC4 = (Spinner)findViewById(R.id.SpnColor4);
  ArrayAdapter<CharSequence> adapter2 = ArrayAdapter.createFromResource(this,
    R.array.Colores2, android.R.layout.simple_spinner_item);
  adapter2.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
  SpnC4.setAdapter(adapter2);
  //Creamos el handler para el evento click
  BtnCallIt.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view)
   {
   //Usamos la funcion ShowDialog para mostrar el resultado
   ShowDialog("El Resultado es:",BuildNumber(SpnC1.getSelectedItemPosition(),SpnC2.getSelectedItemPosition(),
       SpnC3.getSelectedItemPosition())+" "+GetTolerance(SpnC4.getSelectedItemPosition()));
   }
  });
 }
 //Mostrar dialog de manera sencilla
 private void ShowDialog(String Title, String Caption)
 {
  new AlertDialog.Builder(this)
    .setTitle(Title)
    .setMessage(Caption)
    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
     public void onClick(DialogInterface dialog, int which) {
      // algo
     }
    })
    .setIcon(android.R.drawable.ic_menu_info_details)
    .show();
 }
 //Funcion para obtener la toleranacia de la resistencia
 private String GetTolerance(int Value)
 {
  if (Value == 0)
   return "+5% de tolerancia";
  else
   return "+10% de tolerancia";
 }
 //Funcion para generar el valor de la resistencia basado en sus colores
 private String BuildNumber(int Value1, int Value2, int Value3)
 {
  String Significant;
  //Los manejo en string para solo concatenarlos obtener el valor
  Significant = Integer.toString(Value1) + Integer.toString(Value2);
  //Convertimos el valor a uno numerico y lo multiplicamos a la potencia de 10
  double Resultado = Integer.parseInt(Significant)*pow(10,Value3);
  //Para regresar el valor en KΩ
  if (Resultado/1000 >= 1 && Resultado/1e3 < 1000 )
  {
   return (String.valueOf(Resultado / 1e3) + "KΩ");
  }
  //Para regresar el valor en MΩ
  if (Resultado/1e6 >= 1)
  {
   return (String.valueOf(Resultado / 1e6) + "MΩ");
  }else{
   //Regresamos el valor en Ω
   return (String.valueOf(Resultado)+"Ω");
  }

 }
}



Al implementar la aplicacion y correrla se obtiene lo anterior, como lo dije esto solo es ilustrativo, no es para aprender a prgramar, pero si tienes dudas no dudes en preguntar.
Por sí se ofrece, el proyecto entero lo puedes descargar aquí, lo puedes importar completo para ver como funciona.
Los leo luego

Vamos a programar #4 - Calculadora de resistencias para android

Hace algunos días alguien me envió una petición, en ella me preguntaba de la posibilidad de hacer la misma calculadora para resistencia pero en una versión para android.
El día de hoy vamos a ver precisamente eso: Cómo hacer una calculadora de resistencias para android.

¿Qué necesitamos?

  1. Android estudio (se puede usar eclipse).
  2. Un dispositivo que corra android (opcional ya que Android studio incluye emuladores de dispositivos con android).
  3. Un poco de paciencia.
**Nota: Este no es un tutorial para aprender a programar, para poder realizarlo debes de tener el mínimo de conocimiento en programación y Android studio ya instalado**

Paso 1. Creando el proyecto.

 Crearemos un proyecto nuevo.
Seleccionamos proyecto nuevo.
Luego llenaremos los datos que se nos piden. Por lo general  se pueden rellenar con los nombres que uno quiera, pero conveniencia se debe de usar uno de tal modo que no sea tan largo. en este caso y para mi proyecto le puse así:
Por conveniencia se debe de incluir "com" en esta caso el nombre final de mi proyecto es "com.mdev.calculadoraderesistencias".
La siguiente ventana nos muestra el tipo de dispositivo y la versión de android para la cual queremos crear nuestro proyecto, Para este solo marcaremos la opción de "teléfono y tableta" y en Minimum SDK podemos escoger cualquiera, pero solo para esta aplicación. Entre mas "baja" sea la API, en mayor numero de dispositivos funcionara, pero habrá casos en los que alguna característica no este disponible en la versión que seleccionemos, por lo que tendremos que elegir una mas alta. Para este caso podemos elegir cualquiera, ya que no emplearemos funciones complejas. En mi caso opté por la API 7, porque en el teléfono que tengo actualmente, está instalado Android 2.1.
Ademas hay que resaltar que si la aplicación corre en Android 2.1, correrá en 3.0, 4.4, 5.0, etc. entonces siempre en la medida de lo posible, hay que elegir la versión mas baja, así garantizaremos que funcionara en otros dispositivos con una versión superior, 
En este caso uso el API 7 por mi teléfono.
Después se nos preguntara si queremos agregar una "actividad", para está aplicación usaremos "Empty Activity".

Finalmente nos preguntara por los detalles para la "Activity" que queremos crear, no modifivcaremos nada y daremos clic en el botón "Finish".


Paso 2. La interfaz.

Para editar el archivo XML que controla hay que buscarlo en el proyecto. Para eso iremos al apartdo "Project" que se encuentra en el extremo derecho. Haremos clic y se abrirá un panel, navegaremos: app>res>layout>activity_main.xml. Hacemos doble clic en al archivo xml y se abrira el diseñador mostrandonos la interfaz actual.


Para la interfaz debemos de agregar los siguientes controles:
  1. Textview
  2. Spinner.
  3. Botón


En el modo texto pegamos lo siguiente:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.mdev.calculadoraderesistencias.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Color 1:"
        android:id="@+id/TV1"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />
    <Spinner
        android:id="@+id/SpnColor1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/TV1"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:spinnerMode="dialog" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Color 2"
        android:id="@+id/textView"
        android:layout_below="@+id/SpnColor1"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />

    <Spinner
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/SpnColor2"
        android:spinnerMode="dialog"
        android:layout_below="@+id/textView"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="false"
        android:layout_alignParentStart="true" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Color 3"
        android:id="@+id/textView2"
        android:layout_below="@+id/SpnColor2"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentEnd="false" />

    <Spinner
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/SpnColor3"
        android:spinnerMode="dialog"
        android:layout_below="@+id/textView2"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Color 4"
        android:id="@+id/textView3"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_below="@+id/SpnColor3" />

    <Spinner
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/SpnColor4"
        android:layout_below="@+id/textView3"
        android:spinnerMode="dialog"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Calcular"
        android:id="@+id/BtnCalculateIt"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignRight="@+id/SpnColor4"
        android:layout_alignEnd="@+id/SpnColor4" />

</RelativeLayout>

Tras implementarlo, la aplicacion debera lucir similar a la siguiente:



Por ahora es todo para no hacer tan largo este post, en la siguiente vamos a terminar de implementar toda la interfaz
Los leo luego.