Android Dev Summit, October 23-24: two days of technical content, directly from the Android team. Sign-up for livestream updates.

Cómo enviar al usuario a otra app

Una de las funciones más importantes de Android es la capacidad de una app de enviar al usuario a otra app en función de una "acción" que le gustaría realizar. Por ejemplo, si tu app tiene la dirección de una empresa que quieres mostrar en un mapa, no necesitas compilar una actividad en tu app para mostrar el mapa. En su lugar, puedes crear una solicitud para ver la dirección mediante un Intent. Luego, el sistema Android inicia una app que puede mostrar la dirección en un mapa.

Como se explicó en la primera clase, Cómo crear tu primera app, debes utilizar intents para navegar entre las actividades de tu propia app. En general, se hace con un intent explícito, que define el nombre de la clase exacta del componente que quieres iniciar. Sin embargo, cuando quieras que una app separada realice una acción, como "mostrar un mapa", debes usar un intent implícito.

En esta lección, se muestra cómo crear un intent implícito para una acción determinada, y cómo utilizarlo para iniciar una actividad que realice la acción en otra app. También puedes ver el video incorporado aquí para comprender por qué es importante que incluyas comprobaciones del tiempo de ejecución para tus intents implícitos.

Cómo crear un intent implícito

Los intents implícitos no declaran el nombre de clase del componente que se iniciará, en su lugar, declaran una acción que se llevará a cabo. La acción especifica lo que deseas hacer, por ejemplo, ver, editar, enviar o bien obtener algo. Los intents a menudo 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. Según el intent que quieras crear, los datos podrían ser un Uri, uno de varios otros tipos de datos o es posible que el intent no necesite datos.

Si los datos son un Uri, hay un simple constructor Intent() que puedes usar para definir la acción y los datos.

Por ejemplo, a continuación se muestra la manera de crear un intent a fin de iniciar una llamada telefónica usando los datos de un Uri para especificar el número de teléfono:

Kotlin

    val callIntent: Intent = Uri.parse("tel:5551234").let { number ->
        Intent(Intent.ACTION_DIAL, number)
    }
    

Java

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

Cuando tu app invoca este intent llamando a startActivity(), la app de teléfono inicia una llamada al número de teléfono especificado.

Estos son algunos otros intents y su acción y pares de datos de Uri:

  • Ver un mapa:

    Kotlin

        // Map point based on address
        val mapIntent: Intent = Uri.parse(
                "geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California"
        ).let { location ->
            // Or map point based on latitude/longitude
            // Uri location = Uri.parse("geo:37.422219,-122.08364?z=14"); // z param is zoom level
            Intent(Intent.ACTION_VIEW, location)
        }
        

    Java

        // Map point based on address
        Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
        // Or map point based on latitude/longitude
        // Uri location = Uri.parse("geo:37.422219,-122.08364?z=14"); // z param is zoom level
        Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
        
  • Ver una página web:

    Kotlin

        val webIntent: Intent = Uri.parse("http://www.android.com").let { webpage ->
            Intent(Intent.ACTION_VIEW, webpage)
        }
        

    Java

        Uri webpage = Uri.parse("http://www.android.com");
        Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage);
        

Otros tipos de intents implícitos requieren datos "adicionales" que proporcionan diferentes tipos de datos, como una string. Puedes agregar uno o más datos adicionales usando los diferentes métodos putExtra().

De forma predeterminada, el sistema define el tipo de MIME requerido por un intent según los datos de Uri incluidos. Si no se incluye un Uri en el intent, en general, debes usar setType() para especificar el tipo de datos asociados con el intent. Cuando se configura el tipo de MIME también se especifica qué tipos de actividades deben recibir el intent.

A continuación, se incluyen otros intents que agregan datos adicionales para especificar la acción deseada:

  • Enviar un correo electrónico con un archivo adjunto:

    Kotlin

        Intent(Intent.ACTION_SEND).apply {
            // The intent does not have a URI, so declare the "text/plain" MIME type
            type = HTTP.PLAIN_TEXT_TYPE
            putExtra(Intent.EXTRA_EMAIL, arrayOf("jon@example.com")) // recipients
            putExtra(Intent.EXTRA_SUBJECT, "Email subject")
            putExtra(Intent.EXTRA_TEXT, "Email message text")
            putExtra(Intent.EXTRA_STREAM, Uri.parse("content://path/to/email/attachment"))
            // You can also attach multiple items by passing an ArrayList of Uris
        }
        

    Java

        Intent emailIntent = new Intent(Intent.ACTION_SEND);
        // The intent does not have a URI, so declare the "text/plain" MIME type
        emailIntent.setType(HTTP.PLAIN_TEXT_TYPE);
        emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"jon@example.com"}); // recipients
        emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Email subject");
        emailIntent.putExtra(Intent.EXTRA_TEXT, "Email message text");
        emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://path/to/email/attachment"));
        // You can also attach multiple items by passing an ArrayList of Uris
        
  • Crear un evento del calendario:

    Kotlin

        Intent(Intent.ACTION_INSERT, Events.CONTENT_URI).apply {
            val beginTime: Calendar = Calendar.getInstance().apply {
                set(2012, 0, 19, 7, 30)
            }
            val endTime = Calendar.getInstance().apply {
                set(2012, 0, 19, 10, 30)
            }
            putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.timeInMillis)
            putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.timeInMillis)
            putExtra(Events.TITLE, "Ninja class")
            putExtra(Events.EVENT_LOCATION, "Secret dojo")
        }
        

    Java

        Intent calendarIntent = new Intent(Intent.ACTION_INSERT, Events.CONTENT_URI);
        Calendar beginTime = Calendar.getInstance();
        beginTime.set(2012, 0, 19, 7, 30);
        Calendar endTime = Calendar.getInstance();
        endTime.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, "Ninja class");
        calendarIntent.putExtra(Events.EVENT_LOCATION, "Secret dojo");
        

    Nota: Este intent para un evento de calendario se admite solo con la API nivel 14 o un nivel superior.

Nota: Es importante que definas tu Intent para que sea lo más específico posible. Por ejemplo, si quieres mostrar una imagen con el intent ACTION_VIEW, debes especificar un tipo MIME image/*. Esto evita que el intent active las apps que pueden "ver" otros tipos de datos (como una app de mapas).

Cómo comprobar que existe una app para recibir el intent

Si bien la plataforma de Android garantiza que se resolverán ciertos intents con una de las apps integradas (como Teléfono, Correo electrónico o Calendario), siempre debes incluir un paso de verificación antes de invocar un intent.

Precaución: Si invocas un intent y no hay ninguna app disponible en el dispositivo que pueda controlarlo, se detendrá la app.

Para verificar que haya una actividad disponible que pueda responder al intent, llama a queryIntentActivities() a fin de obtener una lista de actividades capaces de controlar el Intent. Si no se muestra List, puedes usar de forma segura el intent. Por ejemplo:

Kotlin

    val activities: List<ResolveInfo> = packageManager.queryIntentActivities(
            intent,
            PackageManager.MATCH_DEFAULT_ONLY
    )
    val isIntentSafe: Boolean = activities.isNotEmpty()
    

Java

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

Si isIntentSafe es true, entonces al menos una app responderá al intent. Si es false, entonces no hay apps para controlar el intent.

Nota: Debes realizar esta comprobación cuando se inicie por primera vez tu actividad, en caso de que necesites inhabilitar la función que utiliza el intent antes de que el usuario trate de utilizarla. Si conoces una app específica que pueda controlar el intent, proporciona un vínculo para que el usuario la descargue (consulta cómo vincular tu producto en Google Play).

Cómo iniciar una actividad con el intent

Figura 1: Ejemplo del diálogo de selección que aparece cuando más de una app puede controlar un intent

Una vez que hayas creado tu Intent y hayas configurado la información adicional, llama a startActivity() para enviarlo al sistema. Si el sistema identifica más de una actividad que puede controlar el intent, se mostrará un diálogo (a veces denominado "diálogo de desambiguación") para que el usuario seleccione qué app usar, como se muestra en la figura 1. Si existe una única actividad que puede manejar el intent, el sistema la inicia de inmediato.

Kotlin

    startActivity(intent)
    

Java

    startActivity(intent);
    

A continuación, se incluye un ejemplo completo que muestra cómo crear un intent para ver un mapa, verificar que exista una app para controlar el intent y luego iniciarlo:

Kotlin

    // Build the intent
    val location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California")
    val mapIntent = Intent(Intent.ACTION_VIEW, location)

    // Verify it resolves
    val activities: List<ResolveInfo> = packageManager.queryIntentActivities(mapIntent, 0)
    val isIntentSafe: Boolean = activities.isNotEmpty()

    // Start an activity if it's safe
    if (isIntentSafe) {
        startActivity(mapIntent)
    }
    

Java

    // Build the intent
    Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
    Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);

    // Verify it resolves
    PackageManager packageManager = getPackageManager();
    List<ResolveInfo> activities = packageManager.queryIntentActivities(mapIntent, 0);
    boolean isIntentSafe = activities.size() > 0;

    // Start an activity if it's safe
    if (isIntentSafe) {
        startActivity(mapIntent);
    }
    

Cómo mostrar un selector de apps

Figura 2: Diálogo de selector

Ten presente que si inicias una actividad pasando tu Intent a startActivity() y existe más de una app que responde al intent, el usuario podrá elegir la app que se usará de forma predeterminada (seleccionando la casilla de verificación de la parte inferior del diálogo; consulta la figura 1). Esto es útil cuando se realiza una acción para la cual el usuario generalmente quiere utilizar siempre la misma app, por ejemplo, cuando se abre una página web (los usuarios suelen usar un solo navegador web) o se toma una fotografía (suelen preferir una cámara).

Sin embargo, si la acción que se realizará se puede controlar mediante varias apps y el usuario quizás prefiere utilizar una diferente cada vez, por ejemplo una acción de "compartir" un elemento, debes mostrar explícitamente un diálogo de selector, como se indica en la figura 2. El diálogo de selector obliga al usuario a seleccionar qué app quiere utilizar para la acción en cada caso (no puede elegir una app predeterminada para la acción).

Para mostrar el selector, crea un Intent utilizando createChooser() y pásalo a startActivity(). Por ejemplo:

Kotlin

    val intent = Intent(Intent.ACTION_SEND)
    ...

    // Always use string resources for UI text.
    // This says something like "Share this photo with"
    val title = resources.getString(R.string.chooser_title)
    // Create intent to show chooser
    val chooser = Intent.createChooser(intent, title)

    // Verify the intent will resolve to at least one activity
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(chooser)
    }
    

Java

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

    // Always use string resources for UI text.
    // This says something like "Share this photo with"
    String title = getResources().getString(R.string.chooser_title);
    // Create intent to show chooser
    Intent chooser = Intent.createChooser(intent, title);

    // Verify the intent will resolve to at least one activity
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(chooser);
    }
    

Aquí se muestra un diálogo con una lista de apps que responden al intent pasado al método createChooser(), con el texto proporcionado como título del diálogo.