Enviando al usuario a otra aplicación

Una de las características más interesantes de Android es la capacidad que tienen las aplicaciones de enviar al usuario a otra aplicación basándose en una "acción" que desee llevar a cabo. Por ejemplo, si tienes una dirección de un negocio que quieres mostrar en un mapa, no necesitas implementar una actividad para mostrar mapas. En su lugar puedes solicitar ver la dirección en un mapa creando un Intent. El sistema Android se encargará de iniciar una aplicación capaz de mostrar mapas.

Como explicamos en la primera clase, Construyendo tu primera aplicación, se usan intenciones para navegar entre actividades de una aplicación. Normalmente se utiliza una intención explícita, que indica el nombre de la clase del componente que queremos iniciar. Sin embargo, si queremos que la acción se lleve a cabo usando otra aplicación, como en el caso de "ver un mapa," tenemos que usar una intención implícita.

Esta lección te enseñará a crear una intención implícita para una acción determinada, y como usar esta intención para iniciar una actividad de otra aplicación con la que llevar a cabo la acción.

Crear una intención implícita


Las intenciones implícitas no indican el nombre de la clase del componente a iniciar, indican una acción a llevar a cabo. La acción especifica lo que queremos hacer, como ver, editar, enviar, u obtener algo. A menudo las intenciones también incluyen datos asociados con la acción, como la dirección que quieres ver, o el mensaje de correo electrónico que quieres enviar. Dependiendo de la intención que quieras crear podemos añadir una Uri, algún otro tipo de dato, o puede que no se necesite ningún dato.

Si la información necesaria es una Uri, podemos utilizar el constructor de la clase Intent() pasando la acción y el dato.

Por ejemplo, así es como crearíamos una intención con la que iniciar una llamada de teléfono usando una Uri para especificar el número de teléfono:

Uri number = Uri.parse("tel:5551234");
Intent callIntent = new Intent(Intent.ACTION_DIAL, number);

Cuando tu aplicación invoca esta intención llamando a startActivity(), la aplicación de Teléfono inicia una llamada al número de teléfono especificado.

A continuación se muestra un par de ejemplos más de intenciones que usan datos tipo Uri:

  • Ver un mapa:
    // Punto en el mapa basándose en la dirección
    Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
    // O punto en el mapa basándose en latitud/longitud
    // Uri location = Uri.parse("geo:37.422219,-122.08364?z=14"); // el parámetro z es el nivel de zoom
    Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
    
  • Ver una página web:
    Uri webpage = Uri.parse("http://www.android.com");
    Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage);
    

Para otras intenciones implícitas necesitaremos pasar otros tipos de datos, como cadenas de texto. Podemos añadir uno o más datos extra usando los distintos métodos putExtra().

Por defecto, el sistema determina el tipo MIME requerido por la intención basándose en la Uri asociada. Si la intención no tiene una Uri asociada, deberías usar setType() para especificar el tipo de datos de la intención. Establecer el tipo MIME ayuda a precisar las actividades que pueden recibir la intención.

A continuación se muestran algunas intenciones que utilizan datos extra:

  • Enviar un correo electrónico con un archivo adjunto:
    Intent emailIntent = new Intent(Intent.ACTION_SEND);
    // La intención no tiene una URI, por lo que declaramos el tipo MIME "text/plain"
    emailIntent.setType(HTTP.PLAIN_TEXT_TYPE);
    emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"jon@example.com"}); // destinatarios
    emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Asunto del correo");
    emailIntent.putExtra(Intent.EXTRA_TEXT, "Texto del mensaje del correo");
    emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://ruta/al/archivo/adjunto"));
    // También puedes adjuntar múltiples elementos pasando un ArrayList de Uris
    
  • Crear un evento de calendario:
    Intent calendarIntent = new Intent(Intent.ACTION_INSERT, Events.CONTENT_URI);
    Calendar beginTime = Calendar.getInstance().set(2012, 0, 19, 7, 30);
    Calendar endTime = Calendar.getInstance().set(2012, 0, 19, 10, 30);
    calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis());
    calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis());
    calendarIntent.putExtra(Events.TITLE, "Clase Ninja");
    calendarIntent.putExtra(Events.EVENT_LOCATION, "Dojo secreto");
    

    Nota: Esta intención para crear un evento de calendario sólo está disponible en el nivel API 14 y superior.

Nota: Es importante que tu Intent sea tan específico como sea posible. Por ejemplo, si quieres mostrar una imagen usando la intención ACTION_VIEW, deberías especificar un tipo MIME image/*. Esto previene que otras aplicaciones que puedan "ver" otros tipos de datos (como una aplicación de mapas) puedan ser lanzadas por error usando la intención.

Verificar que exista una aplicación para recibir la intención


Aunque la plataforma Android garantiza que ciertas intenciones siempre se podrán ejecutar gracias a las aplicaciones incluídas por defecto (como la aplicación de Teléfono, Correo, o Calendario), siempre deberías incluir un paso de verificación antes de invocar una intención.

Precaución: Si invocas una intención y no existe ninguna aplicación capaz de gestionarla en el dispositivo, la aplicación fallará.

Para verificar que exista alguna actividad capaz de responder a la intención, llama a queryIntentActivities() para obtener una lista de actividades capaces de manejar tu Intent. Si el objeto List devuelto no está vacío, puedes usar la intención de forma segura. Por ejemplo:

PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
boolean isIntentSafe = activities.size() > 0;

Si isIntentSafe es true, entonces al menos una aplicación puede responder a la intención. Si es false, entonces no existe ninguna aplicación que pueda gestionar la intención.

Nota: Deberías hacer esta comprobación cuando se inicie tu actividad, para deshabilitar la funcionalidad que usa la intención antes de que el usuario intente usarla. Si conoces alguna aplicación específica capaz de manejar la intención, también puedes proporcionar un enlace para que el usuario descargue la aplicación (lee acerca de cómo enlazar tu producto en Google Play).

Iniciar una actividad con la intención


Figura 1. Ejemplo del diálogo de selección que aparece cuando existe más de una aplicación que puede gestionar una intención.

Una vez hayas creado tu Intent y añadido los datos extra, llama a startActivity() para enviarla al sistema. Si el sistema identifica más de una actividad que puede manejar la intención, este mostrará un diálogo para que el usuario seleccione la aplicación a utilizar, tal y como se muestra en la figura 1. Si existe una única actividad capaz de manejar la intención, el sistema la iniciará inmediatamente.

startActivity(intent);

A continuación tenemos un ejemplo completo que crea una intención para ver un mapa, verifica que existe una aplicación para gestionar la intención, y luego la inicia:

// Construimos la intención
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);

// Verificamos que exista alguna actividad apropiada
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(mapIntent, 0);
boolean isIntentSafe = activities.size() > 0;

// Si es seguro, iniciamos la actividad
if (isIntentSafe) {
    startActivity(mapIntent);
}

Mostrar un diálogo de selección de aplicaciones


Figura 2. Ejemplo del diálogo de selección que aparece cuando usas createChooser() para asegurarte de que siempre se muestre al usuario la lista de aplicaciones que se corresponden con la intención.

Observa que cuando inicias una actividad pasando tu Intent a startActivity() y hay más de una aplicación que puede responder a la intención, el usuario puede seleccionar una aplicación para usar por defecto (marcando la casilla de la parte inferior del diálogo; ver figura 1). Esto es útil cuando el usuario quiere utilizar siempre la misma aplicación para la acción, como al abrir una página web (normalmente los usuarios sólo usan un navegador) o tomar una fotografía (normalmente los usuarios usan una única aplicación de cámara). Sin embargo, si la acción puede ser llevada a cabo por múltiples aplicaciones y el usuario puede querer usar una aplicación distinta cada vez—como una acción de "compartir", para la cual los usuarios pueden tener varias aplicaciones a las que querer enviar un elemento—deberías mostrar un diálogo de selección de manera explícita, forzando al usuario a seleccionar cada vez qué aplicación quiere utilizar para la acción (en este caso el usuario no puede seleccionar una aplicación por defecto).

Para mostrar el diálogo de selección, crea un Intent usando createChooser() y pásaselo a startActivity(). Por ejemplo:

Intent intent = new Intent(Intent.ACTION_SEND);
...

// Usa siempre recursos de cadena para los textos de la interfaz. En este caso usaríamos algo como "Compartir esta foto con"
String title = getResources().getText(R.string.chooser_title);
// Crea e inicia el diálogo de selección
Intent chooser = Intent.createChooser(intent, title);
startActivity(chooser);

Esto mostraría un diálogo con la lista de aplicaciones que pueden responder a la intención pasada al método createChooser() con el texto pasado como título del diálogo.