App Actions を Android ウィジェットと統合する

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。
図 1. GET_EXERCISE_OBSERVATION のウィジェットを起動している。

多くのインテントでは、単純な回答や簡単な確認などの迅速な対話型のエクスペリエンスをユーザーに提供することをおすすめします。Google アシスタントに Android アプリ ウィジェットを表示することで、この種のインテントに対応できます。

このガイドでは、ウィジェットを使用したアシスタントによるユーザークエリの処理と、App Actions の Widgets Extension ライブラリでアシスタントのウィジェット エクスペリエンスを向上させる方法について説明します。

メリット

ウィジェットとは、ランチャーやロック画面などの Android サーフェスに埋め込むことができる小さなアプリビューです。App Actions を使用すると、ウィジェットをアシスタントで表示できるようにして、その効果を高めることができます。

  1. 発見: ユーザーの自然言語クエリに応じてウィジェットを積極的に表示します。
  2. エンゲージメント: アシスタントがロック画面のアカウントに基づく情報を表示するときや、Android Auto の場合などに、ハンズフリー コンテキストでウィジェットを表示します。
  3. リテンション: ユーザーがアシスタントに表示されたウィジェットをランチャーに固定できます。機能を固定するには、Widgets Extension ライブラリが必要です。

アシスタントがウィジェットを表示する方法

ユーザーがアシスタントでウィジェットを呼び出す方法は 2 つあります。

  • 名前を指定してウィジェットを明示的にリクエストする。
  • ウィジェット フルフィルメント用に構成された組み込みインテント(BII)またはカスタム インテントをトリガーするクエリをアシスタントに発話する。

明示的呼び出し

インストール済みのアプリのウィジェットを明示的に呼び出すには、次のようにアシスタントに話しかけます。

  • 「OK Google, ExampleApp ウィジェットを表示して」
  • 「ExampleApp のウィジェット」

アシスタントには、「ExampleApp says, here's a widget」という一般的な紹介とともにウィジェットが表示されます。アシスタントは、アプリ デベロッパーによる開発を必要とすることなく、この方法でリクエストされたウィジェットを返すようになっています。ただし、この呼び出し方法では、ユーザーがリクエストするウィジェットの明示的な知識を持っている必要があります。ウィジェットを簡単に発見できるようにするには、次のセクションで説明するインテント フルフィルメントの方法を使用します。

インテント フルフィルメント

ウィジェットを使用してユーザーがアシスタントで実行する自然言語クエリを処理することで、ウィジェットを見つけやすくします。たとえば、ユーザーが「OK Google, 今週は ExampleApp で何マイル歩いた?」と話しかけてフィットネス アプリの GET_EXERCISE_OBSERVATION BII をトリガーしたときにウィジェットを返すことができます。ウィジェットを App Actions と統合すると、発見が容易になるだけでなく、次のようなメリットも得られます。

  • パラメータ アクセス: アシスタントがユーザークエリから抽出されたインテント パラメータをウィジェットに提供して、カスタム レスポンスを可能にします。
  • 案内用のカスタム TTS: ウィジェットの表示時にアシスタントが読み上げる TTS(テキスト読み上げ)文字列を指定できます。
  • ウィジェットの固定: アシスタントがウィジェットの近くに [このウィジェットを追加] ボタンを表示し、ウィジェットをランチャーに簡単に固定できるようにします。

ウィジェットを実装する

インテントにウィジェット フルフィルメントを実装する手順は次のとおりです。

  1. シンプルなウィジェットを作成するの手順に沿って、Android ウィジェットを実装します。
  2. アプリの shortcuts.xml リソース ファイルで、フルフィルメントの詳細と BII <parameter> タグを含む <app-widget> 要素をケーパビリティに追加します。パラメータを処理するようにウィジェットを更新します。
  3. (必須)Widgets Extension ライブラリを追加します。これにより、アシスタントはウィジェットに BII 名とパラメータを渡すことができます。また、カスタムの TTS の案内とウィジェットの固定機能も有効になります。

次のセクションでは、shortcuts.xml<app-widget> スキーマについて説明します。

ウィジェット スキーマ

<app-widget> 要素は、shortcuts.xml<capability> 要素内のフルフィルメントとして定義されます。任意と記載されていない限り、次の属性が必要です。

shortcuts.xml タグ親要素属性
<app-widget> <capability>
  • android:identifier
  • android:targetClass
<parameter> <app-widget>
<extra> <app-widget>
  • android:name(TTS にのみ適用)
  • android:value(任意)

ウィジェット スキーマの説明

<app-widget>

最上位のウィジェット フルフィルメント要素。

属性:

  • android:identifier: このフルフィルメントの一意の識別子。この値は、<capability> 内で定義された <app-widget><intent> のフルフィルメント要素で一意である必要があります。
  • android:targetClass: インテントを処理する AppWidgetProvider の完全なクラス名。

<parameter>

BII パラメータをインテントの <parameter> 値にマッピングします。<app-widget> 要素ごとに 0 個以上のパラメータを定義できます。アシスタントは、フルフィルメント中に、Key-Value ペアとしてウィジェット インスタンスのエクストラを次の形式で更新することで、パラメータを渡します。

  • キー: パラメータに定義された android:key
  • 値: BII がユーザーの音声入力から抽出する値。

トリガーする BII の名前とパラメータを含んだ Bundle を返す、関連付けられた AppWidgetManager オブジェクトの getAppWidgetOptions() を呼び出すことで、これらのエクストラにアクセスします。詳しくは、パラメータ値の抽出をご覧ください。

BII パラメータの照合の詳細については、パラメータのデータと照合をご覧ください。

<extra>

このウィジェットに案内用のカスタム TTS を使用する必要があることを宣言する省略可能なタグ。このタグには次の属性値が必要です。

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

サンプルコード

次の XML は、GET_EXERCISE_OBSERVATION BII ケーパビリティのウィジェット フルフィルメント構成を示しています。

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>

ケーパビリティごとに複数の <app-widget> 要素を指定することも、<app-widget> 要素と <intent> 要素を組み合わせて使用することもできます。このアプローチにより、ユーザーが指定したパラメータのさまざまな組み合わせに基づいて、カスタマイズされたエクスペリエンスを実現できます。たとえば、ユーザーがクエリで降車場所を指定しなかった場合、乗車場所と降車場所を設定するオプションを表示するアプリ内アクティビティにユーザーを誘導できます。フォールバック インテントの定義について詳しくは、フォールバック インテントのセクションをご覧ください。

パラメータ値を抽出する

次のサンプル AppWidgetProvider クラスでは、プライベート関数 updateAppWidget() を使用して、ウィジェット オプション Bundle から BII 名とパラメータを抽出します。

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

Widgets Extension ライブラリ

App Actions の Widgets Extension ライブラリを使用すると、音声を主体とするアシスタントに対応できるようウィジェットを拡張できます。このライブラリにより、ウィジェットはトリガーされる BII から、BII 名や、ユーザークエリから抽出されたインテント パラメータなどの重要なフルフィルメント情報を受け取ることができます。この Maven ライブラリを使用すると、各ウィジェットに案内用のカスタム TTS(テキスト読み上げ)を提供できます。これにより、視覚的にレンダリングされるコンテンツの概要をアシスタントが読み上げることができます。また、ランチャーの固定も可能になり、ユーザーはアシスタントに表示されるウィジェットを簡単にランチャー画面に保存できるようになります。

まず、アプリ モジュールの build.gradle ファイルの依存関係セクションにライブラリを追加します。

app/build.gradle

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

カスタムの案内

Widgets Extension ライブラリをインポートした後、ウィジェットに案内用のカスタム TTS を提供できます。定義をウィジェットの AppWidgetProvider に追加するには、IDE でクラスを開き、Widgets Extension ライブラリをインポートします。

Kotlin

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

Java

import com.google.assistant.appactions.widgets.AppActionsWidgetExtension;
次に、このライブラリを使って案内用の文字列を定義し、ウィジェットを更新します。
ExampleAppWidget

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

}

ランチャーの固定

このライブラリを使用すると、[このウィジェットを追加] ボタンをアシスタント内のウィジェットとともに表示できます。固定するには、次のレシーバー定義を 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>

インベントリの可用性

インライン インベントリまたはウェブ インベントリをサポートする BII は、これらのインベントリをウィジェット フルフィルメントに拡張できます。

インライン インベントリ

次のコードサンプルは、インライン インベントリとウィジェット フルフィルメント用に構成された START_EXERCISE BII のケーパビリティを示しています。

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>

前述のサンプルで、ユーザーがアシスタントに「ExampleApp でランニングを始めて」と話しかけてこのケーパビリティをトリガーすると、<app-widget> フルフィルメントのオプション バンドルには次の Key-Value ペアが含まれます。

  • キー = “exerciseName”
  • 値 = “RunningShortcut”

ウェブ インベントリ

ウェブ インベントリとウィジェット フルフィルメントに対して有効になっているケーパビリティについては、以下のサンプルコードをご覧ください。

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>

テスト

Android Studio 用の Google アシスタント プラグインの機能である App Actions Test Tool を使用して、実機または仮想デバイスでウィジェットをテストします。このテストツールを使用する手順は次のとおりです。

  1. アプリを実行中のテストデバイスを接続します。
  2. Android Studio で、[Tools] > [App Actions] > [App Actions Test Tool] に移動します。
  3. [Create preview] をクリックします。
  4. Android Studio から、テストデバイスでアプリを実行します。
  5. テストデバイスのアシスタント アプリを使用して、App Action をテストします。たとえば、「OK Google, 今週は ExampleApp で何マイル走った?」と尋ねます。
  6. アプリの動作を観察します。または、Android Studio デバッガを使用して、アクションの結果が想定どおりになることを確認します。

品質に関するガイドライン

このセクションでは、App Actions をウィジェットと統合する際の主な要件とおすすめの方法について説明します。

ウィジェットのコンテンツ

  • 必須)ウィジェットに広告を表示しないでください。
  • ウィジェットのコンテンツは、インテントのフルフィルメントのみに完全に集中させる必要があります。1 つのウィジェットで複数のインテントを遂行しないでください。また、無関係なコンテンツを追加しないでください。

認証の処理

  • 必須)ユーザーフローを完了するためにユーザー認証が必要な場合は、ユーザーが操作を続行する必要があることを説明するウィジェットをアプリ内で返してください。Google アシスタント内のインライン ユーザー認証は、App Actions ではサポートされていません。
  • ユーザーがウィジェットを介したデータの表示をアプリに許可している場合は、権限のないユーザーに対して実行時にエラー ウィジェットを返すことができます。

フォールバック インテント

  • 必須shortcuts.xml では、常に、特定のケーパビリティのウィジェット フルフィルメントに加えて、フォールバック <intent> を指定します。フォールバック インテントは、必須の <parameter> 値のない <intent> 要素です。これがあることにより、ユーザークエリに、ケーパビリティで定義された他のフルフィルメント要素に必要なパラメータが含まれていない場合に、アシスタントがアクションを実行できるようになります。ただし、そのケーパビリティに必要なパラメータがない場合は例外です。この場合、ウィジェット フルフィルメントのみが必要になります。
  • フォールバック インテントは、ホーム画面ではなく、アプリ内の関連する画面にアプリを開く必要があります。

次のサンプルコードは、プライマリ <app-widget> フルフィルメントをサポートするフォールバック <intent> を備えた <capability> を示しています。

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>

Google Play のデータ開示

このセクションでは、最新バージョンの Widgets Extension ライブラリによって収集されるエンドユーザー データを記載しています。

この SDK では、アシスタントの音声機能を使用して Google アシスタントがユーザーに読み上げる、デベロッパー提供のテキスト読み上げ(TTS)応答が送信されます。この情報が Google に保存されることはありません。

App Actions は、次の目的でクライアント アプリのメタデータを収集する場合があります。

  • 各 SDK バージョンの導入率をモニタリングするため。
  • アプリ全体での SDK 機能の使用状況を数量化するため。