Cómo integrar Acciones en apps con widgets de Android

Figura 1: Uso de un widget para GET_EXERCISE_OBSERVATION

Para muchos intents, la mejor respuesta consiste en brindar a los usuarios una respuesta simple, una confirmación breve o una experiencia interactiva rápida. Puedes mostrar un widget de apps para Android en Asistente de Google a fin de cumplir con estos tipos de intents.

En esta guía, se explica el modo en que se entregan las consultas de los usuarios de Asistente con widgets y cómo mejorar la experiencia del widget para Asistente mediante la biblioteca de extensiones de widgets de Acciones en apps.

Ventajas

Los widgets son vistas en miniatura de una aplicación que se pueden incorporar en plataformas de Android, como el selector o la pantalla de bloqueo. Con las Acciones en apps, puedes aumentar el impacto de tus widgets de modo que resulten aptos para mostrarse en Asistente:

  1. Descubrimiento: Muestra widgets de manera proactiva en respuesta a consultas de los usuarios en lenguaje natural.
  2. Participación: Muestra widgets en contextos de manos libres, como cuando Asistente brinda los resultados personales en la pantalla de bloqueo y en Android Auto.
  3. Retención: Permite que los usuarios fijen en su selector los widgets que se muestran en Asistente. La función de fijar requiere la Biblioteca de Widgets Extension.

Cómo muestra widgets Asistente

Existen dos maneras en las que los usuarios pueden invocar widgets en Asistente:

  • Pueden solicitar explícitamente un widget por su nombre.
  • Pueden realizar una consulta al Asistente que activa un intent integrado (BII) o un intent personalizado configurados para la entrega de widgets.

Invocación explícita

Para invocar widgets de manera explícita con cualquier app instalada, los usuarios pueden pedirle al Asistente cosas como las siguientes:

  • "Hey Google, muestra el widget de AppDeEjemplo".
  • "Widgets de AppDeEjemplo".

Asistente muestra estos widgets con la introducción genérica: "AppDeEjemplo dice, aquí hay un widget". Si bien Asistente muestra widgets de esta manera de forma nativa, sin necesidad de que el desarrollador de apps realice el trabajo, con este método de invocación, el usuario debe tener conocimiento explícito del widget que desea solicitar. Para simplificar el descubrimiento de widgets, usa el método de entrega de intents que se describe en la siguiente sección.

Entrega de intents

Haz que sea más fácil encontrar los widgets utilizándolos para entregar las consultas de lenguaje natural que los usuarios realizan en Asistente. Por ejemplo, puedes mostrar un widget cada vez que un usuario active el BII GET_EXERCISE_OBSERVATION en tu app de fitness. Para ello, pregunta "Hey Google, ¿cuántas millas corrí esta semana en AppDeEjemplo?". Además de simplificar el descubrimiento, integrar estos widgets en Acciones en apps ofrece las siguientes ventajas:

  • Acceso a parámetros: Asistente proporciona a tu widget los parámetros de intent extraídos de la consulta del usuario, lo que habilita las respuestas personalizadas.
  • Presentaciones personalizadas de TTS: Puedes brindar una string de texto a voz (TTS) de modo que Asistente la anuncie cuando muestre tu widget.
  • Fijación de widgets: Asistente muestra un botón para agregar el widget cerca de él, lo que permite a los usuarios fijar con facilidad tus widgets en su selector.

Cómo implementar widgets

Si deseas implementar la entrega de widgets para tus intents, sigue estos pasos:

  1. Implementa un widget de Android con los pasos que se describen en el artículo para crear un widget simple.
  2. En el archivo de recursos shortcuts.xml de tu app, agrega un elemento <app-widget> a tu función que contenga detalles de entrega y etiquetas <parameter> del BII. Actualiza tu widget para controlar los parámetros.
  3. (Obligatorio) Agrega la biblioteca de Widgets Extension, que permite que Asistente pase nombres y parámetros de BII a tus widgets. También habilita presentaciones de TTS personalizadas y la funcionalidad de fijación de widgets.

En la siguiente sección, se describe el esquema de <app-widget> para el archivo shortcuts.xml.

Esquema de widgets

Los elementos <app-widget> se definen como entregas dentro de elementos <capability> en el archivo shortcuts.xml. Requieren los siguientes atributos, a menos que se indique que son opcionales:

Etiqueta Shortcuts.xmlDónde se incluyeAtributos
<app-widget> <capability>
  • android:identifier
  • android:targetClass
<parameter> <app-widget>
<extra> <app-widget>
  • android:name (solo se aplica a TTS)
  • android:value (opcional)

Descripción del esquema de widgets

<app-widget>

Es el elemento de entrega de widgets de nivel superior.

Atributos:

  • android:identifier: Es el identificador único de esta entrega. Este valor debe ser único entre los elementos de entrega <app-widget> y <intent> definidos en una <capability>.
  • android:targetClass: Es el nombre completo de la clase de tu AppWidgetProvider que controlará el intent.

<parameter>

Esta etiqueta asigna un parámetro BII a un valor de intent de <parameter>. Puedes definir cero o más parámetros para cada elemento <app-widget>. Durante la entrega, Asistente pasa los parámetros con la actualización de los elementos adicionales de la instancia del widget como pares clave-valor con el siguiente formato:

  • Clave: El android:key definido para el parámetro
  • Valor: El valor que extrae el BII de la entrada de voz del usuario

Para acceder a estos elementos adicionales, llama a getAppWidgetOptions() en el objeto AppWidgetManager asociado que muestra un Bundle que contiene el nombre del BII de activación y sus parámetros. Consulta la información para extraer valores de parámetros a fin de obtener más detalles.

Si deseas obtener más información sobre la coincidencia de parámetros de BII, consulta Datos de parámetros y coincidencias.

<extra>

Es una etiqueta opcional que declara que se debe usar una presentación personalizada de TTS para este widget. Esta etiqueta requiere los siguientes valores del atributo:

  • android:name: "hasTts"
  • android:value: "true"

Código de muestra

En el siguiente XML, se muestra una configuración de entrega de widgets para una función de BII GET_EXERCISE_OBSERVATION:

shortcuts.xml

<capability android:name="actions.intent.GET_EXERCISE_OBSERVATION">
  <app-widget
    android:identifier="GET_EXERCISE_OBSERVATION_1"
    android:targetClass="com.exampleapp.providers.exampleAppWidgetProvider"
    android:targetPackage="com.exampleapp">
    <parameter
      android:name="exerciseObservation.aboutExercise.name"
      android:key="exercisename">
    </parameter>
    <extra android:name="hasTts" android:value="true"/>
  </app-widget>
</capability>

Puedes especificar varios elementos <app-widget> o usar una combinación de elementos <app-widget> y <intent> por función. Este enfoque te permite brindar una experiencia personalizada basada en diferentes combinaciones de parámetros proporcionados por los usuarios. Por ejemplo, si el usuario no especifica una ubicación de destino en la consulta, puedes dirigirlo a la actividad de tu app que muestra opciones para configurar las ubicaciones de recogida y llegada. Consulta la sección Intents de resguardo a fin de obtener más información para definir intents de resguardo.

Cómo extraer valores de parámetros

En la siguiente clase de AppWidgetProvider de muestra, se usa la función privada updateAppWidget() para extraer el nombre y los parámetros del BII a partir del Bundle de opciones del widget:

Kotlin

package com.example.exampleapp

//... Other module imports
import com.google.assistant.appactions.widgets.AppActionsWidgetExtension

/**
 * Implementation of App Widget functionality.
 */
class MyAppWidget : AppWidgetProvider() {
    override fun onUpdate(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetIds: IntArray
    ) {
        // There may be multiple widgets active, so update all of them
        for (appWidgetId in appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId)
        }
    }

    private fun updateAppWidget(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetId: Int
    ) {
        val widgetText: CharSequence = context.getString(R.string.appwidget_text)

        // Construct the RemoteViews object
        val views = RemoteViews(context.packageName, R.layout.my_app_widget)
        views.setTextViewText(R.id.appwidget_text, widgetText)

        // Extract the name and parameters of the BII from the widget options.
        val optionsBundle = appWidgetManager.getAppWidgetOptions(appWidgetId)
        val bii = optionsBundle.getString(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_BII) // "actions.intent.CREATE_TAXI_RESERVATION"
        val params = optionsBundle.getBundle(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_PARAMS)
        if (params != null && params.containsKey("dropoff")) {
            val dropoffLocation = params.getString("dropoff")
            // Build your RemoteViews with the extracted BII parameter
            // ...
        }
        appWidgetManager.updateAppWidget(appWidgetId, views)
    }
}

Java

package com.example.exampleapp;

//... Other module imports
import com.google.assistant.appactions.widgets.AppActionsWidgetExtension;

/**
 * Implementation of App Widget functionality.
 */
public class MyAppWidget extends AppWidgetProvider {

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // There may be multiple widgets active, so update all of them
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }

    private static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {

        CharSequence widgetText = context.getString(R.string.appwidget_text);

        // Construct the RemoteViews object
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.my_app_widget);
        views.setTextViewText(R.id.appwidget_text, widgetText);

        // Extract the name and parameters of the BII from the widget options.
        Bundle optionsBundle = appWidgetManager.getAppWidgetOptions(appWidgetId);
        String bii =
                optionsBundle.getString(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_BII); // "actions.intent.CREATE_TAXI_RESERVATION"
        Bundle params =
                optionsBundle.getBundle(AppActionsWidgetExtension.EXTRA_APP_ACTIONS_PARAMS);

        if (params != null && params.containsKey(("dropoff"))){
            String dropoffLocation = params.getString("dropoff");
            // Build your RemoteViews with the extracted BII parameter
            // ...
        }

        appWidgetManager.updateAppWidget(appWidgetId, views);
    }
}

Biblioteca de Widgets Extension

La Biblioteca de Widgets Extension de Acciones en apps mejora tus widgets para las experiencias de Asistente de voz. Esta biblioteca permite que tus widgets reciban información de entrega importante del BII de activación, incluido el nombre del BII y cualquier parámetro del intent extraído de la consulta del usuario. Esta biblioteca de Maven te permite brindar una presentación personalizada de texto a voz (TTS) para cada widget, lo que le permite al Asistente anunciar un resumen del contenido que se renderiza visualmente para los usuarios. También habilita la fijación de selector, lo que facilita a los usuarios guardar los widgets que se muestran en Asistente en sus pantallas de selector.

Para comenzar, agrega la biblioteca en la sección de dependencias del archivo build.gradle del módulo de tu app:

app/build.gradle

dependencies {
    //...
    implementation "com.google.assistant.appactions:widgets:0.0.1"
}

Presentaciones personalizadas

Después de importar la biblioteca de extensiones de widgets, puedes proporcionar presentaciones personalizadas de TTS para tus widgets. Para agregar tu definición al AppWidgetProvider del widget, abre la clase en tu IDE y, luego, importa la biblioteca de extensiones de widgets:

Kotlin

import com.google.assistant.appactions.widgets.AppActionsWidgetExtension

Java

import com.google.assistant.appactions.widgets.AppActionsWidgetExtension;
Luego, usa la biblioteca para definir tus strings de presentación y actualizar el widget:
WidgetDeAppDeEjemplo

Kotlin

package com.example.exampleapp

//... Other module imports
import com.google.assistant.appactions.widgets.AppActionsWidgetExtension

/**
 * Implementation of App Widget functionality.
 */
object MyAppWidget : AppWidgetProvider() {
    fun updateAppWidget(
        context: Context?,
        appWidgetManager: AppWidgetManager,
        appWidgetId: Int
    ) {
        val appActionsWidgetExtension = AppActionsWidgetExtension.newBuilder(appWidgetManager)
            .setResponseSpeech("Hello world") // TTS to be played back to the user.
            .setResponseText("Hello world!") // Response text to be displayed in Assistant.
            .build()

        // Update widget with TTS.
        appActionsWidgetExtension.updateWidget(appWidgetId)

        // Update widget UI.
        appWidgetManager.updateAppWidget(appWidgetId, views)
    }
}

Java

package com.example.exampleapp;

//... Other module imports
import com.google.assistant.appactions.widgets.AppActionsWidgetExtension;

/**
 * Implementation of App Widget functionality.
 */
public class MyAppWidget extends AppWidgetProvider {

  static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
    int appWidgetId) {

    AppActionsWidgetExtension appActionsWidgetExtension = AppActionsWidgetExtension.newBuilder(appWidgetManager)
      .setResponseSpeech("Hello world")  // TTS to be played back to the user.
      .setResponseText("Hello world!")  // Response text to be displayed in Assistant.
      .build();

      // Update widget with TTS.
      appActionsWidgetExtension.updateWidget(appWidgetId);

      // Update widget UI.
      appWidgetManager.updateAppWidget(appWidgetId, views);
    }

}

Fijación de selectores

Esta biblioteca permite que se muestre el botón para agregar el widget con tu widget en Asistente. La fijación requiere incorporar la siguiente definición del receptor a AndroidManifest.xml:

AndroidManifest.xml

<application>
  <receiver android:name="com.google.assistant.appactions.widgets.pinappwidget.PinAppWidgetBroadcastReceiver"
    android:exported="false">
    <intent-filter>
      <action android:name="com.google.assistant.appactions.widgets.COMPLETE_PIN_APP_WIDGET" />
    </intent-filter>
  </receiver>
  <service
    android:name=
    "com.google.assistant.appactions.widgets.pinappwidget.PinAppWidgetService"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
      <action
        android:name="com.google.assistant.appactions.widgets.PIN_APP_WIDGET" />
    </intent-filter>
  </service>
</application>

Disponibilidad de inventario

Los BIIs que admiten inventario intercalado o inventario web pueden extender estos inventarios a tus entregas de widgets.

Inventario intercalado

En la siguiente muestra de código, se incluye una función de BII START_EXERCISE configurada para el inventario intercalado y la entrega de widgets:

shortcuts.xml

<capability
  android:name="actions.intent.START_EXERCISE">
  <app-widget
    android:identifier="START_EXERCISE_1"
    android:targetClass="com.example.exampleapp.StartExerciseAppWidgetProvider">
    <parameter
      android:name="exercise.name"
      android:key="exerciseName"
      app:shortcutMatchRequired="true">
    </parameter>
  </app-widget>
</capability>

<shortcut android:shortcutId="RunningShortcut">
  <intent
    android:action="android.intent.action.VIEW"
    android:targetClass="com.example.exampleapp.StartExcerciseActivity" />
  <capability-binding
    android:capability="actions.intent.START_EXERCISE"
    android:parameter="exercise.name"
    android:value="running;runs" />
</shortcut>

En el ejemplo anterior, cuando un usuario pide al Asistente: "Comienza a ejecutar con AppDeEjemplo". El paquete de opciones para la entrega de <app-widget> contendrá el siguiente par clave-valor:

  • Clave = “exerciseName”
  • Valor = “RunningShortcut”

Inventario web

Consulta el siguiente código de muestra a fin de ver una función habilitada para el inventario web y la entrega de widgets:

shortcuts.xml

<shortcuts>
  <capability
    android:name="actions.intent.START_EXERCISE">
    <app-widget
      android:identifier="START_EXERCISE_1"
      android:targetClass="com.example.exampleapp.CreateTaxiAppWidgetProvider">
      <parameter
        android:name="exercise.name"
        android:key="exerciseName"
        android:mimeType="text/*">
        <data android:pathPattern="https://exampleapp.com/exercise/.*" />
      </parameter>
    </app-widget>
  </capability>
</shortcuts>

Pruebas

Usa la herramienta de pruebas de Acciones en apps, una función del complemento de Asistente de Google para Android Studio, a fin de probar widgets en un dispositivo físico o virtual. Para usar esa herramienta, sigue estos pasos:

  1. Conecta el dispositivo de prueba a la app en ejecución.
  2. En Android Studio, ve a Tools > App Actions > App Actions Test Tool.
  3. Haz clic en Create Preview.
  4. En Android Studio, ejecuta la app en tu dispositivo de prueba.
  5. Usa la app de Asistente en tu dispositivo de prueba para probar la Acción en la app. Por ejemplo, puedes decir "Hey Google, ¿cuántos kilómetros corrí esta semana en AppDeEjemplo?".
  6. Observa el comportamiento de tu app o usa el depurador de Android Studio a fin de verificar el resultado de la acción que deseas.

Lineamientos de calidad

En esta sección, se destacan los requisitos fundamentales y las prácticas recomendadas para la integración de Acciones en apps con widgets.

Contenido en widgets

  • No muestres anuncios en tus widgets. (Obligatorio)
  • El contenido del widget debe enfocarse por completo en entregar el intent: no intentes entregar varios intents con un widget ni agregues contenido irrelevante.

Cómo controlar la autenticación

  • En los casos en que se necesite la autenticación para completar un flujo de usuarios, muestra un widget que explique que el usuario debe continuar en la app. La autenticación intercalada de usuarios en Asistente de Google no es compatible con Acciones en apps. (Obligatorio)
  • Si los usuarios permiten que tu app muestre datos a través de widgets, puedes mostrar un widget de error en el tiempo de ejecución a aquellos usuarios no autorizados.

Intents de resguardo

  • En tu archivo shortcuts.xml, siempre proporciona un <intent> de resguardo además de la entrega de widgets para una función determinada. (Obligatorio) Un intent de resguardo es un elemento <intent> sin valores de <parameter> obligatorios. Esto permite que Asistente realice una Acción cuando la consulta del usuario no contenga parámetros requeridos por los otros elementos de entrega definidos en la función. La excepción se da cuando no hay parámetros obligatorios para esa función, en cuyo caso solo se necesita la entrega del widget.
  • El intent de resguardo debe abrir tu app en la pantalla que resulte relevante, no en la principal.

En el siguiente código de muestra, se incluye una <capability> con un <intent> de resguardo que admite una entrega principal de <app-widget>:

shortcuts.xml

<shortcuts>
  <capability
    android:name="actions.intent.CREATE_TAXI_RESERVATION">
    <!-- Widget with required parameter, specified using the "android:required" attribute. -->
    <app-widget
      android:identifier="CREATE_TAXI_RESERVATION_1"
      android:targetClass="com.example.myapplication.CreateTaxiAppWidgetProvider">
      <parameter
        android:name="taxiReservation.dropoffLocation.name"
        android:key="dropoff"
        android:required="true">
      </parameter>
    </app-widget>
    <!-- Fallback intent with no parameters required to successfully execute. -->
    <intent
      android:identifier="CREATE_TAXI_RESERVATION_3"
      android:action="myapplication.intent.CREATE_TAXI_RESERVATION_1"
      android:targetClass="com.example.myapplication.TaxiReservationActivity">
    </intent>
  </capability>
</shortcuts>

Divulgación de datos de Google Play

En esta sección, se muestra la recopilación de datos del usuario final de la versión más reciente de la Biblioteca de Widgets Extension.

Este SDK envía respuestas de texto a voz (TTS) proporcionadas por el desarrollador, que Asistente de Google anuncia al usuario con la tecnología de voz de Asistente. Google no almacena esta información.

Las Acciones en apps también pueden recopilar metadatos de la app cliente para los siguientes fines:

  • Supervisar las tasas de adopción de diferentes versiones del SDK
  • Cuantificar el uso de las funciones del SDK en las apps