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 para entregar estos tipos de intents.

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

Beneficios

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ántos kilómetros 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 cadena 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.

Implementa la entrega de 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. Agrega la biblioteca de Widgets Extension requerida, 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 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>

Etiqueta opcional que declara que se usa una introducción personalizada a 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 ejemplo de un archivo shortcuts.xml, se muestra una configuración de entrega de widgets para una función de BII GET_EXERCISE_OBSERVATION:

<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 might 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 might 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:

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;
A continuación, usa la biblioteca para definir tus cadenas de introducción y actualizar el widget, como se muestra en este widget de una app de ejemplo:

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);
    }

}

Recomendaciones de estilo de TTS

Usa las siguientes recomendaciones de estilo para optimizar las introducciones de widgets personalizados para TTS y mensajes visibles.

Recomendación Se recomienda No se recomienda
Sencillez
Usa expresiones sencillas en mensajes de TTS. Las expresiones más enrevesadas suenan forzadas y robóticas en lugar de naturales y familiares. Decir frases como "no es posible" y "no se ha podido" puede sonar rígido y severo.
ResponseSpeech (TTS)
Lo siento, no puedo encontrar una reserva.

ResponseText
Lo siento, no puedo encontrar una reserva.
ResponseSpeech (TTS)
Lo siento, no se ha podido encontrar una reserva.

ResponseText
Lo siento, no se ha podido encontrar una reserva.
Comas
Utiliza la coma de enumeración en listas de tres o más elementos para agregar claridad. Sin la coma de enumeración, los elementos individuales de la lista pueden escucharse de manera incorrecta o leerse como grupos. Por ejemplo, en "narcisos, margaritas y girasoles", "margaritas y girasoles" suenan como si se unieran. En "narcisos, margaritas y girasoles", los tres están claramente separados.
ResponseSpeech (TTS)
Nuestras flores más populares son las rosas amarillas, los narcisos, las margaritas, y los girasoles.

ResponseText
Nuestras flores más populares son las rosas amarillas, los narcisos y las margaritas, y los girasoles.
ResponseSpeech (TTS)
Nuestras flores más populares son las rosas amarillas, los narcisos y las margaritas y los girasoles.

ResponseText
Nuestras flores más populares son las rosas amarillas, los narcisos y las margaritas y los girasoles.
Numerales
Usa números en lugar de texto para que el contenido visual sea más claro.
ResponseSpeech (TTS)
Tu presión arterial es 100 80.

ResponseText
Tu presión arterial es 100/80.
ResponseSpeech (TTS)
Tu presión arterial es 100/80.

ResponseText
Tu presión arterial es cien ochenta.
Símbolos
Usa símbolos especializados en lugar de texto para que el contenido visual sea más claro.
ResponseSpeech (TTS)
Tu última compra fue de USD 24.65.

ResponseText
Tu última compra fue de USD 24.65.
ResponseSpeech (TTS)
Tu última compra fue de veinticuatro dólares y sesenta y cinco centavos.

ResponseText
Tu última compra fue de veinticuatro dólares y sesenta y cinco centavos.
Evita el exceso de educación
El exceso de educación hace que las respuestas suenen distantes y formales. Evítalo y mantén una conversación informal y amigable.
ResponseSpeech (TTS)
Se entregó tu pedido.

ResponseText
Se entregó tu pedido.
ResponseSpeech (TTS)
Por supuesto, puedo decirte eso. Se entregó tu pedido.

ResponseText
Por supuesto, puedo decirte eso. Se entregó tu pedido.
Evita los signos de exclamación
Pueden percibirse como gritos.
ResponseSpeech (TTS)
Hoy corriste 2.4 kilómetros.

ResponseText
Hoy corriste 2.4 kilómetros.
ResponseSpeech (TTS)
¡Hoy corriste 2.4 kilómetros!

ResponseText
¡Hoy corriste 2.4 kilómetros!
Hora
Usa números: "5:15" en lugar de "cinco quince" o "quince después de cinco". Para el reloj de 12 horas, usa a.m. o p.m.
ResponseSpeech (TTS)
La entrega debería llegar a las 8:15 a.m.

ResponseText
La entrega debería llegar a las 8:15 a.m.
ResponseSpeech (TTS)
La entrega debería llegar hoy a las 8 y 15 de la mañana.

ResponseText
La entrega debería llegar hoy a las 8 y 15 de la mañana.
No hagas monólogos
Brinda la información, pero asegúrate de que las respuestas sean concisas. No desarrolles detalles que no ofrezcan un beneficio claro para el usuario.
ResponseSpeech (TTS)
El mes pasado consumiste 159 horas de energía.

ResponseText
El mes pasado consumiste 159 horas de energía.
ResponseSpeech (TTS)
Ahorrar energía es muy importante para el planeta y el medioambiente. El mes pasado consumiste 159 horas de energía. Este mes consumiste 58 horas de energía.

ResponseText
Ahorrar energía es muy importante para el planeta y el medioambiente. El mes pasado consumiste 159 horas de energía. Este mes consumiste 58 horas de energía.
Usa palabras simples y cortas
El lenguaje sencillo y claro tiene el atractivo más amplio, ya que es accesible para personas de todos los orígenes.
ResponseSpeech (TTS)
Tu última medición de azúcar en sangre fue de 126.

ResponseText
Tu última medición de azúcar en sangre fue de 126 mg/dl.
ResponseSpeech (TTS)
El penúltimo nivel de glucemia fue de 126.

ResponseText
El penúltimo nivel de glucemia fue de 126.

Fijación de selectores

La biblioteca de Widgets Extension permite que se muestre el botón para agregar el widget con tu widget en Asistente. Para habilitar la fijación, agrega la siguiente definición del receptor a 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 el siguiente código de un archivo shortcuts.xml de muestra, se muestra una función de BII START_EXERCISE configurada para el inventario intercalado y la entrega de widgets:

<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

En el siguiente código de un archivo shortcuts.xml de muestra, se muestra una función habilitada para el inventario web y la entrega de widgets:

<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>

Prueba las Acciones en apps

Usa la herramienta de pruebas de Acciones en apps, una función del complemento de Asistente de Google para Android Studio, para 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)
  • Enfoca el contenido del widget 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 la app muestre datos con widgets, puedes mostrar un widget de error en el tiempo de ejecución a los 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.

  • Usa el intent de resguardo para abrir la app en la pantalla relevante, no en la pantalla principal.

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

<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