Archivo de la categoría: Android

Análisis interno y de seguridad de la plataforma #CiudApp

26757261694_a0273fdcb6_z

El 30 de mayo de este año (2016) Enrique Alfaro, presidente de Guadalajara, anunció durante la sesión de Campus Night la nueva aplicación de administración y gestión de información relevante para los ciudadanía: CiudApp.

Entre muchas otras cosas, la aplicación permite a los ciudadanos estar informados de las noticias más relevantes de la administración (por eso del trending de los gobiernos abiertos), realizar reportes incluyendo geolocalización y solicitar o proponer servicios al gobierno.

A raíz del lanzamiento de la aplicación ha surgido bastante debate y comentarios en Internet en torno a la misma, incluso leí un artículo muy interesante en donde le hacían un análisis de usabilidad a la aplicación, pienso que está bien ya que en nuestro querido México, en el pasado hemos tenido casos polémicos como el de la app de los 115 millones de pesos y es natural que la gente que sabe del tema (desarrolladores, ux designers, community managers, etc.) emitan su opinión acerca de un producto tecnológico “generado” por el gobierno.

Regresando a lo anterior, después de leer el análisis heurístico de usabilidad de la aplicación me pregunte a mí mismo ¿Quién está detrás del desarrollo de la aplicación?, ¿Y si le hacemos un análisis interno a la app? Podemos hacer algunas pruebas de seguridad también, vamos a “destripar” la aplicación para entender como está construida y ver cómo fue desarrollada, todo esto con la premisa de que no vamos a romper ni a explotar / vulnerar / hackear nada 🙂 y pues manos a la obra.

Instalando la aplicación en genymotion

Lo primero que vamos a hacer es descargar el apk de la aplicación (Si, el análisis lo vamos a hacer sobre un dispositivo con Android), el link de la app CiudApp en la playstore es https://play.google.com/store/apps/details?id=com.radmas.iyc.guadalajara.mex y hay miles de sitios web que te permiten descargar el apk utilizando la url de la tienda, solo busquen algún en google y descarguen el archivo, cuando hayan terminado tendrán un archivo llamado más o menos así Ciudapp_Guadalajara_v3.0.146_apkpure.com.apk.

Después de eso vamos a instalar la aplicación en genymotion, si no saben lo que es pueden investigar y descargarlo de acá https://www.genymotion.com/ y probablemente escriba un tutorial en el futuro acerca de cómo instalarlo y configurarlo.

android

Ejecutamos nuestro dispositivo virtual de con Android y vamos a proceder a instalar el apk de la aplicacion mediante adb con el comando:

adb install Ciudapp_Guadalajara_v3.0.146_apkpure.com.apk

adb

Una vez termine el proceso de instalación tendremos la app en nuestro dispositivo virtual.

installed

Bueno, ya tenemos la aplicación preparada, vamos a dejar esto por un momento y vamos a regresar a la carpeta en donde descargamos nuestra apk, ahora vamos a utilizar 3 herramientas, apktool, dex2jar y Jd (Jar Decompiler) para entender un poco más como está construida la app.

Obteniendo los assets de la aplicación

Vamos a utilizar apktool para de compilar la app y leer así algunos archivos importantes como el AndroidManifest.xml, strings.xml y cualquier otro archivo que nos pueda decir algo acerca de la app o sus desarrolladores.

apktool.bat d "C:\Users\Alevskey\Documents\Pentest\mobile\ciudapp\Ciudapp Guadalajara_v3.0.146_apkpure.com.apk"

d

apktool_d_folder

Entre los permisos que nos solicita la aplicación tenemos cosas típicas como acceso a la cámara, acceso al GPS, escribir y leer en almacenamiento externo, acceso a internet, etc., permisos que uno esperaría de una aplicación como esta.

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.radmas.iyc.guadalajara.mex" platformBuildVersionCode="23" platformBuildVersionName="6.0-2166767">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.CAMERA"/>
    <permission android:name="com.radmas.iyc.guadalajara.mex.permission.MAPS_RECEIVE" android:protectionLevel="signature"/>
    <uses-permission android:name="com.radmas.iyc.guadalajara.mex.permission.MAPS_RECEIVE"/>
    <uses-feature android:glEsVersion="0x20000" android:required="true"/>
    <permission android:name="com.radmas.iyc.guadalajara.mex.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
    <uses-permission android:name="com.radmas.iyc.guadalajara.mex.permission.C2D_MESSAGE"/>
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="com.radmas.iyc.guadalajara.mex.permission.GPSTRACKER"/> //Show custom dialgo above Statusbar
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW"/>

Lo que me llamo la atención desde un inicio, y una de las razones por las que quería hacer este ejercicio, fue encontrar que empresa fue la desarrolladora de la app, revisando más a fondo el archivo vemos que muchos de los packages empiezan con com.radmas.iyc.guadalajara.mex, haciendo una búsqueda en google de radmas.com encontramos lo que parece ser una agencia española (pueden revisar su información en who.is) de desarrollo de software especializada en diseño, marketing, creación de aplicaciones móviles y páginas web.

radmas

Si son desarrolladores de Android pueden identificar rápidamente otras cosas interesantes como los activities de la aplicación, uno que otro broadcast receiver, algunos servicios, intents, public api keys, etc.

También me llamo la atención que la aplicación tiene internacionalización, aunque la app parece un poco genérica, está bien hecha en ese aspecto

idiomas

Con la utilidad File Locator Lite también encontramos algunos archivos interesantes como:

app.json

{
	"server_url":"https://api.mejoratuciudad.org",
	"base_uri":"",
	"jurisdiction_id":"mx.guadalajara",
	"api_key":"12",
	"name":"Ciudapp Guadalajara",
	"promo_text": "Echa un vistazo a 'Ciudapp Guadalajara', la mejor aplicación móvil de atención ciudadana https://play.google.com/store/apps/details?id=com.radmas.iyc.guadalajara.mex",
  	"promo_web": "https://play.google.com/store/apps/details?id=com.radmas.iyc.guadalajara.mex",
  	"email": "[email protected]",
  	"languages":    [{"key":"Español","value":"es"},
	{"key":"Inglés","value":"en"}],
  "share_url": "http://intranet.mejoratuciudad.org/mail/request/"
}

arrays.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="enviroments">
        <item>http://api-canary.mejoratuciudad.org</item>
        <item>http://api-developer.mejoratuciudad.org</item>
        <item>http://api.mejoratuciudad.org</item>
        <item>http://open010.valvaro.lan</item>
        <item>http://open010.vivan.lan</item>
        <item>http://open010.vnacho.lan</item>
        <item>http://open010.vfernando.lan</item>
        <item>http://open010.vwalter.lan</item>
        <item>http://open010.vemilio.lan</item>
    </string-array>
</resources>

De acuerdo a lo que vemos la app se comunica con varios servicios web hospedados en mejoratuciudad.org, investigando un poco sabemos que la empresa anterior mencionada (radmas.com) también es dueña de este producto, y según sus propias palabras:

Mejora Tu Ciudad (MTC) es una plataforma de comunicación entre los ciudadanos y el ayuntamiento, que se enmarca dentro de las soluciones Smart City y que se basa en los tres pilares fundamentales de Open Government:

Participación, colaboración y transparencia

Al menos con esto estoy un poco más tranquilo sabiendo que el gobierno de Guadalajara contrato una empresa especializada para el desarrollo de la aplicación y no a un par de becarios para programar la app XD.

mejoratuciudad

Del DEX al JAR y del JAR al código fuente

Antes de avanzar al siguiente paso hay algo más que podemos hacer, es sabido que los archivos apk, son en realidad archivos rar, entnces tomamos nuestro Ciudapp Guadalajara_v3.0.146_apkpure.com.apk lo renombramos a Ciudapp Guadalajara_v3.0.146_apkpure.com.rar y lo descomprimimos obteniendo algo como lo siguiente:

dex2jar

El contenido de la carpeta es muy parecido a lo que generamos cuando utilizamos apktool sin embargo si tratan de abrir algún archivo xml aquí, como por ejemplo el AndroidManifest.xml lo unico que obtendrán serán 0s y 1s, pero tenemos algo interesante, el archivo classes.dex.

Podemos utilizar dex2jar con el siguiente comando para generar un archivo jar (Javar Archive)

dex2jar.bat "C:\Users\Alevskey\Documents\Pentest\mobile\ciudapp\Ciudapp Guadalajara_v3.0.146_apkpure.com\classes.dex"

Obtendremos un archivo classes_dex2jar.jar que podemos abrir en el Java decompiler, tan solo abrimos la aplicación y seleccionamos el archivo jar.

jd

Muchos de los paquetes que vemos ahí son librerías de terceros y conforme indagamos más y más podemos ver que la aplicación utiliza librerías de facebook, google, librerías para animaciones, reyclerviews personalizados, analytics, Bitly, crash analytics, etc.

jd2

Para agilizar la búsqueda podemos seleccionar File > Save All Sources y elegimos una carpeta en donde guardaremos el archivo Zip generado que después descomprimimos y analizamos, nuevamente utilizando File Locator Lite

app_decompiled

Algunas búsquedas nos arrojan cosas interesantes, como por ejemplo credenciales en texto plano XD

password

passwords2

username

Con las credenciales obtenidas fue posible acceder a lo que parece un portal administrativo de estacionamientos de una tercer empresa llamada urbiotica en http://services.urbiotica.net/

urbiotica

Podemos ver, al parecer, en tiempo real el status de los cajones de un estacionamiento en algún lugar de España, la pregunta aquí es: ¿Que hacían esas credenciales hardcodeadas en la app?, navegando la aplicación no veo por ningún lado algo que haga referencia a estacionamientos sin embargo haciendo otra búsqueda con la palabra park obtenemos referencias en muchos otros archivos.

park

Sin embargo no pude lanzar ningún activity o servicio relacionado al Parking desde adb shell ya que no estaban registradas en el AndroidManifest.xml.

Opino que esas credenciales son quizás de algún desarrollo anterior, el código fuente fue re utilizado y las credenciales fueron olvidadas ahí y ahora están siendo distribuidas masivamente XD, en fin la pantalla administrativa del sistema de estacionamientos luce de esta forma, tiene sentido que haya otra empresa más involucrada en este proyecto, sobre todo si es está enfocada en tecnologías para Smart cities.

park2

Revisando los archivos de la aplicación

Regresamos a nuestro emulador y ahora vamos a abrir una shell de windows y ejecutamos el comando:

adb shell

El comando anterior nos entregara una consola para poder navegar el dispositivo donde instalamos la app, conociendo un poco sobre Internals y arquitectura general de Android sabemos que la información de una app se almacena en la ruta /data/data/package.de.la.app, en este caso /data/data/com.radmas.iyc.guadalajara.mex/

shared_pref

Aquí encontramos otros archivos que podrían contener más cosas interesantes y que a su vez nos podrían enseñar un poco más cómo funciona la aplicación, comenzamos por ver que hay dentro de la carpeta shared_prefs, si no saben lo que es pueden investigar más al respecto acá https://developer.android.com/training/basics/data-storage/shared-preferences.html pero en resumen es un método de almacenamiento persistente que nos ofrece el sistema operativo, la información es almacenada en archivos XML y es generalmente utilizado para guardar configuraciones de la aplicación.

shared_prefs2

Quizás el archivo más interesante es JURISDICTION_LOADED.xml que contiene:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="mx.guadalajara">mx.guadalajara</string>
</map>

Y del cual vamos a hablar en la siguiente etapa. Después de que se loguen en la app pueden ver que nuevos archivos de preferencias compartidas son creados, estos contienen información relacionada con su cuenta, facebook, etc, etc.

shared_prefs3

En la carpeta /data/data/com.radmas.iyc.guadalajara.mex/databases/ encontramos dos bases de datos sqlite, procedemos a descargarlas para ver qué es lo que contienen usando los siguientes comandos.

adb pull /data/data/com.radmas.iyc.guadalajara.mex/databases/DBImproveYourCity C:\Users\Alevskey\Documents\Pentest\mobile\ciudapp\databases

adb pull /data/data/com.radmas.iyc.guadalajara.mex/databases/DBImproveYourCity-journal C:\Users\Alevskey\Documents\Pentest\mobile\ciudapp\databases

Y después podemos abrirlas utilizando cualquier visor de sqlite como por ejemplo SQLiteBrowser

db2

En la tabla request es donde podemos ver los reportes “cacheados” por la aplicación en nuestro teléfono, podemos observar el id del reporte (service_request_id), el id de la cuenta de la persona que hizo el reporte (account_id), la calle, coordenadas, url de imagenes, descripcion, status, fecha, etc.

db4

db5

db6

En general cumplen con las buenas prácticas de almacenamiento de información, no almacenan datos sensible en lugares inseguros como la SD externa y en su lugar lo hacen en bases de datos SQLite y shared preferences que es donde solo la aplicación tiene privilegios de lectura y escritura.

Entendiendo las comunicaciones

Ahora viene una de las partes más divertidas de este tipo de ejercicios, saber cómo y con quien se comunica la aplicación, que es lo que envía y que es lo que recibe, etc. Pero antes, si están haciendo este ejercicio mientras leen mi artículo se darán cuenta que la app maneja un concepto muy interesante llamado Jurisdicciones o Jurisdiction, después de analizar el código fuente y visualizar en mi mente el flujo de información, jurisdiction es un identificador que le dice a la app que información cargar de acuerdo a una región especifica, por default la app al iniciar hace un request a https://api.mejoratuciudad.org/applications/12.json y obtiene como resultado

{
  "name": "Guadalajara",
  "active": true,
  "default_jurisdiction_id": "mx.guadalajara"
}

Pueden jugar con el numero al final de la url y de esta manera nos damos cuenta que esta aplicación (o al menos el backend y los servicios web) son escalables y han sido implementados en muchas otras ciudades y países (¿Posiblemente otros clientes de radmas?), por ejemplo https://api.mejoratuciudad.org/applications/5.json nos arroja:

{
  "name": "Cuenta conmigo",
  "active": true,
  "default_jurisdiction_id": "org.sevilla"
}

https://api.mejoratuciudad.org/applications/4.json

{
  "name": "Greencities",
  "active": true,
  "default_jurisdiction_id": "eu.greencities.malaga"
}

Revisando rápidamente con una herramienta para automatizar el proceso podemos ver que actualmente existen 16 jurisdicciones diferentes (al menos en ese servidor), podemos encontrar muchísimas ciudades más del estado de Jalisco así como ciudades de otros países, eso nos dice que la aplicación está siendo implementada en muchos más lugares 🙂

burp

El mismo ID de jurisdicción es utilizado para obtener las noticias, reportes y servicios relacionados de una región en específico.

Continuamos, como sabemos la aplicación se comunica con servicios web hospedados con el proveedor https://api.mejoratuciudad.org/, si vamos a esa URL podremos ver una bonita api REST muy bien documentada 🙂

rest

Esto es muy bueno ya que los desarrolladores ya no estamos limitados a solo utilizar la aplicación, ahora podemos crear nuestras propias aplicaciones utilizando la api ya existente :)! hay otros servicios web muy interesantes como:

Como mencionaba anteriormente, tal solo cambien el valor de la jurisdicción por alguno de los otros y obtendrán la información de las otras plataformas. Para obtener un poco mas de información general de la aplicación, ejecutamos fierce sobre mejoratuciudad.org para tratar de obtener una lista de posibles subdominios

fierce -dns mejoratuciudad.org -wordlist /root/tools/SecLists/Discovery/DNS/subdomains-top1mil-110000.txt -threads 10

fierce

La lista completa:

ftp.mejoratuciudad.org
www.mejoratuciudad.org
intranet.mejoratuciudad.org
api.mejoratuciudad.org
m.mejoratuciudad.org
apps.mejoratuciudad.org
blog.mejoratuciudad.org
demo.mejoratuciudad.org
lab.mejoratuciudad.org
doc.mejoratuciudad.org
developer.mejoratuciudad.org
pandora.mejoratuciudad.org
hera.mejoratuciudad.org
WWW.mejoratuciudad.org
landing.mejoratuciudad.org
privacy.mejoratuciudad.org
electra.mejoratuciudad.org
pi-canary.mejoratuciudad.org
api-developer.mejoratuciudad.org

En este punto el servidor me dio ban (probablemente por mis peticiones automatizadas, pero nada que no se solucione con una VPN), y ya no continué haciendo más pruebas, pero no sin antes darme cuenta de que al parecer su servidor no está hardenizado, cualquier petición a su api devolvía un error 403 revelando la versión del servidor web y su sistema operativo.

<html>
    <head>
        <title>403 Forbidden</title>
    </head>
    <body bgcolor="white">
        <center>
            <h1>403 Forbidden</h1>
        </center>
        <hr>
        <center>nginx/1.4.6 (Ubuntu)</center>
    </body>
</html>

Y si le corremos un scan con nmap a los puertos más conocidos obtenemos.

PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.7 (Ubuntu Linux; protocol 2.0)
80/tcp   open  http       nginx 1.4.6 (Ubuntu)
111/tcp  open  rpcbind    2-4 (RPC #100000)
443/tcp  open  ssl/http   nginx 1.4.6 (Ubuntu)
5666/tcp open  tcpwrapped
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Por buenas prácticas de seguridad esa información no debería ser revelada, y es bastante sencillo corregir eso (literalmente es modificar una línea en el archivo de configuración de nginx).

Con la ayuda de Burp Suite es posible analizar los request y eso nos arroja aún más información de cómo se comunica la app, aunque teniendo la documentación de su api a la mano ya no es tan necesario.

Para terminar, que es lo que aprendimos hoy

  • En el desarrollo de este proyecto estuvieron involucradas al menos 3 partes: El gobierno de Guadalajara, la empresa Radmas y Urbiotica (dejando de lado el posible caso de que en realidad ellos no tienen nada que ver y solo las credenciales de uno de sus clientes fueron leakeadas en la app, eso sería muy mala suerte)
  • Al menos la app de Android (que fue la que analice) fue desarrollada de forma nativa
  • Hacen buen uso de las funcionalidades para almacenar información, guardan la información sensible utilizando shared preference y bases de datos sqlite
  • El almacenamiento de imágenes lo hacen en la nube de aws
  • La app tiene credenciales hardcodeadas (posiblemente de un proyecto anterior del cual re utilizaron el código) y que al día de hoy funcionan
  • La app utiliza el servicio de mejoratuciudad.org que al parecer es un producto de radmas.com y podemos decir que es un software / plataforma / tecnologia especializada para hacer implementaciones en smart cities
  • Los servidores de mejoratuciudad.org no están hardenizados
  • Los servidores de mejoratuciudad.org cuentan con algún tipo de IDS/IPS que bloquea a usuarios después de X número de peticiones automatizadas.
  • La plataforma no implementa esos controles puesto que solo utilice una VPN para cambiar mi IP y ya pude seguir interactuando con la app sin problemas.
  • Debido al concepto de Jurisdicciones sabemos que la app está siendo implementada en muchas otras ciudades de México y el mundo (16 clientes para ser exactos según los servidores de mejoratuciudad.org)
  • Los servicios web no parecen vulnerables a algún tipo de ataque popular como SQL injection, xss, RCE, etc. Muy bien por Radmas 🙂 )
  • El código fuente de la aplicacion no fue ofuscado

Happy hacking … salu2

Tutorial Material Design en Android #5

Crear un RecyclerView para Android

Hola lectores, ha pasado un tiempo desde que publique la última entrega del tutorial de Material Design en Android en donde aprendimos a crear un Navigation Drawer, continuamos con los tutoriales y en esta ocasión les enseñare a crear un RecyclerView.

Pero antes, ¿Que es un RecyclerView?, seguramente muchos lo abran visto ya en acción, este tipo de componente es cada vez más y más popular y ha venido remplazando a las antiguas ListViews, se trata de un componente que nos permite crear listas de contenedores que a su vez pueden tener dentro otros componentes (TextView, EditText, ImageView, etc.). El RecyclerView tiene la particularidad de haber sido diseñado con la eficiencia en mente, como su nombre lo indica los objetos que son visible en la pantalla son los que se dibujan en pantalla y una vez que desaparecen (el usuario hace scroll) estos remplazan su contenido (se reciclan) para mostrar otro tipo de información.

Imaginemos una lista de 100 contactos, en la pantalla de nuestro teléfono solo podemos renderizar 5 contactos a la vez, en lugar de renderizar los 100 desde un inicio solo vamos remplazando la información en las filas de nuestro RecyclerView conforme lo vamos necesitando, cabe decir que este es un proceso automático.

list_mail

Pero el RecyclerView es mucho más que eso, a diferencia de su predecesor con este nuevo componente podemos crear listas con diferentes tipos de layouts

20150415193645985

images

EAF-MD3

Retomamos nuestro proyecto, como les comentaba en la publicación pasada he subido el código realizado hasta el momento en el siguiente repositorio https://github.com/Alevsk/Material-design-en-Android.

Lo primero que vamos a hacer sera agregar la dependencia a nuestro proyecto en el archivo de gradle, abrimos el archivo build.gradle que se encuentra en MaterialdesignApp (proyecto)> app > build.gradle y en la parte de abajo en el apartado de dependencias agregaremos
compile ‘com.android.support:recyclerview-v7:+’ quedando de la siguiente forma

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.2.0'
    compile 'com.android.support:recyclerview-v7:22.2.0'
}

Sincronizamos gradle para descargar las dependencias (o también podemos dar re build al proyecto) y una vez tenemos la dependencia vamos a comenzar a utilizar el componente, vamos a nuestro archivo fragment_navigation_drawer.xml (aquí vamos a crear el menú), si no recuerdan donde está el archivo se encuentra en MaterialdesignApp > app > src > main > res > layout > fragment_navigation_drawer.xml

Por el momento tenemos esto, un simple texto ¿bastante simple cierto?

Capture

drawer_navigation

Al final de nuestro tutorial vamos aprender a crear algo como esto :), pues manos a la obra, o mejor dicho al codigo XD

16170261501_9b7ce86ca7_b

En nuestro archivo XML (fragment_navigation_drawer.xml) vamos a crear nuestro RecyclerView con el siguiente código, si se fijan elimine el TextView que teníamos porque ya no lo necesitamos.

<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:background="@color/grayPanel"
    tools:context="com.alevsk.materialdesignapp.fragments.NavigationDrawerFragment">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/drawerList"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </android.support.v7.widget.RecyclerView>

</RelativeLayout>

Una vez añadido el componente en nuestra vista xml vamos a nuestro controller NavigationDrawerFragment.java (MaterialdesignApp > app > src > main > java > com.alevsk.materialdesignapp > fragments > NavigationDrawerFragment.java) y definimos una nueva variable tipo RecyclerView como propiedad de la clase

public class NavigationDrawerFragment extends Fragment {

    private RecyclerView mRecyclerView;
    private ActionBarDrawerToggle mDrawerToggle;
    private DrawerLayout mDrawerLayout;
    private Toolbar mToolbar;
    ....
    ..

Una vez creado nuestro RecyclerView vamos a agregar la referencia hacia su objeto xml, para eso en el método onCreateView, vamos a modificar un poco el código, actualmente tenemos algo como esto

Capture

Lo remplazamos por esto, sencillo, a estas alturas del tutorial ya deberían saber que estamos obteniendo el elemento mediante el id (layout.findViewById(R.id.drawerList)) que definimos, estamos haciendo un cast (RecyclerView) y lo estamos asignando a la variable mRecyclerView, por ultimo retornamos el layout (View) que es con el que trabajara la aplicacion.

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View layout = inflater.inflate(R.layout.fragment_navigation_drawer, container, false);
        mRecyclerView = (RecyclerView) layout.findViewById(R.id.drawerList);
        return layout;
    }

Pero no tan rápido, antes de correr nuestra aplicación nos falta crear un par de componentes más, un Adapter (un manejador lógico para los elementos de nuestra lista), ViewHolders y un modelo (representación de un elemento en la lista)

Pero antes, para tener todo más organizado, vamos a crear dos nuevos Package en nuestro proyecto, uno llamado adapters y otro models

Capture

Vamos a comenzar a pensar en MVC (Model View Controller), si bien no existe un estándar totalmente definido para utilizar MVC en Android podemos tomar el concepto y definir lo siguiente:

  • Model: Qué vamos a renderizar en la aplicación (objetos principales utilizados en los controladores)
  • View: Como lo vamos a renderizar (xml)
  • Controller: Eventos del ciclo de vida de las pantallas, manejar las entradas de usuario, definición de componentes de Android como el RecyclerView, etc.

Creando un Modelo en Android

Teniendo lo anterior como premisa al momento de definir nuestro menú lo que necesitamos es una imagen que sirva como icono y un texto que sirva como título, procedemos a crear nuestra clase Menu.java en el Package models que definimos previamente

Quicktip: en nuestra clase primero definimos las propiedades de nuestro objeto (int icon, String title) y después para generar nuestros gets y sets usamos el shortcut “alt + insert” lo que desplegara un menú en donde elegimos getters and settters y marcamos todas las propiedades (variables) de nuestra clase.

Capture

public class Menu {
    private int icon;
    private String title;

    public int getIcon() {
        return icon;
    }

    public void setIcon(int icon) {
        this.icon = icon;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

Creando un Adapter personalizado para el RecyclerView

Ahora en el Package adapters creamos una clase llamada MenuAdapter que extenderá o heredara sus metodos de RecyclerView.Adapter, el código inicialmente queda así.

package com.alevsk.materialdesignapp.adapters;

import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;

/**
 * Created by Alevskey on 23/01/2016.
 */
public class MenuAdapter extends RecyclerView.Adapter {
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

    }

    @Override
    public int getItemCount() {
        return 0;
    }
}

Creando un ViewHolder personalizado

Ademas de eso también vamos a crear un ViewHolder, podemos definir la clase como una subclase de nuestro MenuAdapter, como siempre aquí tienen más documentación respecto a lo que vamos a utilizar http://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html

Quedando el codigo final de la siguiente forma

Capture

Continuamos, Si leyeron la documentación acerca del comportamiento de los Adapter sabrán que por default manejan el tipo de objeto ViewHolder, pero nosotros acabamos de definir uno llamado MenuViewHolder que será el encargado de “contener” elementos como TextView, ImageView y todo lo que necesitemos para personalizar nuestro elemento de menú, sabiendo eso necesitamos realizar algunos cambios en nuestra clase, para empezar en la definición de nuestra clase vamos a definir (valga la redundancia) explícitamente que queremos utilizar MenuViewHolder

public class MenuAdapter extends RecyclerView.Adapter<MenuAdapter.MenuViewHolder> {
...
..
.

Ahora tenemos un montón de errores en la clase y esto es porque tenemos que modificar el tipo de objeto que retorna la función onCreateViewHolder de RecyclerView.ViewHolder a MenuViewHolder, también deberemos definir otra función llamada OnBindViewHolder, una vez realizados los cambios tenemos lo siguiente.

public class MenuAdapter extends RecyclerView.Adapter<MenuAdapter.MenuViewHolder> {
    @Override
    public MenuViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(MenuViewHolder holder, int position) {

    }

    @Override
    public int getItemCount() {
        return 0;
    }

    class MenuViewHolder extends RecyclerView.ViewHolder {

        public MenuViewHolder(View itemView) {
            super(itemView);
        }
    }
}

Ahora vamos a crear la vista xml de nuestro item de Menu, en la carpeta layout creamos un archivo llamado viewholder_menu.xml que contendrá dos cosas, un ImageView y un TextView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/listIcon"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:padding="8dp"
        android:layout_gravity="center_vertical"
        android:src="@drawable/dummy" />

    <TextView
        android:id="@+id/listText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:layout_gravity="center_vertical"
        android:text="@string/dummy_text" />

</LinearLayout>

La imagen @drawable/dummy es una imagen de ejemplo que yo utilice, ustedes pueden usar la que más les guste.

Regresamos a nuestro MenuAdapter.java y vamos a comenzar a utilizar los métodos que creamos anteriormente, primero vamos a crear un constructor para nuestro adapter, el cual recibirá un Context que nos servirá para hacer el binding de los elementos xml en nuestro ViewHolder, y una lista (de objetos Menu) que será la información con la cual llenaremos el RecyclerView después de eso vamos a realizar ese binding en el método onCreateView, tambien modificamos la función getItemCount para retornar el tamaño actual de la lista.

    private LayoutInflater mInflater;
    private List<Menu> data = Collections.emptyList();

    public MenuAdapter(Context context, List<Menu> data) {
        mInflater = LayoutInflater.from(context);
        this.data = data;
    }

    @Override
    public MenuViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = mInflater.inflate(R.layout.viewholder_menu, parent, false);
        MenuViewHolder holder = new MenuViewHolder(view);
        return holder;
    }

    @Override
    public int getItemCount() {
        return data.size();
    }

Ahora en la subclase MenuViewHolder vamos a definir el ImageView y TextView

    class MenuViewHolder extends RecyclerView.ViewHolder {
        ImageView icon;
        TextView title;
        public MenuViewHolder(View itemView) {
            super(itemView);
            title = (TextView) itemView.findViewById(R.id.listText);
            icon = (ImageView) itemView.findViewById(R.id.listIcon);
        }
    }

Ahora en el método onBindViewHolder es donde se realiza la acción de asignar los valores a las variables del MenuHolder (imágenes, textos, etc).

    public void onBindViewHolder(MenuViewHolder holder, int position) {
        holder.title.setText(data.get(position).getTitle());
        holder.icon.setImageResource(data.get(position).getIcon());
    }

Lo que hace nuestro método es recibir un holder, y una posición int, entonces con esa información de nuestra Lista de objetos Menu extraemos el titulo y el id de recurso de imagen que contiene el objeto Menu en la posición indicada de la lista, por ultimo hacemos set de esa información al TextView e ImageView del holder.

Venga ya falta poco, casi terminamos!!!

Me di cuenta que aún no hemos definido el tema de la app, lo he estado pensando y me gustaría enfocarla a un pequeño catálogo de anime que consuma la api opensource de hummingbird, desarrollando un catálogo les poder enseñar a utilizar muchos más componentes que el ecosistema de Android nos ofrece, así como a consumir Apis e interactuar con las mismas, algo muy común en la industria de desarrollo de aplicaciones hoy en día.

Teniendo claro lo anterior las opciones de nuestro menú serán:

  • Buscar (buscador de anime)
  • Lo que estoy viendo (anime que el usuario ve actualmente)
  • Para más tarde (anime que el usuario tiene en la categoría de plan-to-watch)
  • Completado (series que el usuario ha terminado de ver)
  • Mi cuenta (información del usuario y anime favorito)

De ahí hay 3 opciones que solo pueden ser utilizados una vez el usuario haya sido autenticado en nuestra aplicación, pero eso lo dejaremos para más tarde, por ahora nos centraremos en desarrollar el menú.

Primero vamos a nuestro archivo strings.xml que se encuentra en MaterialdesignApp > app > src > main > res > values > strings.xml y ahi vamos a crear una lista de items que contendrá los elementos del menú

    <string-array name="menu_list">
        <item>Buscar</item>
        <item>Lo que estoy viendo</item>
        <item>Para mas tarde</item>
        <item>Mi cuenta</item>
    </string-array>

Buscamos en Internet algún set de iconos gratuitos y los incluimos a los recursos de nuestro proyecto en la carpeta res > drawable pues los necesitamos para el menu

    <array name="menu_icons">
        <item>@drawable/ic_search</item>
        <item>@drawable/ic_eye</item>
        <item>@drawable/ic_ticket</item>
        <item>@drawable/ic_account</item>
    </array>

Cargando los datos al RecyclerView

En nuestro archivo NavigationDrawerFragment.java vamos a crear una nueva función llamada getData(), esta función será la encargada de leer la información estática que definimos en nuestro archivo strings.xml

    public List<Menu> getData() {
        List<Menu> menu = new ArrayList<Menu>();
        TypedArray icons = getResources().obtainTypedArray(R.array.menu_icons);
        String[] labels = getResources().getStringArray(R.array.menu_list);

        for(int i = 0; i < labels.length; i++) {
            Menu item = new Menu();
            item.setTitle(labels[i]);
            item.setIcon(icons.getResourceId(i,-1));
            menu.add(item);
        }

        return menu;
    }

Lo que estamos haciendo en nuestra función es leer los títulos e imágenes (ids de recurso) y con ello estamos instanciando objetos Menu que a su vez agregamos a una lista que será devuelta por el método.

Finalmente en nuestra función onCreateView vamos a crear una nueva instancia de nuestro Adapter personalizado y se lo vamos a pasar al RecyclerView, también definiremos un objeto llamado LayoutManager para nuestro RecyclerView (asegúrate de definir el Adapter como una propiedad de tu clase)

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View layout = inflater.inflate(R.layout.fragment_navigation_drawer, container, false);
        mRecyclerView = (RecyclerView) layout.findViewById(R.id.drawerList);
        mAdapter = new MenuAdapter(getActivity(),getData());
        mRecyclerView.setAdapter(mAdapter);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

        return layout;
    }

Corremos la aplicación en nuestro emulador o teléfono físico, abrimos el NavigationDrawer y vemos nuestro menú 🙂

recyclerview

Eso es todo por ahora lectores, en el siguiente tutorial aprenderemos a aplicar color a los iconos utilizando filtros y a definir las acciones una vez que el usuario da Tap en un elemento del menú. Recuerden que el código de lo trabajado durante este tutorial lo pueden encontrar en el repositorio https://github.com/Alevsk/Material-design-en-Android
salu2

Tutorial Material Design en Android #4

Crear un Navigation Drawer para Android

Después de una pequeña pausa retomamos la serie de tutoriales de Material Design en Android, en el tutorial anterior aprendimos como agregar iconos (actions) a nuestra Toolbar, así que lo que sigue ahora es aprender cómo implementar el Navigation Drawer.

El Navigation Drawer en Android es un panel que podemos utilizar en nuestra app y que por lo general contiene un menú para movernos o “navegar” por las distintas secciones en la aplicación, la principal ventaja es que puede permanecer oculta la mayor parte del tiempo y desplegarse solo cuando el usuario lo necesite, de esa manera “economizamos” el espacio en la pantalla del teléfono, como en los tutoriales anteriores, si quieren aprender un poco más sobre la teoría siempre pueden consultar la documentación oficial de google Navigation Drawer

patterns_navdrawer_settings1

También vale la pena mencionar que hay básicamente 3 estilos de Navigation Drawer:

  • Navigation Drawer por debajo de la Toolbar oscurenciendola
  • Navigation Drawer por debajo de la Toolbar sin oscurecerla
  • Navigation Drawer por encima de la Toolbar

El estilo que queramos lograr dependerá de cómo escribamos el xml en nuestras views, y un poco de Java también

playstore_styledrawer_bellowdrawer_over_toolbar

Pues manos a la obra, o mejor dicho al código, vamos a nuestro archivo activity_main.xml y deberíamos de tener algo así.

activity_main

Vamos a modificar un poco nuestro código xml y agregaremos un par de elementos llamados DrawerLayout, FrameLayout y NavigationDrawer, viendo la siguiente imagen quedara más claro lo que vamos a hacer en nuestra activity.

drawer_layout_demon

Vamos a crear un elemento android.support.v4.widget.DrawerLayout y vamos a meter todo nuestro código existente dentro de esa etiqueta.

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.alevsk.materialdesignapp.MainActivity">

        <include
            android:id="@+id/app_bar"
            layout="@layout/app_bar" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/app_bar"
            android:text="@string/hello_world" />

    </RelativeLayout>
</android.support.v4.widget.DrawerLayout>

Lo siguiente es crear un Fragment y lo que hago normalmente para tener mi código más organizado es crear un nuevo Package en el proyecto llamado fragments y ahí empiezo a crearlos, para eso en su com.alevsk.materialdesignapp > botón secundario > new > Package y le ponen el nombre que deseen, yo le puse fragments para que sea descriptivo, después de eso procedemos a crear la nueva clase como se muestra en la siguiente imagen.

fragments

Nos aparecerá el siguiente cuadro de dialogo donde tendremos que elegir el nombre del nuevo menú NavigationDrawer (fragmento), le ponemos NavigationDrawerFragment o como ustedes quieran pero que sea descriptivo hacia el hecho de que será el menú de nuestra aplicación, asegúrense de marcar la opción de crear el layout xml y podemos desmarcar las dos opciones de abajo, el código que nos genere Android studio será más limpio y nosotros implementaremos los métodos que necesitamos desde 0, para terminar damos clic en el botón de Finish.

navigationdrawerfragment

Android Studio nos habrá generado dos archivos, NavigationDrawerFragment.java y fragment_navigation_drawer.xml

1

2

En nuestro fragment_navigation_drawer.xml vamos a modificar el elemento principal de Fragment a RelativeLayout quedando el código de la siguiente manera

<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"
    tools:context="com.alevsk.materialdesignapp.fragments.NavigationDrawerFragment">

    <!-- TODO: Update blank fragment layout -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/hello_blank_fragment" />

</RelativeLayout>

Hicimos este cambio ya que más adelante necesitaremos agregar elementos extras como un RecyclerView, a continuación vamos al archivo activity_main.xml y vamos a agregar nuestro NavigationDrawer usando un elemento fragment de la siguiente forma.

    <fragment
        android:id="@+id/fragment_navigation_drawer"
        android:name="com.alevsk.materialdesignapp.fragments.NavigationDrawerFragment"
        android:layout_width="280dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:layout="@layout/fragment_navigation_drawer"
        tools:layout="@layout/fragment_navigation_drawer" />

El codigo final del activity_main.xml es el siguiente:
3

Lo que sigue es crear un objeto NavigationDrawer en nuestro MainActivity.java

NavigationDrawerFragment drawerFragment = (NavigationDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_navigation_drawer);

Si por alguna razón les da un error de compatibilidad asegúrense que en NavigationDrawerFragment.java en la sección de import (hasta arriba) el Fragment que esté utilizando sea android.support.v4.app.Fragment

Ya que estamos en este archivo vamos a crear de una vez un método llamado setUp que va a servir para inicializar los componentes de nuestro NavigationDrawer, también vamos a utilizar un nuevo objeto llamado ActionBarDrawerToogle cuya finalidad es la de manejar nuestro DrawerLayout y Toolbar de una manera más eficiente, así como encargarse de manejar el evento de abrir y cerrar el panel de navegación.

    public void setUp(DrawerLayout drawerLayout, Toolbar toolbar) {
        mDrawerLayout = drawerLayout;
        mToolbar = toolbar;
        mDrawerToggle = new ActionBarDrawerToggle(getActivity(),drawerLayout,toolbar, R.string.open, R.string.close) {
            @Override
            public void onDrawerClosed(View drawerView) {
                super.onDrawerClosed(drawerView);
            }

            @Override
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
            }
        };
        mDrawerLayout.setDrawerListener(mDrawerToggle);
    }

Tip: R.string.open y R.string.close son necesarios para el constructor del ActionBarDrawerToggle ya que son los textos que aparecen al abrir y cerrar el panel, pueden definir esas cadenas en su archivo strings.xml que se encuentra en res/values

<string name="open">Abierto</string>
<string name="close">cerrado</string>

De nuevo, aquí mucho cuidado, asegúrense de que el Toolbar que estén importando sea android.support.v7.widget.Toolbar para efectos de compatibilidad, ya que si utilizan la clase por defecto les va a dar errores en los demás archivos.

Lo que acabamos de hacer en nuestro código es definir un nuevo ActionBarDrawerToggle, vinculamos el DrawerLayout y Toolbar que estamos recibiendo desde nuestro MainActivity y preparamos el listener (manejador de eventos) para cuando el usuario abra y cierre el panel de navegación.

En el MainActivity.java después de instanciar nuestro NavigationDrawer mandamos llamar el método

public class MainActivity extends ActionBarActivity {

    private Toolbar toolbar;

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

        toolbar = (Toolbar) findViewById(R.id.app_bar);
        setSupportActionBar(toolbar);

        NavigationDrawerFragment drawerFragment = (NavigationDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_navigation_drawer);
        drawerFragment.setUp((DrawerLayout)findViewById(R.id.drawer_layout),toolbar);
    }

La clase NavigationDrawerFragment quedo de la siguiente manera
NavigationDrawerFragment

En este punto, si corremos nuestra aplicación y deslizamos haciendo tap desde la izquierda podemos observar como la pantalla comienza a oscurecerse, si ponemos un poco más de atención podemos ver el texto de nuestro fragment encima de la app”Hello Blank Fragment“, ha funcionado! ahora solo nos queda poner un poco de estilo para hacer a aplicación más agradable a la vista.

navigation_drawer_feo

En nuestro archivo colors.xml (se encuentra en app/res/values/colors.xml) podemos definir nuevos colores, por ejemplo yo he definido uno nuevo llamado graypanel

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="primaryColor">#E91E63</color>
    <color name="primaryColorDark">#C2185B</color>
    <color name="textColorPrimary">#FFFFFF</color>
    <color name="grayPanel">#EEEEEE</color>
    <color name="lightGray">#d6d6d6</color>
    <color name="colorAccent">#8BC34A</color>
</resources>

Y despues en nuestro fragment_navigation_drawer.xml definimos el color de fondo a nuestro RelativeLayout con android:background=”@color/grayPanel”

<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:background="@color/grayPanel"
    tools:context="com.alevsk.materialdesignapp.fragments.NavigationDrawerFragment">

    <!-- TODO: Update blank fragment layout -->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/hello_blank_fragment" />

</RelativeLayout>

navigation_drawer_color

¿Un poco mejor cierto? pero todavía le falta bastante, por ejemplo no resulta obvio para el usuario saber que la aplicación tiene un menú lateral, lo que vamos a hacer es agregar el clásico icono de menú de lado izquierdo, para eso utilizaremos un método llamado syncState de nuestro objeto DrawerToggle.

Nos vamos a nuestro archivo NavigationDrawerFragment.java y agregamos el siguiente código después de haber hecho el mDrawerLayout.setDrawerListener(mDrawerToggle);

        mDrawerLayout.post(new Runnable() {
            @Override
            public void run() {
                mDrawerToggle.syncState();
            }
        });

Corremos la app de nuevo y automáticamente nos aparecerá un icono de menú.

menu_icon

Una vez abierto el menú de navegación rara notaran que si hacemos tap donde se supone que esta el botón de menú el panel se cierra, es bastante raro pues la aplicación no está respetando las “capas”, podemos corregir ese comportamiento bajando un poco el NavigationDrawer.

Modificamos un poco nuestro archivo main_activity.xml, si se fijan bien ahora nuestro DrawerLayout esta debajo de nuestra app_bar, ambos elementos estan al mismo nivel y rodeados por un LinearLayout con orientación vertical.

<LinearLayout 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:background="@color/lightGray"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <include
        android:id="@+id/app_bar"
        layout="@layout/app_bar" />

    <android.support.v4.widget.DrawerLayout
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:text="@string/hello_blank_fragment" />
        </RelativeLayout>

        <fragment
            android:id="@+id/fragment_navigation_drawer"
            android:name="com.alevsk.materialdesignapp.fragments.NavigationDrawerFragment"
            android:layout_width="280dp"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            app:layout="@layout/fragment_navigation_drawer"
            tools:layout="@layout/fragment_navigation_drawer" />
    </android.support.v4.widget.DrawerLayout>
</LinearLayout>

drawer_navigation

Y eso es todo por ahora, así es como se programa un NavigationDrawer desde 0 :), en el siguiente tutorial implementaremos las opciones en nuestro menú de navegación, para eso aprenderemos a utilizar los RecyclerViews.

He montado un repositorio en Github con el código de la aplicación que vamos desarrollando durante los tutoriales, si tienen alguna duda pueden consultarlo directamente de ahí o preguntarme en los comentarios https://github.com/Alevsk/Material-design-en-Android

salu2

Platica: ¿Quieres comenzar tu StartUP? Tips y tecnologías que te ayudaran

startup

Esta charla se titula ¿Quieres comenzar tu StartUP? Tips y tecnologías que te ayudaran, y la impartí durante un hackers & founders en la ciudad de Morelia, Michoacán.

No importa si eres desarrollador con experiencia o apenas comienzas en el mundo de la tecnología, esta charla esta enfocada principalmente a personas que tienen la iniciativa de crear su StartUP de tecnología, puede ser una app movil, una plataforma web o un servicio que utilice Internet, pero tienen problemas al momento de empezar o no conocen cuales son las mejores opciones que hay en el mercado.

En esta charla se explican las diferencia entre distintas tecnologías como hosting (servidores compartidos, dedicados, virtuales), domains (com,mx,org,etc), algunos manejadores de contenidos que existen en el mercado (wordpress, joomla, drupal), ecommerce (prestashop, magento, broadleafcommerce) y foros (phpbb, simplemachine).

Tambien hablamos sobre servicios web y los distintos proveedores que nos pueden ayudar a darle soporte a nuestras aplicaciones (Amazon Web Service, Azure, Rackspace).

Terminamos con una comparación sobre desarrollo de aplicaciones nativas y multi plataforma para Android y iOS, sus ventajas y desventajas y sobre todo en cual nos conviene desarrollar o invertir.

Tutorial Material Design en Android #3

Agregando iconos (actions) al ToolBar

Hola lectores programadores :), continuamos con la serie de tutoriales de Material Design en Android, siguiendo con los post anteriores ahora toca agregar algunos elementos a nuestra Toolbar personalizada, pero primero nos encargaremos de un pequeño “bug estético”, actualmente nuestra app luce así y si hacemos tap en el icono superior derecho (3 puntos) veremos que la letra del popupMenu es apenas visible. Corrijamos eso.
popupmenu_bug

Nos vamos a nuestros ya conocidos archivos styles.xml

  • /app/src/main/res/values/styles.xml
  • /app/src/main/res/values-21/styles.xml (api 21)

Y vamos a agregar un nuevo item con nombre popupTheme, quedando de la siguiente forma

values/style.xml

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="AppTheme.Base">
        <!-- Customize your theme here. -->
    </style>

    <style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">@color/primaryColor</item>
        <item name="colorPrimaryDark">@color/primaryColorDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="popupTheme">@style/Base.ThemeOverlay.AppCompat.Dark</item>
    </style>

</resources>

values-21/style.xml (api 21)

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="AppTheme.Base">
        <item name="android:colorPrimary">@color/primaryColor</item>
        <item name="android:colorPrimaryDark">@color/primaryColorDark</item>
        <item name="android:textColorPrimary">@color/textColorPrimary</item>
        <item name="android:colorAccent">@color/colorAccent</item>
        <item name="android:popupTheme">@style/Base.ThemeOverlay.AppCompat.Dark</item>
    </style>

</resources>

Guardamos y corremos la aplicación de nuevo y ahora veremos algo como lo siguiente, ¿Mucho mejor cierto?

popupmenu_dark

Es posible personalizar aún mas este elemento del ToolBar, estos son algunos de los atributos que podemos utilizar

  • popupMenuStyle
  • textColorPrimary
  • popupAnimationStyle>
  • popupBackground

Se puede personalizar prácticamente cualquier aspecto del menú, por ejemplo:

popupmenu_custom2

Les dejo de tarea hacer su propio menú custom 🙂 y bueno ahora que tenemos resuelta esa parte del popupMenu toca agregar los iconos, en nuestro proyecto de Android Studio, en la carpeta res (resources) dentro del apartado de menú encontramos un archivo llamado menu_main.xml (si por algún motivo no está entonces deberán crearlo /res/menu/menu_main.xml).

Como en todos los tutoriales les dejo la documentación oficial sobre los lineamientos sobre los iconos, sus dimensiones y formas aqui y aca.

Por default nuestro menú solo tiene 1 elemento como podemos ver en el siguiente código

<menu 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"
    tools:context="com.alevsk.materialdesignapp.MainActivity">
    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:title="@string/action_settings"
        app:showAsAction="never" />
</menu>

El elemento item tiene algunos atributos, por ejemplo app:showAsAction puede tomar tres valores:

  • never: No mostrara el icono
  • ifRoom: Si hay espacio mostrara el icono
  • always: Siempre mostrara el icono

android:title es el atributo donde definimos el nombre de la acción (si por ejemplo deciden no mostrar un icono o mostrarlo solo si hay espacio, por default el título de la acción será el que definan con este atributo).

Por el momento vamos a definir 3 acciones (conforme avance esto los remplazaremos), quedando el código del archivo menu_main.xml de la siguiente manera.

<menu 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"
    tools:context="com.alevsk.materialdesignapp.MainActivity">
    <item
        android:id="@+id/action_search"
        android:icon="@drawable/abc_ic_search_api_mtrl_alpha"
        android:orderInCategory="1"
        android:title="Busqueda"
        app:showAsAction="always" />
    <item
        android:id="@+id/action_copy"
        android:icon="@drawable/abc_ic_menu_copy_mtrl_am_alpha"
        android:orderInCategory="2"
        android:title="Copiar"
        app:showAsAction="always" />

    <item
        android:id="@+id/action_selectall"
        android:icon="@drawable/abc_ic_menu_selectall_mtrl_alpha"
        android:orderInCategory="3"
        android:title="Seleccionar todo"
        app:showAsAction="always" />
</menu>

Corremos la app y veremos algo como esto

toolbar_icons

¿Los iconos no son muy visibles verdad?, podemos arreglar esto rápidamente, el tema por default de nuestra aplicación es texto oscuro sobre fondos claros, esa es la razón por la que los iconos tienen un color oscuro, sin embargo podemos modificar nuestro archivo app_bar.xml e indicar que queremos que esa parte especifica de la app (ToolBar) utilice el tema de textos claros sobre fondos oscuros, así que editamos /app/src/main/res/layout/app_bar.xml y definimos el tema Holo como se muestra a continuación.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    app:theme="@style/Theme.AppCompat.NoActionBar"
    android:layout_height="wrap_content"
    android:background="@color/primaryColor">

</android.support.v7.widget.Toolbar>

toolbar_icons3

Eso es todo por ahora, en el siguiente tutorial vamos a definir de que va a tratar nuestra app y aprenderemos a implementar desde cero unos de los elementos más poderosos de Android, el NavigationDrawer 🙂 salu2