App Actions deployment requirements

To ensure that users enjoy a seamless experience when using App Actions, you must meet these requirements before activating App Actions for your app:

  • Log contextual content: To create a better experience when users invoke App Actions from the Assistant while in the app, your Activity should call the onProvideAssistContent() method.
  • Report App Action interaction results: To help Google proactively suggest your App Actions to users, use the Firebase App Indexing API to let the Assistant know whether your app has successfully handled a built-in request.
  • Handle the SEARCH_ACTION Android intent: If your app has a search function, you must support the SEARCH_ACTION common intent. This lets Google provide a fallback mechanism if we are unable to resolve the user's query to a built-in intent.

These requirements are described in more detail below.

Log contextual content

By implementing onProvideAssistContent(), you provide the Assistant with structured information about the current foreground content. This contextual information enables the Assistant to continue being helpful after the user enters your app.

You only need to implement onProvideAssistContent() for the final app activity in the user's task flow after invoking the App Action. For example, in a CREATE_MONEY_TRANSFER flow, you would implement the method in the final screen showing the receipt; you would not need to implement it for any in-progress or preview screens.

You must provide this data as a JSON-LD object in the structuredData field. The following code snippet shows how you might implement the contextual content logging:

Java
@Override
public void onProvideAssistContent(AssistantContent outContent) {
  super.onProvideAssistContent(outContent);

  // JSON-LD object based on Schema.org structured data
  outContent.structuredData = new JSONObject()
          .put("@type", "MenuItem")
          .put("name", "Blueberry Crisp Iced Signature Latte")
          .put("url", "https://mysite.com/menuitems/12345a")
          .toString();
}
      
Kotlin
override fun onProvideAssistContent(outContent: AssistContent) {
    super.onProvideAssistContent(outContent)

    // JSON-LD object based on Schema.org structured data
    outContent.structuredData = JSONObject()
            .put("@type", "MenuItem")
            .put("name", "Blueberry Crisp Iced Signature Latte")
            .put("url", "https://mysite.com/menuitems/12345a")
            .toString()
}
      

We recommend that you implement onProvideAssistContent() for any sharable entity in your app. However, onProvideAssistContent() is required in the activities that are triggered by the following built-in intents for App Actions:

Category Built-in intent Required entity type
Finance CREATE_MONEY_TRANSFER @MoneyTransfer
Fitness STOP_EXERCISE @Exercise
GET_EXERCISE_OBSERVATION @ExerciseObservation
GET_FOOD_OBSERVATION @FoodObservation
Food ordering ORDER_MENU_ITEM @MenuItem, @Restaurant
GET_ORDER @Order
Ride sharing CREATE_TAXI_RESERVATION @TaxiReservation
GET_TAXI_RESERVATION @TaxiReservation

Similarly, while we recommend providing as much data as possible about each entity, we require the following fields:

  • @type
  • .name
  • .url (only required if the content is URL-addressable)

To learn more about using onProvideAssistContent(), see the Optimizing Contextual Content for the Assistant guide in the Android developer documentation.

Report interaction results

Google monitors the health of your App Actions to know when they aren't working as intended. You must let Google know using Firebase whether your app successfully handled a built-in intent with App Actions.

To satisfy this requirement, take these steps in your Android app:

  1. Add Firebase and the App Indexing library to your Android Studio project.
  2. For deep-linked activities launched via App Actions, use the FirebaseUserActions class to report either that the App Action invocation was handled by your app successfully or that it was not.

    For example, a user uses App Actions to order soup from a soup provider. The user is deep linked to a confirmation page prior to adding the soup to a cart. At this point, the app reports a success via FirebaseUserActions.

    As another example, a user uses App Actions to order soup from a provider who can't fulfill their request (like a shoe store). The app only supports deep links for items in inline inventory. When the deep link handler runs, the app reports a failure via FirebaseUserActions.

    Note that if this app handled the absence of the desired item with a fallback search for the string in inventory, the app would have reported a success instead.

You can tell that an activity was launched via App Actions if the incoming deep link matches the format of a URL template defined in your actions.xml file. Because you define both the URL template and how an activity handles deep links, you can flag and recognize deep links from App Actions in a way that best suits your app.

For example, in your actions.xml file, you can add a query string parameter to the URL template of the <fulfillment> element. In the corresponding activity, you can then look for that parameter to determine whether a user triggered the deep link via App Actions.

The following code snippet shows how to use the FirebaseUserActions class to report status when users invoke your App Actions:

Java
private static final String ACTION_TOKEN_EXTRA =
        "actions.fulfillment.extra.ACTION_TOKEN";

void notifyActionStatus(String status) {
  String actionToken = getIntent().getStringExtra(ACTION_TOKEN_EXTRA);
  final Action action = new AssistActionBuilder()
          .setActionToken(actionToken)
          .setActionStatus(status)
          .build();
  FirebaseUserActions.getInstance().end(action);
}

// On Action success
notifyActionStatus(Action.Builder.STATUS_TYPE_COMPLETED);

// On Action failed
notifyActionStatus(Action.Builder.STATUS_TYPE_FAILED);
      
Kotlin
private val actionTokenExtra = "actions.fulfillment.extra.ACTION_TOKEN"

fun notifyActionStatus(status: String) {
    val actionToken = intent.getStringExtra(actionTokenExtra)
    val action = AssistActionBuilder()
            .setActionToken(actionToken)
            .setActionStatus(status)
            .build()
    FirebaseUserActions.getInstance().end(action)
}

// On Action success
notifyActionStatus(Action.Builder.STATUS_TYPE_COMPLETED)

// On Action failed
notifyActionStatus(Action.Builder.STATUS_TYPE_FAILED)
      

Make sure that you're uploading the action data to Google servers (default behavior) by not setting setUpload(false).

You can verify that the correct logs are being reported by doing the following:

  1. In Android Studio, open the App Actions test tool and create a preview of your App Actions. If you've already created a preview, update your preview instead.
  2. On your test device, enable developer options if not already enabled.
  3. In the Settings app, select Google, then Firebase App Indexing.
  4. Toggle Capture User Actions and Errors to On.
  5. Trigger your App Action intent on your test device via the Assistant or the App Actions test tool.
  6. Return to the Firebase App Indexing page of the Settings app and select your app.

  7. Verify that the AssistAction user action contains items for actionToken and actionStatus.

  8. Verify that the actionStatus item contains the status corresponding to success or failure of the triggered built-in intent. Validate your Firebase logging separately for successes and failures.

The actionStatus and actionToken items are highlighted in a device screenshot of the User Action Details page.

In the event where the Assistant is confident that the user wishes to access your app via the Assistant, but is unable to resolve the query to a built-in intent, it's important to provide a fallback mechanism. If your app already has a search function, you must handle the SEARCH_ACTION Android intent, so that Google can pass the user query into your app, if needed.

The following snippet shows how to add an intent filter to an Activity with the SEARCH_ACTION common intent in the AndroidManifest.xml file:

<activity android:name=".SearchActivity">
    <intent-filter>
        <action android:name="com.google.android.gms.actions.SEARCH_ACTION"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>

In your search activity, you should check the for the SEARCH_ACTION intent and extract the query. Then, do something with the query like running a search and displaying the results in the UI. For example, you could make a query to the Assistant like this: "Hey Google, show me cats on myvideoapp".

The following code snippet shows an example implementation.

Java
@Override
protected void onCreate(Bundle savedInstanceState) {
...
  Intent intent = getIntent();
  if (intent.getAction().equals(SearchIntents.ACTION_SEARCH)) {
    String query = intent.getStringExtra(SearchIntents.EXTRA_QUERY);
    //TODO: user query value to run search
  }
}
      
Kotlin
override fun onCreate(savedInstanceState: Bundle?) {
    ...
    if (intent?.action == SearchIntents.ACTION_SEARCH) {
        val query = intent?.getStringExtra(SearchIntents.EXTRA_QUERY)
        // TODO: use query value to run search
    }
    ...
}
      

To learn more about handling this common intent, see the Search using a specific app guide in the Android documentation.