Tutoriales Android

Introducción y objetivos

En este capítulo abordaremos dos de los aspectos más novedosos de Android la seguridad y el API de posicionamiento.

El capítulo comienza estudiando los fundamentos del sistema de seguridad que incorpora Android. Se trata de un aspecto vital para protegernos de aplicaciones mal intencionadas que intenten violar la privacidad del usuario y evitar que realicen acciones no deseadas. Gracias al sistema de permisos, se consige impedir que las aplicaciones realicen acciones comprometidas, si previamente no han solicitado el permiso adecuado.

En la segunda parte del capítulo, se describe el API que incorpora Android para permitir conocer la posición geográfica del dispositivo. Estos servicios se basan principalmente en el GPS, pero también disponemos de novedosos servicios de localización basados en telefonía móvil y redes Wi-Fi. A lo largo de este capítulo mostraremos una serie de ejemplos que te permitirán aprender a utilizar estas funciones.

Terminamos el capítulo describiendo como podemos incorporar a nuestra aplicación servicios realizados por terceros. En concreto instalaremos una vista que permite representar un mapa de Google Maps.

Objetivos:

  • Mostrar los pilares de la seguridad en Android

  • Describir como Android crea un usuario Linux asociado a cada aplicación.

  • Describir el esquema de permisos en Android y enumerar los permisos más importantes.

  • Mostrar como los permisos de Android pueden ser ampliados con permisos definidos por el usuario y enumerar los pasos a seguir para crear un nuevo permiso.

  • Describir las APIs de Android para la geolocalización y los diferentes tipos de sistemas de posicionamiento disponible.

  • Ver lo sencillo que resulta incorporar en nuestra aplicación un servicio de un tercero. En concreto Google Maps.

Permisos en Android 6 Marshmallow

En un dispositivo con versión de Android anterior a Marshmallow un usuario concede los permisos a una aplicación en el momento de la instalación. Si no está de acuerdo con algún permiso, la única alternativa para el usuario es no instalar la aplicación. Una vez instalada la aplicación, puede realizar las acciones asociadas a estos permisos tantas veces como desee y cuando desee. Esta forma de trabajar dejaba a los usuarios indefensos ante posibles abusos. Por ejemplo, si queremos utilizar WhatsApp o jugar a Apalabrados tenemos que aceptar la larga lista de permisos innecesarios que nos solicitan. El usuario se resigna y acaba aceptando prácticamente cualquier permiso.

En la versión 6 se introducen importantes novedades a la hora de conceder los permisos a las aplicaciones. En primer lugar los permisos son divididos en normales y peligrosos. A su vez los permisos peligrosos se dividen en 9 grupos: almacenamiento, localización, teléfono, SMS, contactos, calendario, cámara, micrófono y sensor de ritmo cardíaco. En el proceso de instalación el usuario da el visto bueno a los permisos normales, de la misma forma como se hacía en la versión anterior. Por el contrario, los permisos peligrosos no son concedidos  en la instalación. La aplicación consultará al usuario si quiere conceder un permiso peligroso en el momento de utilizarlo:

Además se recomienda que la aplicación indique para que lo necesita. De esta forma el usuario tendrá más elementos de juicio para decidir si da o no el permiso. Si el usuario no concede el permiso la aplicación ha de tratar de continuar el proceso sin este permiso. Otro aspecto interesante es que el usuario podrá configurar en cualquier momento que permisos concede y cuáles no. Por ejemplo, podemos ir al administrador de aplicaciones y seleccionar la aplicación Navegador. En el apartado permisos se nos mostrará los grupos de permisos que podemos conceder:

Observa como de los grupos de permisos solicitados, en este momento solo concedemos el permiso de Ubicación.

El usuario concede o rechaza los permisos por grupos. Si en el manifiesto se ha pedido leer y escribir en la SD, concedemos los dos permisos o ninguno. Es decir, no podemos conceder permiso de lectura, pero denegar el de escritura.

video[Tutorial] Permisos en Android 6.0 Marshmallow

 

Para reforzar los conceptos que acabamos de exponer es recomendable que hagas el siguiente ejercicio:

 

Ejercicio: Trabajando con permisos en Android Marshmallow

 

1.     Asegúrate de tener instalada una versión de Android 6.0 o posterior. Para verificarlo pulsa en el botón  SDK Manager:

2.     Crea un nueva proyecto con los siguientes datos:

Application name: Permisos en Marshmallow

Package name: org.example.permisosenmarshmallow

 Phone and Tablet

    Minimun SDK: API 9 Android 2.3 (Gingerbread)

Add an activity: BasicActivity

3.   Añade las líneas subrayadas y elimina las tachadas dentro de el método onCreate() de MainActivity

public void onClick(View view) {
    borrarLlamada();
    Snackbar.make(view, "Replace with your …", Snackbar.LENGTH_LONG)
            .setAction("Action", null).show();
}

4.   Declara la siguiente variable al principio de la clase:

private View vista;

5.     En el método onCreate() añade:

vista = findViewById(R.id.content_main);

6.    En la etiqueta <RelativeLayout> de content_main.xml añade:

android:id="@+id/content_main"

7.     Añade el siguiente método:

void borrarLlamada() {
   getContentResolver().delete(CallLog.Calls.CONTENT_URI,
                                     "number='555555555'", null);
   Snackbar.make(vista, "Llamadas borradas del registro.",
                                         Snackbar.LENGTH_SHORT).show();
}

Como se describirá en el capítulo 9, este código elimina del registro de llamadas del teléfono todas las llamadas cuyo número sea 555555555. La segunda línea muestra un cuadro de texto tipo Snackbar para avisar que la acción se ha realizado.

8.  Observa cómo el sistema nos advierte de que estamos actuando de forma no correcta:

 

9 .   Ignora esta advertencia y ejecuta el proyecto. Si pulsas en el botón flotante, aparecerá el siguiente  error:

10.  Abre el Log cat para verificar la causa del error:

Caused by: java.lang.SecurityException: Permission Denial: opening 
   provider com.android.providers.contacts.CallLogProvider from … requires 
   android.permission.READ_CALL_LOG or android.permission.WRITE_CALL_LOG

Es decir, la aplicación se ha detenido porque está realizando una acción que requiere de la solicitud de un permiso

11.  Añade en AndroidManifest.xml:         

<uses-permission android:name="android.permission.WRITE_CALL_LOG"/>

12.  Si ejecutas de nuevo el proyecto en un dispositivo con una versión anterior a la 6.0, podrás verificar que ya no se produce el error.

13.  Si ejecutas ahora en un dispositivo con versión 6.0 (si no dispones de uno utiliza un emulador), observarás que el error continúa.

14. Para entender lo que ha ocurrido, ve a Ajustes / Aplicaciones / Permisos en Marshmallow / Permisos:

 

Desde aquí podrás configurar los permisos peligrosos que quieres otorgar a la aplicación. Observa como el grupo de permisos referentes al teléfono está desactivado. Cuando instalamos una aplicación no se le concede ningún permiso peligroso.

15. Activa el permiso:

16. Vuelve a ejecutar la aplicación y verificar que ya no se produce el error:

Como acabamos de comprobar la aplicación anterior va a funcionar correctamente en dispositivos con una versión anterior a la 6.0. Sin embargo, cuando se ejecute en la nueva versión, se producirá un error. Aunque hemos visto cómo el usuario puede evitarlo, no es desde luego la forma correcta de trabajar.

A partir de Android Marshmallow trabajar con acciones que necesiten de un permiso va a suponer un esfuerzo adicional para el programador. Antes de realizar la acción tendremos que verificar si tenemos el permiso. En caso negativo hay que exponer al usuario para qué lo queremos y pedírselo. Si el usuario no nos diera el permiso, tendremos qué decidir qué hacer. ¿Podemos realizar la acción solicitada aunque no dispongamos de cierta información? ¿Dejamos de hacer la acción solicitada? ¿O salimos de la aplicación? En el siguiente ejercicio veremos cómo realizar esta tarea.

video[Tutorial] Trabajando con permisos en Android 6.0

Ejercicio: Solicitud de permisos en Android Marshmallow

1. El primer paso va a ser verificar que tenemos el permiso adecuado antes de realizar una acción que lo requiera. Resulta sencillo, simplemente has de añadir el if que se muestra a continuación

void borrarLlamada() {
    if (ContextCompat.checkSelfPermission(this,
            Manifest.permission.WRITE_CALL_LOG)
            == PackageManager.PERMISSION_GRANTED) {
        getContentResolver().delete(CallLog.Calls.CONTENT_URI,
                                           "number='555555555'", null);
        Snackbar.make(vista, "Llamadas borradas del registro.",
                                           Snackbar.LENGTH_SHORT).show();
    }
}

2. Ejecuta de nuevo la aplicación en un dispositivo con versión 6.0 y verifica que ya no se produce el error.

3. Esto no resuelve el problema. Nuestra aplicación no puede limitarse a no realizar la acción cuando no disponga del permiso. Ha de avisar al usuario y solicitar el permiso. Para ello añade una sección else a el if anterior

if (ContextCompat.checkSelfPermission(this,
            Manifest.permission.WRITE_CALL_LOG)
            == PackageManager.PERMISSION_GRANTED) {
   …
} else {
    solicitarPermiso(Manifest.permission.WRITE_CALL_LOG, "Sin el permiso"+
        " administrar llamadas no puedo borrar llamadas del registro.",
        SOLICITUD_PERMISO_WRITE_CALL_LOG, this);
}

4. Añade el siguiente método:

public static void solicitarPermiso(final String permiso, String justificacion,
                        final int requestCode, final Activity actividad) {
   if (ActivityCompat.shouldShowRequestPermissionRationale(actividad,
                                                                permiso)){
     new AlertDialog.Builder(actividad)
        .setTitle("Solicitud de permiso")
        .setMessage(justificacion)
        .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                ActivityCompat.requestPermissions(actividad,
                        new String[]{permiso}, requestCode);
            }})
        .show();
   } else {
      ActivityCompat.requestPermissions(actividad,
              new String[]{permiso}, requestCode);
   }
}

Es posible que tengas que solicitar permisos desde diferentes puntos de la aplicación. Por esta razón se ha declarado este método público y estático. Además, se ha pasado a parámetros toda la información que necesita: el permiso a solicitar, la justificación de porque lo necesitamos, un código de solicitud y la actividad que recogerá la respuesta. Una vez el usuario decida si da el permiso, se llamará al método onRequestPermissionsResult(), que  tendrás que declarar en la actividad que se pasa en el cuarto parámetro. El código es un valor numérico que permitirá identificar diferentes solicitudes.

Android nos recomienda que indiquemos al usuario para qué le estamos solicitando el permiso. Si consideras que no es necesario, puedes eliminar la primera parte del método y dejar solo el código que aparece dentro del else.  Antes de mostrar la explicación usando un AlertDialog, se verifica en el if si interesa mostrar esta información. Si el usuario ha indicado que no nos da el permiso y además ha marcado la casilla de que no quiere que volvamos a preguntar, no es conveniente insistir. El sistema se encarga de recordar esta información, nosotros simplemente tenemos que usar el método shouldShowRequestPermissionRationale().

NOTA: Este código se ejecuta en el hilo principal, por lo tanto nunca utilices un método para preguntar al usuario que pueda bloquear el hilo. Observa como en el ejemplo se utilizan llamadas asíncronas.

El trabajo más importante lo hace el método requestPermissions() que muestra un cuadro de diálogo como el siguiente y registra el permiso según la respuesta del usuario:

5.  Una vez que el usuario escoja se realizará una llamada a onRequestPermissionsResult(). Aquí podremos procesar la respuesta. Añade el siguiente método:

 

 @Override public void onRequestPermissionsResult(int requestCode,
                        String[] permissions, int[] grantResults) {
   if (requestCode == SOLICITUD_PERMISO_WRITE_CALL_LOG) {
      if (grantResults.length== 1 &&
         grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            borrarLlamada();
      } else {
         Toast.makeText(this, "Sin el permiso, no puedo realizar la " +
                              "acción", Toast.LENGTH_SHORT).show();
      }
   }
}

Este método ha de estar declarado en una actividad. En caso de que el usuario nos conceda el permiso, tenemos que volver a realizar la acción que no pudo realizarse (en el ejemplo, borrarLlamada()). En caso de que hayas solicitado el permiso desde diferentes acciones o que hayas solicitado diferentes permisos, el valor de requestCode permitirá diferenciar cada caso.

6.  Declara la siguiente variable al principio de la clase:

 private static final int SOLICITUD_PERMISO_WRITE_CALL_LOG = 0;

7.  Verifica que la aplicación funciona correctamente.

Práctica: Solicitud de permisos en Mis Lugares

En el ejercicio Añadiendo fotografías desde la galería era imprescindible disponer del permiso para acceder a la memoria externa. Para poder ejecutar la aplicación en un dispositivo con una versión de Android 6 o superior, tuvimos que conceder este permiso manualmente. Un usuario final no debe realizar esta solicitud desde Ajustes. Va a ser imprescindible, solicitar este permiso desde la aplicación.

1.  Cuando el usuario intente obtener una fotografía desde la galería o tomar una fotografía, solicita el permiso READ_EXTERNAL_STORAGE tal y como se ha realizado en el ejercicio anterior.

Preguntas de repaso : Permisos en Android 6.0 Marshmallow

 

Los tres pilares de la seguridad en Android

video[Tutorial] Seguridad en Android

La seguridad es un aspecto clave de todo sistema. Si nos descargáramos una aplicación maliciosa de Internet o de Google Play Store, esta podría leer nuestra lista de contactos, averiguar nuestra posición GPS, mandar toda esta información por Internet y terminar enviando 50 mensajes SMS.

En algunas plataformas antiguas, como Windows Mobile, estábamos prácticamente desprotegidos ante aplicaciones maliciosas. Por lo tanto, los usuarios tenían que ser muy cautos antes de instalar una aplicación.

En otras plataformas, como en iOS, toda aplicación ha de ser validada por Apple antes de poder ser instala en un terminal. Además, solo está permitido instalar aplicaciones de la tienda oficial de Apple. Esto limita a los pequeños programadores y da un poder excesivo a Apple. Se trata de un planteamiento totalmente contrario al software libre.

Android propone un esquema de seguridad que protege a los usuarios, sin la necesidad de imponer un sistema centralizado y controlado por una única empresa. La seguridad en Android se fundamenta en los tres pilares siguientes:

  • Como se  ha comentado en el primer capítulo Android está basado en Linux, por lo tanto, vamos a poder aprovechar la seguridad que incorpora este sistema operativo. De esta forma Android puede impedir que las aplicaciones tengan acceso directo al hardware o interfieran con recursos de otras aplicaciones.
  • Toda aplicación ha de ser firmada con un certificado digital que identifique a su autor. La firma digital también nos garantiza que el fichero de la aplicación no ha sido modificado. Si se desea modificar la aplicación está tendrá que ser firmada de nuevo, y esto solo podrá hacerlo el propietario de la claveprivada. Es habitual que un certificado digital sea firmado a su vez por una autoridad de certificación, sin embargo en Android esto no es necesario
  • Si queremos que una aplicación tenga acceso a partes del sistema que pueden comprometer la seguridad del sistema hemos de utilizar un modelo de permisos, de forma que el usuario conozca los riesgos antes de instalar la aplicación.

En los siguientes apartados se describe con más detalle el primer y tercer punto. El proceso de firmar una aplicación será descrito en el último capítulo. Si no estás familiarizado con este concepto te recomendamos que veas el siguiente vídeo:

video[Tutorial]: La firma digital

Preguntas de repaso :  Los tres pilares de la seguridad

 

Usuario Linux y acceso a ficheros

Para proteger el acceso a recursos utilizados por otras aplicaciones, Android crea una cuenta de usuario Linux (user ID) nueva por cada paquete (.apk) instalado en el sistema. Este usuario es creado cuando se instala la aplicación y permanece constante durante toda su vida en el dispositivo.

Cualquier dato almacenado por la aplicación será asignado a su usuario Linux, por lo que normalmente no tendrán acceso otras aplicaciones. No obstante, cuando crees un fichero puedes usar los modos MODE_WORLD_READABLE y/o MODE_WORLD_WRITEABLE para permitir que otras aplicaciones puedan leer o escribir en el fichero. Aunque otras aplicaciones puedan escribir el fichero, el propietario siempre será el usuario asignado a la aplicación que lo creó.

Dado que las restricciones de seguridad se garantizan a nivel de proceso, el código de dos paquetes no puede, normalmente, ejecuten en el mismo proceso. Para que dos aplicaciones se ejecuran en un mismo proceso, sería necesario usar el mismo usuario. Puedes utilizar el atributo sharedUserId en AndroidManifest.xml para asignar un mismo usuario Linux a dos aplicaciones. Con esto conseguimos que a efectos de seguridad ambas aplicaciones sean tratadas como una sola. Por razones de seguridad, ambas aplicaciones han de estar firmadas con el mismo certificado digital.

Preguntas de repaso : Usuario Linux

 

El esquema de permisos en Android

 

Para proteger ciertos recursos y características especiales, Android define un esquema de permisos. Toda aplicación que acceda a estos recursos está obligada a declarar su intención de usarlos. En caso de que una aplicación intente acceder a un recurso del que no ha solicitado permiso, se generará una excepción de permiso y la aplicación será interrumpida inmediatamente.

video[Tutorial] Los permisos en Android

Cuando el usuario instala una aplicación este podrá examinar la lista de permisos que solicita la aplicación y decidir si considera oportuno instalar dicha aplicación. A continuación se muestra una lista con algunos de los permisos más utilizados en Android:

PERMISOS PELIGROSOS:

Almacenamiento Externo:

  • WRITE_EXTERNAL_STORAGE– Modificar/eliminar almacenamiento USB (API 4). Permite el borrado y la modificación de archivos en la memoria externa. Lo ha de solicitar toda aplicación que necesite escribir un fichero en la memoria externa; por ejemplo, exportar datos en XML. Pero al permitirlo también podrán modificar/eliminar ficheros externos creados por otras aplicaciones.
  • READ_EXTERNAL_STORAGE– Leer almacenamiento USB  (API 16).  Permite leer archivos en la memoria externa. Este permiso se ha introducido en la versión 4.1. En versiones anteriores todas las aplicaciones pueden leer en la memoria externa. Por lo tanto, has de tener cuidado con la información que dejas en ella.

Ubicación:

  • ACCESS_COARSE_LOCATION –Localización no detallada (basada en red). Localización basada en telefonía móvil (Cell-ID) y Wi-Fi. Aunque en la actualidad esta tecnología suele ofrecernos menos precisión que el GPS, no siempre es así. Por ejemplo, se está aplicando en el interior de aeropuertos y museos con precisiones similares.
  • ACCESS_FINE_LOCATION –Localización GPS detallada. Localización basada en satélites GPS. Al dar este permiso también estamos permitiendo la localización basada en telefonía móvil y Wi-Fi (ACCESS_COARSE_LOCATION).

Telefono:

  • CALL_PHONE –Llamar a números de teléfono directamente  Servicios por los que tienes que pagar. Permite realizar llamadas sin la intervención del usuario. Nunca solicites este permiso en tus aplicaciones, muchos usuarios no instalarán tu aplicación. Si has de realizar una llamada, es mejor realizarla por medio de una intención. A diferencia de la llamada directa, no necesitas ningún permiso, dado que el usuario ha de pulsar el botón de llamada para que comience.
  • READ_PHONE_STATE –Consultar identidady estado del teléfono.. Muchas aplicaciones, como los juegos, piden este permiso para ponerse en pausa cuando recibes una llamada. Sin embargo, también permite el acceso al número de teléfono,  IMEI (identificador de teléfono GSM), IMSI (identificador de tarjeta SIM) y al identificador único de 64 bits que Google asigna a cada terminal. Incluso si hay una llamada activa, podemos conocer el número al que se conecta la llamada.
  • READ_CALL_LOG y WRITE_CALL_LOG –Leer y modificar el registro de llamadas telefónicas. Como realizar estas acciones se describe al final del capítulo 9.
  • ADD_VOICEMAIL –Añadir mensajes de voz. Permite crear nuevos mensajes de voz en el sistema.
  • USE_SIP –Usar Session Initial Protocol. (API 9). Permite a tu aplicación usar el protocolo SIP.
  • PROCESS_OUTGOING_CALLS –Procesar llamadas salientes. Permite a la aplicación controlar, modificar o abortar las llamadas salientes.

Mensajes de texto (SMS):

  • SEND_SMS –Enviar mensaje SMS  Servicios por los que tienes que pagar Permite la aplicación mandar de texto SMS sin la validación del usuario. Por iguales razones que CALL_PHONE,  a no ser que tu aplicación tenga que mandar SMS sin la intervención del usuario, resulta más conveniente enviarlos por medio de una intención.
  • RECEIVE_SMS –Recibir mensajes de texto.  Permite a la aplicación recibir y procesar SMS. Una aplicación puede modificar o borrar los mensajes recibidos
  • READ_SMS –Leer mensajes de texto.  Permite a la aplicación leer los mensajes SMS entrentes.
  • RECEIVE_MMS –  Recibir mensajes MMS. Permite monitorizar los mensajes multimedia entrantes, pudiendo acceder a su contenido.
  • RECEIVE_WAP_PUSCH – Recibir mensajes WAP Push. Permite monitorizar los mensajes WAP Push entrantes. Un mensaje WAP PUSH es un tipo de SMS que se usa para acceder de manera sencilla a una página WAP en lugar de teclear su direccion URL en el navegador.

Contactos:

  • READ_CONTACTS –  Leer datos de contactos. Permite leer información sobre los contactos almacenados (nombres, correos electrónicos, números de teléfono). Algunas aplicaciones podrían utilizar esta información de forma no lícita
  • WRITE_CONTACTS – Escribir datos de contactos. Permite modificar los contactos.
  • GET_ACCOUNTS Obtener Cuentas. Permiten acceder a la lista de cuentas en el Servicio de Cuentas[1].

Calendario:

  • READ_CALENDAR –  Leer datos de contactos. Permite leer información del calendario del usuario.
  • WRITE_CONTACTS – Escribir datos de contactos. Permite escribir en el calendario, pero no leerlo.

Camara:

  • CAMARA  –  Hacer fotos / grabar vídeos. Permite acceso al control de la cámara y a la toma de imágenes y vídeos. El usuario puede no ser consciente.

Microfono:

  • RECORD_AUDIO  –  Grabar audio. Permite acceso grabar sonido desde el micrófono del teléfono.

Sensores corporales :

  • BODY SENSORS_ – Leer sensores corporales. Da acceso a los datos de los sensores que están monitorizando el cuerpo del usuario. Por ejemplo, el lector de ritmo cardiaco.

PERMISOS NORMALES:

Comunicaciones:

  • INTERNET_– Acceso a Internet sin límites. Permite establecer conexiones a través de Internet. Este es un permiso muy importante, en el que hay que fijarse a quién se otorga.La mayoría de las aplicaciones lo piden, pero no todas lo necesitan. Cualquier malware necesita una conexión para poder enviar datos de nuestro dispositivo.
  • ACCESS_NETWORK_STATE –Ver estado de red.  Información sobre todas las redes. Por ejemplo para saber si tenemos conexión a internet.
  • CHANGE_NETWORK_STATE – Cambiar estado de red. Permite cambiar el estado de conectividad de redes.
  • NFC – Near field communication. (API 19) Algunos dispositivos disponen de un trasmisor infrarrojo para el control remoto de electrodomésticos.
  • TRASMIT_R – Trasmitir por infrarrojos. (API 19) Algunos dispositivos disponen de un trasmisor infrarrojo para el control remoto de electrodomésticos.

Conexión WIFI:

Bluetooth:
  • BLUETOOTH – Crear conexión Bluetooth. Permite a una aplicación conectarse con otro dispositivo Bluetooth. Antes ambos dispositivos han de emparejarse
  • .BLUETOOTH_ADMIN – Emparejar Bluetooth. Permite descubrir y emparejarse con otros dispositivos Bluetooth.

Consumo de batería:

  • WAKE_LOCK –Impedir que el teléfono entre en modo de suspensión. Para algunas aplicaciones, como un navegador GPS, puede ser importante que no sean suspendidas nuca. Realmente, a lo único que puede afectar es a nuestra batería.
  • FLASHLIGHT – Linterna. Permite encender el flash de la cámara.
  • VIBRATE – Control de la vibración. Permite hacer vibrar al teléfono. Los juegos suelen utilizarlo.

Aplicaciones:

  • RECEIVE_BOOT_COMPLETED – Ejecución automática al encender el teléfono. Permite a una aplicación recibir el anuncio broadcast  ACTION_BOOT_COMPLETED enviado cuando el sistema finaliza un inicio. Gracias a esto la aplicación pondrá ponerse en ejecución al arrancar el teléfono.
  • BROADCAST_STICKY – Enviar anuncios broadcast permanentes. Un broadcast permanente llegará a los receptores de anuncios que actualmente estén escuchando, pero también a los que se instancien en un futuro. Por ejemplo, el sistema emite el anuncio broadcast ACTION_BATTERY_CHANGED de forma permanente. De esta forma, cuando se llama a registerReceiver() se obtiene la intención de la última emisión de este anuncio. Por lo tanto, puede usarse para encontrar el estado de la batería sin necesidad de esperar a un futuro cambio en su estado. Se ha incluido este permiso dado que las aplicaciones mal intencionadas pueden ralentizar el dispositivo o volverlo inestable al demandar demasiada memoria.
  • KILL_ BACKGROUND_PROCESSES – Matar procesos en Background(API 9). Permite llamar a killBackgroundProcesses(String). Al hacer esta llamada el sistema mata de inmediato a todos los procesos de fondo asociados con el paquete indicado. Es el mismo método que usa el sistema cuando necesita memoria. Estos procesos serán reiniciados en el futuro, cuando sea necesario.
  • REORDER_TASKS – Reordenar tareas. Permite a una aplicación cambiar el orden de la lista de tareas.
  • INSTALL_SHORTCUT y UNINSTALL_SHORTCUT– Instalar y desinstalar acceso directo(API 19). Permite a una aplicación añadir o eliminar un acceso directo a nuestra aplicación en el escritorio.
  • GET_PACKAGE_SIZE – Obtener tamaño de un paquete. Permite a una aplicación conocer el tamaño de cualquier paquete.
  • EXPAND_STATUS_BAR – Expandir barra de estado. Permite a una aplicación expandir o contraer la barra de estado

Configuraciones del sistema:

  • CHANGE_CONFIGURATION – Modificar la configuración global del sistema. Permite cambiar la configuración del sistema (como la configuración local)
  • SET_WALLPAPER – Poner fondo de pantalla. Permite establecer fondo de pantalla en el escritorio.
  • SET_WALLPAPER_HITS – Sugerencias de  fondo de pantalla. Permite a las aplicaciones establecer  sugerencias de fondo de pantalla.
  • SET_ALARM – Establecer Alarma. Permite a la aplicación enviar una intención para poner una alarma o temporizador en la aplicación Reloj.
  • SET_TIME_ZONE – Cambiar zona horario.  Permite cambiar la zona horaria del sistema.
  • ACCESS_NOTIFICATION_POLICY  – Acceso a política de notificaciones (API 23). Permite conocer la política de notificaciones del sistema.

Audio:

  • MODY_AUDIO_SETTINGS  Cambiar ajustes de audio . Permite cambiar ajustes globales de audio, como el volumen.

Sincronización:

  • READ_SYNC_SETTINGS – Leer ajustes de sincronización. Permite saber si tienes sincronización en segundo plano con alguna aplicación (como con un cliente de Twitter o Gmail).
  • WRITE_SYNC_SETTINGS – Escribir ajustes de sincronización. Permite registrar tu aplicación como adaptador de sincronización (SyncAdapter).
  • READ_SYNC_STATS – Leer estadísticas de sincronización.

Ubicación:

  • ACCESS_LOCATION_EXTRA_COMMANDS – Mandar comandos extras de localización. Permite a una aplicación acceder a comandos adicionales de los proveedores de localización. Por ejemplo, tras pedir este permiso podríamos enviar el siguiente comando al GPS, con el método: sendExtraCommand("gps", "delete_aiding_data", null);.

Seguridad:

  • USE_FINGERPRINT  – Usar huella digital(API 23).   Permite usar el hardware de reconocimiento de huella digital.
  • DISABLE_KEYGUARD – Deshabilitar bloqueo de teclado.  Permite a las aplicaciones desactivar el bloqueo del teclado si no es seguro.

 

NOTA: Los permisos peligrosos pertenecen a uno de los 9 grupos anteriores. Estos grupos son importantes dado que el usuario concede o deniega el permiso a un grupo entero. Por el contrario, a partir de la versión 6.0 los permisos normales ya no se clasifican en grupos. Se han organizado en este texto por grupos para una mejor organización.

NOTA: Existen otros permisos que no han sido incluidos en esta lista dado que no podemos solicitarlos en nuestras aplicaciones al estar reservados para aplicaciones del sistema.

Para solicitar un determinado permiso en tu aplicación, no tienes más que incluir una etiqueta<uses-permission> en el fichero AndroidManifest.xml de tu aplicación. En el siguiente ejemplo se solicitan dos permisos:
<manifest package="org.example.mi_aplicacion" >
    …
    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
    <uses-permission android:name="android.permission.SEND_SMS"/>
</manifest>

Preguntas de repaso :  Los tres pilares de la seguridad



Permisos definidos por el usuario en Android

Además de los permisos definidos por el sistema, los desarrolladores vamos a poder crear nuevos permisos para restringir el acceso a elementos de nuestro software.

Puedes ver algunos aspectos relacionados en formato poli[Media]

Permisos definidos por el usuario en Android

Abordaremos el estudio de la creación de nuevos permisos utilizando el siguiente ejemplo. Somos la empresa PayPerView especializada en ofrecer servicios de reproducción de vídeos bajo demanda. Queremos crear un software que permita a cualquier desarrollador reproducir nuestros vídeos desde sus aplicaciones. No obstante, este servicio no es gratuito, por lo que nos interesa que el usuario sea advertido cuando se instale la aplicación del tercero, indicándole que esta aplicación va hacer uso de un servicio no gratuito.

Para definir el nuevo permiso utilizaremos el tag <permission> en el ficheroAndroidManifest.xml de nuestro software. A continuación se muestra un ejemplo:

<permission
      android:name="com.payperview.servicios.VER_VIDEOS"
      android:description="@string/descripcion"
      android:label="@string/etiqueta"
      android:permissionGroup="android.permission-group.COST_MONEY"
      android:protectionLevel="dangerous" />

El atributo android:name indica el nombre del permiso. Como ves ha de estar dentro del mismo dominio que nuestra aplicación. El atributo android:permissionGroup es opcional y permite incluir nuestro permiso en un grupo. En el ejemplo se ha incluido en el grupo de permisos que pueden suponer un coste económico al usuario. El atributo android:protectionLevelinforma al sistema cómo el usuario ha de ser informado y qué aplicaciones tienen acceso al permiso. Los valores posibles se indican a continuación:

 

normal  El usuario no es advertido de que se va utilizar el permiso.
dangerous El usuario es advertido.
signature Solo aplicaciones firmadas con la misma firma digital que la aplicación que declara el permiso
signatureOrSystem Igual que signature pero además puede ser usado por el sistema. Caso poco frecuente donde varios fabricantes necesitan compartir características a través de la imagen del sistema.

Los atributos android:label y android:description son opcionales y han de ser introducidos a través de un recurso de cadena. En estas cadenas hay que describir el permiso de forma abreviada y extensa, respectivamente. Veamos cómo podría ser en el ejemplo:

<string name="etiqueta">
   reproducción de videos bajo demanda </string>
<string name="descripcion">Permite a la aplicación reproducir 
   videos de la empresa PayPerView sin tu intervención. Se trata 
   de un servicio no gratuito, por lo que puede afectar al saldo 
   de tu cuenta con esta empresa. Si no tienes una cuenta 
   abierta los videos no podrán ser reproducidos. </string>

Ejercicio paso a paso: Creando tus propios permisos.

 

Vamos a comprobar cómo usar este permiso con este ejercicio:

1.     Crea un nuevo proyecto con los siguientes datos:

 

Project Name: NuevoPermiso

Package Name: com.payperview.servicios

Min SDK Version: 3

2.     Crea una nueva actividad en este proyecto con nombre VerVideo y copia el mismo código de la actividad principal. En el ejemplo real esta nueva actividad sería la responsable de la reproducción de vídeos.

3.  En lugar de visualizar un vídeo vamos a poner un toast. Añade en el método onCreate el siguiente código:

Toast.makeText(this, "Reproduciendo Vídeo", Toast.LENGTH_SHORT).show();

La aplicación NuevoPermiso va a tener dos actividades: MainActivity y VerVideo. Realmente estas actividades no hacen nada, se limitan a poner "hello word". La actividad  VerVideo es la que reproduciría los videos y la que queremos proteger con un permiso. La actividad MainActivity no sirve para nada pero es para que la aplicación tenga una actividad inicial.

4.     Modifica el fichero AndroidManifest.xml según el código mostrado a continuación:

<manifest…>
       <application…>
             …
             <activity 
                    android:name="VerVideo"
                    android:permission= "com.payperview.servicios.VER_VIDEOS">
                    <intent-filter>
                           <action android:name="android.intent.action.VIEW" />
                    </intent-filter>
             </activity>
       </application>
</manifest>

5.     Copia delante de <application…/> la etiqueta <permission …/> del ejemplo anterior.

6.     Recuerda definir los recursos de cadena etiqueta y descripcion, tal y como se indica en el ejemplo anterior.

7.     Ejecuta el proyecto. Es imprescindible para registrar en el teléfono el nuevo permiso y la nueva actividad que queremos lanzar desde otras aplicaciones.

8.     Para usar este servicio crea un nuevo proyecto con los siguientes datos:

Project Name: UsarPermiso

Package Name: org.example.usarpermiso

9.     Abre el fichero del layout principal (activity_main.xml) y reemplaza el código por el siguiente:

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="verVideo"
        android:text="Ver Vídeo" />
</LinearLayout>

10.  Abre la actividad principal y añade el siguiente método:

public void verVideo (View view){
        Intent i = new Intent();
        i.setClassName("com.payperview.servicios",
                         "com.payperview.servicios.VerVideo");
        startActivity(i);
    }

11.  Ejecuta la aplicación. Cuando pulses el botón la aplicación provocará un error.

12.  Visualiza la ventana LogCat para verificar que se trata de un error de permiso. Ha de aparecer algo parecido a:

13.  Para solucionar el problema tendrás que incluir el siguiente código al principio del fichero AndroidManifest.xml:         

<uses-permission android:name="com.payperview.servicios.VER_VIDEOS"/>

14.  Comprueba cómo ahora se accede al servicio sin problemas.

15.  En este ejercicio hemos puesto el nivel de protección del permiso como dangerous. No obstante, si la aplicación es instalada desde Eclipse no se nos advierte sobre este permiso.Para que si lo advierta, tenemos que instalar la aplicación manualmente.

Para ello, con un administrador de archivo, abre la carpeta bin del proyecto y copia el ficheroUsarPermiso.apk a la memoria externa del dispositivo. Instala la aplicación. Verás como aparece un mensaje similar al siguiente:

Preguntas de repaso y reflexión:

Pincha aquí para hacer un test.

La API de localización de Android

 

video[Tutorial] La API de localización de Android

Ejercicio: El API de localización de Android.

En este ejercicio crearemos una aplicación que es capaz de leer información de localización del dispositivo y actualizarla cada vez que se produce un cambio.

1.     Crea un nuevo proyecto con los siguientes datos:

ApplicationName: Localizacion

Package Name: org.example.localizacion

RPhone and Tablet

     Minimum SDK: API 9 Android 2.3 (Gingerbread)

Add an activity: Empty Activity

2.     Por razones de privacidad acceder a la información de localización está en principio prohibido a las aplicaciones. Si estas desean hacer uso de este servicio han de solicitar el permiso adecuado. En concreto hay que solicitar ACCESS_FINE_LOCATION para acceder a cualquier tipo de sistema de localización o ACCESS_COARSE_LOCATION para acceder al sistema de localización basado en redes. Añade la siguiente línea en el fichero AndroidManifest.xml dentro de la etiqueta <manifest>:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

Por lo tanto en este ejemplo  vamos autilizar, tanto la localización fina, que nos proporciona el GPS, como una menos precisa,que nos proporcionada  las torres de telefonía celular y las redes WiFi.

 3.   Sustituye el fichero res/layout/activity_main.xml por:

<ScrollView
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent">
     <TextView
        android:id="@+id/salida"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</ScrollView>

En este ejemplo nos limitaremos a mostrar en modo de texto la información obtenida desde el API de localización. Para ello usaremos un TextView  dentro de un ScrollView, tal y como se muestra en la siguiente pantalla:

4.     Abre la clase MainActivity y copia el siguiente código:

public class MainActivity extends Activity implements LocationListener {
   private static final long TIEMPO_MIN = 10 * 1000 ; // 10 segundos
   private static final long DISTANCIA_MIN = 5; // 5 metros
   private static final String[] A = { "n/d", "preciso", "impreciso" };
   private static final String[] P = { "n/d", "bajo", "medio","alto" };
   private static final String[] E = { "fuera de servicio",
                     "temporalmente no disponible ", "disponible" };
   private LocationManager manejador;
   private String proveedor;
   private TextView salida;

   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      salida = (TextView) findViewById(R.id.salida);
      manejador = (LocationManager) getSystemService(LOCATION_SERVICE);
      log("Proveedores de localización: \n ");
      muestraProveedores();

      Criteria criterio = new Criteria();
      criterio.setCostAllowed(false);
      criterio.setAltitudeRequired(false);
      criterio.setAccuracy(Criteria.ACCURACY_FINE);
      proveedor = manejador.getBestProvider(criterio, true);
      log("Mejor proveedor: " + proveedor + "\n");
      log("Comenzamos con la última localización conocida:");
      Location localizacion = manejador.getLastKnownLocation(proveedor);
      muestraLocaliz(localizacion);
   }

La primera línea que nos interesa es la llamada a getSystemService(LOCATION_SERVICE) que crea el objeto manejador de tipo LocationManager. La siguiente línea hace una llamada al metodo log(), que se definirá más adelante. Simplemente muestra por el TextView ,salida, el texto indicado. La siguiente   llamada a muestraProveedores() también es un método definido por nosotros, que listará todos los proveedores de localización disponibles.

En las tres siguientes líneas vamos a seleccionar uno de estos proveedores de localización. Comenzamos creando un objeto de la clase Criteria, donde se podrá indicar las características que ha de tener el proveedor buscado. En este ejemplo indicamosque no ha de tener coste económico, ha de poder obtener la altura y ha de tener precisión fina. Para consultar otras restricciones, consultar documentación de la clase Criteria[1]. Con estas restricciones parece que estamos interesados en el proveedor basado en GPS, aunque de no estar disponible, se seleccionará otro que cumpla el mayor número de restricciones. Para seleccionar el proveedor usaremos el método getBestProvider(). En este método hay que indicar el criterio de selección y un valor booleano, donde indicamos si solo nos interesa los sistemas que el usuario tenga actualmente habilitados. Nos devolverá un Stringcon el nombre del proveedor seleccionado.

Algunos proveedores, como el GPS, puedentardar un cierto tiempo en darnos una primera posición. No obstante, Android recuerda la última posición que fue devuelta por este proveedor. Es lo que nos devuelve la llamada a getLastKnownLocation(). El método muestraLocaliz() será definido más tarde y muestra en pantalla una determinada localización.

5.     A continuación copia el resto del código:

// Métodos del ciclo de vida de la actividad
@Override protected void onResume() {
   super.onResume();
   manejador.requestLocationUpdates(proveedor, TIEMPO_MIN, DISTANCIA_MIN,
                           this);
}

@Override protected void onPause() {
   super.onPause();
   manejador.removeUpdates(this);
}

// Métodos de la interfaz LocationListener
public void onLocationChanged(Location location) {
   log("Nueva localización: ");
   muestraLocaliz(location);
}

public void onProviderDisabled(String proveedor) {
   log("Proveedor deshabilitado: " + proveedor + "\n");
}

public void onProviderEnabled(String proveedor) {
   log("Proveedor habilitado: " + proveedor + "\n");
}

public void onStatusChanged(String proveedor, int estado, Bundle extras) {
   log("Cambia estado proveedor: " + proveedor + ", estado="
            + E[Math.max(0, estado)] + ", extras=" + extras + "\n");
}

// Métodos para mostrar información
private void log(String cadena) {
   salida.append(cadena + "\n");
}

private void muestraLocaliz(Location localizacion) {
   if (localizacion == null)
      log("Localización desconocida\n");
   else
      log(localizacion.toString() + "\n");
}

private void muestraProveedores() {
   log("Proveedores de localización: \n ");
   List<String> proveedores = manejador.getAllProviders();
   for (String proveedor : proveedores) {
      muestraProveedor(proveedor);
   }
}

private void muestraProveedor(String proveedor) {
   LocationProvider info = manejador.getProvider(proveedor);
   log("LocationProvider[ " + "getName=" + info.getName()
         + ", isProviderEnabled="
         + manejador.isProviderEnabled(proveedor) + ", getAccuracy="
         + A[Math.max(0, info.getAccuracy())] + ", getPowerRequirement="
         + P[Math.max(0, info.getPowerRequirement())]
         + ", hasMonetaryCost=" + info.hasMonetaryCost()
         + ", requiresCell=" + info.requiresCell()
         + ", requiresNetwork=" + info.requiresNetwork()
         + ", requiresSatellite=" + info.requiresSatellite()
         + ", supportsAltitude=" + info.supportsAltitude()
         + ", supportsBearing=" + info.supportsBearing()
         + ", supportsSpeed=" + info.supportsSpeed() + " ]\n");
}
}

Para conseguir que se notifiquen cambios de posición hay que llamar al método requestLocationUpdates() y para indicar que se dejen de hacer las notificaciones hay que llamar a removeUpdates(). Dado que queremos ahorrar batería nos interesa que se reporten notificaciones solo cuando la aplicación esté activa. Por lo tanto tenemos que reescribir los métodos onResume() y onPause().

El método requestLocationUpdates() dispone de 4 parámetros: el nombre del proveedor, el tiempo entre actualizaciones en ms (se recomienda valores mayores de 60.000 ms), la distancia mínima (de manera que si es menor, no se notifica) y un escuchador de evenos que implemente el interface LocationListener.

Como nuestra actividad es un LocationListener tenemos que implementar los siguientes métodos:onLocationChanged() se activará cada vez que se obtenga una nueva posición. Los otros tres métodos pueden ser usados para cambiar de proveedor en caso de que se active uno mejor o deje de funcionar el actual. Sería buena idea llamar de nuevo aquí al método getBestProvider().

       El resto del código resulta fácil de interpretar.

6.     Observa cómo aparecen algunos errores. El sistema nos advierte de que estamos actuando de forma no correcta:

7.     Ignora esta advertencia y ejecuta el proyecto.

8.     Si ejecutas el proyecto en un dispositivo con una versión 6.0 o superior, podrás verificar que se produce el error. Para evitar que se produzca, en el dispositivo accede a Ajustes / Aplicaciones / Localización / Permisos: y activa el permiso de Ubicación:

Vuelve a ejecutar la aplicación y verificar que ya no se produce el error.

NOTA: Para aligerar el código del ejercicio no hemos incluido el código necesario para solicitar permiso a partir de la versión 6.0. Si vas a distribuir la aplicación, resulta impresindible realizar los pasos descritos en la sección Permisos en Android 6 Marshmallow.

9.     Verifica el funcionamiento del programa, si es posible con un dispositivo real con el GPS activado.

Preguntas de repaso: API Localización en Android

Emulación del GPS con Android Studio

Probar una aplicación de localización con un dispositivo real requiere que te desplaces para cambiar tu posición. Suele resultar más práctico probar este tipo de aplicaciones desde un emulador, ya que estos incorporan un sistema de emulación de posición GPS. Para abrir los controles extendidos de un emulador pulsa en los tres puntos que aparecen en la parte inferior de la barra de herramientas y selecciona la pestaña Location.

Desde aquí podremos enviar nuevas posiciones al dispositivo que está siendo emulado. El botón LOAD GPX / KML nos permiten realizar pruebas más complejas en nuestras aplicaciones de localización, sin necesidad de dar vueltas con el dispositivo en la mano. Un fichero GPX o KML registra una secuencia temporal de localizaciones. Existen muchos programas (Google Earth) que permiten grabar este tipo de ficheros. Luego podremos reproducir esta secuencia tantas veces como queramos hasta que nuestro programa funcione perfectamente.

Práctica: Emulación del GPS

Ejecuta el proyecto anterior en un emulador y prueba la emulación del GPS.

Google Maps

Google Maps nos proporciona un servicio de cartografía online que podremos utilizar en nuestras aplicaciones Android. Veamos las claves necesarias para utilizarlo. Estudiaremos la versión 2 de la API, que incorpora interesantes ventajas respecto a la versión anterior. Entre estas ventajas destaca el menor tráfico intercambiado con el servidor, la utilización de fragments y los gráficos en 3D. Como inconveniente cabe resaltar que la nueva versión solo funciona en el dispositivo con Google Play instalado.

Conviene destacar que, a diferencia de Android, Google Maps no es un software libre, por lo que está limitado a una serie de condiciones de servicio. Podemos usarlo de forma gratuita siempre que nuestra aplicación no solicite más de 25.000 solicitudes geográficas al día [1]. Podemos incluir propaganda en los mapas o incluso podemos usarlo en aplicaciones móviles de pago. Una información más completa de esta API la encontramos en:

  https://developers.google.com/maps/documentation/android/

Obtención de una clave Google Maps

Para poder utilizar este servicio de Google, igual que como ocurre cuando se utiliza desde una página web, será necesario registrar la aplicación que lo utilizará. Tras registrar la aplicación se nos entregará una clave que tendremos que indicar en la aplicación.

Ejercicio: Obtención de una clave Google Maps

1.     Para obtener la clave Google Maps entra en la siguiente página web de administración de APIs de Google:Tendrás que introducir un usuario de Google que realiza la solicitud.

https://code.google.com/apis/console/

2.   Tendrás que introducir un usuario de Google que realiza la solicitud.

3.    Crea un nuevo proyecto en esta consola. Para ello pulsa el botón CREAR PROYECTO, en la parte superior.  Introduce como nombre “Ejemplo Google Maps”  y pulsa en Crear.

4.     A la izquierda aparecerá un menú: selecciona Biblioteca y luego la pestaña APIs de Google. Localiza el elemento “Google Maps Android API” y seleccionalo. Pulsa botón  Habilitar, para indicar que vamos a usar esta API.

5.   Una vez activado, ve a la opción Credenciales del menú de la izquierda y haz clic en el botón Crear credenciales. Selecciona Clave de API:

6.   En la ventana siguiente, copia al portapapeles la clave creada:

Ejercicio: Un ejemplo simple con Google Maps

Veamos un sencillo ejemplo que nos permite visualizar un mapa centrado en las coordenadas geográficas detectadas por el sistema de posicionamiento.

1.      Crea un nuevo proyecto con los siguientes datos:

Application Name: Ejemplo Google Maps

Package Name: org.example.ejemplogooglemaps

Phone and Tablet

    Minimun Required SDK: API 15 Android 4.0.3 (IceCreamSandwich)

Add an activity: Empty Activity

2.      Abre Android SDK Manager y asegúrate que el paquete Google Play services y Google Repositiry están instalados. Si hay una versión más reciente actualizala:

3.   Vamos a importa a nuestro proyectoel paquete de  la libreria de Google Play Services. Para ello añade en el graddle la siguiente línea.

compile 'com.google.android.gms:play-services-maps:10.2.1'

4.      En AndroidManifest.xml,  añade las siguientes líneas dentro de la sección <application>:

<meta-data
     android:name="com.google.android.geo.API_KEY"
     android:value="@string/google_maps_key" />

Crea el siguiente fichero de recurso res/values/google_maps_api.xml:

<resources>
   <string name="google_maps_key" templateMergeStrategy="preserve"
                                                    translatable="false">
      AIzaSyCfcwo2LEBJ11aiW3bxZ9tUujYULXI-GN8
   </string>
</resources>

Reemplaza los caracteres marcados (Aiza…) por la API Key obtenido en el ejercicio anterior.

6.    Añade el siguiente permiso, dentro de la sección <manifest>:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

NOTA: Los permisos de localización no son necesarios para trabajar con Google Maps API2, pero sí los debemos especificar para trabajar con la funcionalidad MyLocation, la cual vamos a utilizar en nuestro ejemplo.

NOTA: Este permiso incluye de forma implícita los permisos de ACCESS_COARSE_LOCATION y de NETWORK_PROVIDER.

7.   Reemplaza el contenido del layout activity_main.xml por:

<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=".MainActivity">
    <fragment
        android:id="@+id/mapa"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.google.android.gms.maps.SupportMapFragment"/>
</RelativeLayout>

NOTA: La etiqueta <fragment> ha de escribirse en minúscula.

8.   Abre MainActivity.class y haz que esta clase herede de FragmentActivity y que implemente la interfaz OnMapReadyCallback:

public class MainActivity extends FragmentActivity
                          implements OnMapReadyCallback

9.   Reemplaza el método OnCreate por el siguiente:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   // Obtenemos el mapa de forma asíncrona (notificará cuando esté listo)
   SupportMapFragment mapFragment = (SupportMapFragment)
                 getSupportFragmentManager().findFragmentById(R.id.mapa);
   mapFragment.getMapAsync(this);
}

10.  Debemos implementar el método OnMapReady que será llamado en el momento qen el que el mapa estádisponible. Es en esta función donde podremos manipular el mapa. Una implementación mínima sería la siguiente:.

@Override
public void onMapReady(GoogleMap googleMap) {
   GoogleMap mapa = googleMap;
   LatLng UPV = new LatLng(39.481106, -0.340987); //Nos ubicamos en la UPV
   mapa.addMarker(new MarkerOptions().position(UPV).title("Marker UPV"));
   mapa.moveCamera(CameraUpdateFactory.newLatLng(UPV));
}

11.  Ejecuta la aplicación. A continuación se muestra el resultado:

NOTA: Es posible que el dispositivo que estés utilizando no tenga instalados los servicios de Google Play. En tal caso, la aplicación te pedirá que los instales:

Ejercicio: Introduciendo código en Google Maps

En En el ejercicio anterior hemos visto un ejemplo muy básico donde solo se mostraba un mapa con las opciones predeterminadas. En este ejercicio aprenderemos a configurarlo y añadir marcadores desde el código.

1. Abre el layout activity_main.xml  y añade los siguientes tres botones dentro del <RelativeLayout> (tras el <fragment …>) :

<Button    android:id="@+id/button1"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_alignParentBottom="true"
           android:layout_toLeftOf="@+id/button2"
           android:onClick="moveCamera"
           android:text="ir a UPV" />
<Button    android:id="@id/button2"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_alignParentBottom="true"
           android:layout_centerHorizontal="true"
           android:onClick="animateCamera"
           android:text="a mi posición" />
<Button    android:id="@+id/button3"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_alignParentBottom="true"
           android:layout_toRightOf="@id/button2"
           android:onClick="addMarker"
           android:text="marcador" />

2.      Sustituye el contenido de MainActivity.java por

public class MainActivity extends FragmentActivity implements
                        OnMapReadyCallback, GoogleMap.OnMapClickListener {
   private GoogleMap mapa;
   private final LatLng UPV = new LatLng(39.481106, -0.340987);

   @Override protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      SupportMapFragment mapFragment = (SupportMapFragment)
                  getSupportFragmentManager().findFragmentById(R.id.mapa);
      mapFragment.getMapAsync(this);
   }

   @Override public void onMapReady(GoogleMap googleMap) {
      mapa = googleMap;
      mapa.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
      mapa.moveCamera(CameraUpdateFactory.newLatLngZoom(UPV, 15));
      mapa.addMarker(new MarkerOptions()
                .position(UPV)
                .title("UPV")
                .snippet("Universidad Politécnica de Valencia")
                .icon(BitmapDescriptorFactory
                        .fromResource(android.R.drawable.ic_menu_compass))
                .anchor(0.5f, 0.5f));
      mapa.setOnMapClickListener(this);
      if (ContextCompat.checkSelfPermission(this,
                android.Manifest.permission.ACCESS_FINE_LOCATION) ==
                PackageManager.PERMISSION_GRANTED) {
         mapa.setMyLocationEnabled(true);
         mapa.getUiSettings().setZoomControlsEnabled(false);
         mapa.getUiSettings().setCompassEnabled(true);
      } else {
         Button btnMiPos=(Button) findViewById(R.id.button2);
         btnMiPos.setEnabled(false);
      }
   }

   public void moveCamera(View view) {
      mapa.moveCamera(CameraUpdateFactory.newLatLng(UPV));
   }

   public void animateCamera(View view) {
      if (mapa.getMyLocation() != null)
         mapa.animateCamera(CameraUpdateFactory.newLatLngZoom(
                    new LatLng(mapa.getMyLocation().getLatitude(),
                               mapa.getMyLocation().getLongitude()), 15));
   }

   public void addMarker(View view) {
      mapa.addMarker(new MarkerOptions().position(
                                      mapa.getCameraPosition().target));
   }

   @Override public void onMapClick(LatLng puntoPulsado) {
      mapa.addMarker(new MarkerOptions().position(puntoPulsado)
                   .icon(BitmapDescriptorFactory
                   .defaultMarker(BitmapDescriptorFactory.HUE_YELLOW)));
   }
}

Comenzamos declarando dos objetos: UPV, que hace referencia a la posición geográfica de la Universidad Politécnica de Valencia y mapa, que nos permitirá acceder al objeto GoogleMap que hemos insertado en un fragment de nuestro Layout. . Con la nueva versión del API el mapa es cargado asíncronamiente, por lo que es necesario implementar el método onMapReady de la interfaz OnMapReadyCallback, que sera llamado en el momento en que mapa esté listo. Será en este método cuando podremos configurar el objeto GoogleMap que nos pasan como parámetro, para adaptarlo a nuestras necesidades. setMapType() permite seleccionar el tipo de mapa (normal, satélite, hibrido o relieve). Para averiguar las constantes correspondientes te recomendamos que utilices la opción de autocompletar (escribe GoogleMap. y podrás seleccionar las constantes de esta clase). El método moveCamera() desplaza el área de visualización a una determinada posición (UPV) a la vez que define el nivel de zoom  (15). El nivel de zoom ha de estar en un rango de 2 (continente) hasta 21 (calle). El método addMarker() permite añadir los típicos marcadores que habrás visto en muchos mapas. En este ejemplo se indica como posición UPV, un título, una descripción, como icono se utiliza el mismo drawable usado como icono de la aplicación y el punto del icono que haremos coincidir con el punto exacto que queremos indicar en el mapa. Un valor de (0, 0) corresponde a la esquina superior izquierda del icono y (1, 1) a la esquina inferior derecha. Como nuestro icono tiene forma de círculo , hemos indicado el valor (0.5, 0.5) para que coincida con su centro. Finalmente, hemos registrado un escuchador de evento para detectar pulsaciones sobre la pantalla. El escuchador vamos a ser nosotros mismos (this), por lo que hemos implementado el interfaz OnMapClickListener y añadido el método onMapClick().

El método setMyLocationEnabled(true) activa la visualización de la posición del dispositivo por medio del típico círculo azul. Para dispositivos con versión 6 o superior hay que tener la precaución de verificar si tenemos permiso de localización, antes de activar esta opción. Por defecto, al tratarse de un permiso catalogado como peligroso se encontrará desactivado. En este código no se solicita este permiso. Para activarlo manualmente has de usar en el dispositivo la opción Ajustes / Aplicaciones / Ejemplo Google Maps / Permisos.   El método getUiSettings() permite configurar las acciones de la interfaz de usuario. En este ejemplo se han utilizado dos: desactivar los botones de zum y visualizar una brújula. Estos métodos sólo están disponibles si la capa LocationLayer está activa, por lo que es recomendable iniciarlos posteriormente al método setMyLocationEnabled(true). Puedes usar autocompletas para descubrir otra posibles configuraciones. En caso de no tener permiso de localización, debemos deshabilitar el botón que nos lleva a nuestra posición

 

A continuación se incluyen los tres métodos que se ejecutarán al pulsar sobre los botones añadidos al layout. El primero, moveCamera(), desplaza el punto de visualización a la UPV. A diferencia del uso anterior, sin cambiar el nivel de zoom que el usuario tenga seleccionado.

El segundo, animateCamera(), nos desplaza hasta nuestra posición actual por medio de una animación (similar a la que a veces utilizan en el TeleDiario para mostrar un punto en conflicto). Observa como el método getMyLocation() permite obtener la posición del dispositivo sin usar la API Android de posicionamiento. Si usas este método verifica siempre que ya se dispone de una posición (!= null) y que has pedido permisos de localización.

El tercero, addMarker(), añade un nuevo marcador en el centro del mapa que estamos observando (getCameraPosition()). En este caso usaremos el marcador por defecto, sin información adicional.

Como hemos indicado onMapClick() será llamado cuando se pulse sobre el mapa. Se pasa como parámetro las coordenadas donde se ha pulsado, que utilizaremos para añadir un marcador. Esta vez el marcador será de color amarillo.

3.      Ejecuta la aplicación. Se muestra el resultado: