Integrate App Actions with Android Slices (deprecated)

For many intents, the best response from an app is a quick inline answer or a simple confirmation that an action was taken. You can fulfill those intents with an Android Slice, rather than a deep link, to provide a user with information directly in Google Assistant.

Try the sample

Figure 1. Launching a Slice using GET_INVOICE.

Google automatically renders the app name and the logo of the Play Store listing in the header of the Slice and the Open app button in the footer of the Slice. The user can tap on the Slice to follow a deep link into the app if more information or additional interaction is needed.

For example, using the actions.intent.GET_INVOICE built-in intent, your banking app can implement a custom Slice that Assistant displays when the user utters the invocation phrase, "Check my upcoming bills." In the Slice, you can show real time data for the user's bill.

Android Slices work best when a user asks for a small piece of information that can be displayed in a line or two of text. Use deep link fulfillment instead when the user wants to perform a task in your app or start a task that requires further interaction.

Slices integration is also supported for custom intents.

Implement Slices

You can implement Slices with actions.xml functionality or shortcuts.xml functionality.

Select one of the following buttons to choose which implementation method you want to use.

To integrate App Actions with your Slices, follow these steps:

  1. Implement an Android Slice by following the steps described in the Slices Getting started guide.
  2. In your shortcuts.xml file, add a <slice> element to your capability. Within the <slice> element, add <url-template> and <parameter> tags for the capability.

When a Slice is used for fulfillment, the url-template refers to the Slice URI as described in the SliceProvider. The url-template must be of the type content://{slice_authority}/..., where slice_authority is the authority defined in the AndroidManifest.xml file.

The following snippet shows how you might specify a Slice as the fulfillment for the CREATE_TAXI_RESERVATION built-in intent in your shortcuts.xml file:

<!-- shortcuts.xml -->
<capability android:name="actions.intent.CREATE_TAXI_RESERVATION">
  <slice>
    <url-template
      android:name="content://com.example/get_ride{?pickup,dropoff}" />
      <parameter
            android:name="taxiReservation.pickupLocation.name"
            android:key="pickup" />
      <parameter
            android:name="taxiReservation.dropoffLocation.name"
            android:key="dropoff" />
  </slice>
</capability>

For a given capability, you can specify multiple <slice> blocks or use a combination of <slice> and <intent> blocks for fulfillment. This approach lets you provide a customized experience based on different combinations of parameters that the user specifies.

For example, if the user does not specify a dropoff location in their query, you can direct them to the activity in your app that shows options for setting the pickup and dropoff locations. For more information about adding fallback intents, see Create shortcuts.xml.

Note that, similar to multiple <intent> blocks, multiple <slice> blocks or a combination of <slice> and <intent> blocks are executed in the order they are declared. Only the first block matching all the requirements for a given user query executes.

Slice permissions

You need to grant Slice access permission to Google Assistant on startup. Slice permissions are hierarchical, so Assistant gets permission to all Slice URIs as long as you grant permission to Assistant on a top-level URI (for example: content://com.example.slice.provider/).

The following code snippet shows how you might implement this:

Kotlin

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        grantSlicePermissions()
    }
    private fun grantSlicePermissions() {
        val context = applicationContext
        val sliceProviderUri = Uri.Builder()
            .scheme(ContentResolver.SCHEME_CONTENT)
            .authority(SLICE_AUTHORITY)
            .build()
        val assistantPackage = getAssistantPackage(context) ?: return
        SliceManager.getInstance(context)
            .grantSlicePermission(assistantPackage, sliceProviderUri)
    }
    private fun getAssistantPackage(context: Context): String? {
        val packageManager = context.packageManager
        val resolveInfoList = packageManager.queryIntentServices(
            Intent(VoiceInteractionService.SERVICE_INTERFACE), 0
        )
        return if (resolveInfoList.isEmpty()) {
            null
        } else resolveInfoList[0].serviceInfo.packageName
    }
    companion object {
        private const val SLICE_AUTHORITY = "..."
    }
}
  

Java

public class MyApplication extends Application {
    private static final String SLICE_AUTHORITY = "...";
    @Override
    public void onCreate() {
        super.onCreate();
        grantSlicePermissions();
    }
    private void grantSlicePermissions() {
        Context context = getApplicationContext();
        Uri sliceProviderUri =
            new Uri.Builder()
                .scheme(ContentResolver.SCHEME_CONTENT)
                .authority(SLICE_AUTHORITY)
                .build();
        String assistantPackage = getAssistantPackage(context);
        if (assistantPackage == null) {
            return;
        }
        SliceManager.getInstance(context)
            .grantSlicePermission(assistantPackage, sliceProviderUri);
    }
    private String getAssistantPackage(Context context) {
        PackageManager packageManager = context.getPackageManager();
        List resolveInfoList = packageManager.queryIntentServices(
            new Intent(VoiceInteractionService.SERVICE_INTERFACE), 0);
        if (resolveInfoList.isEmpty()) {
            return null;
        }
        return resolveInfoList.get(0).serviceInfo.packageName;
    }
}

Slice logging

To trigger App Actions, Google logs the following data about your Slice (specifically from the SliceMetadata object):

We don't log the following SliceMetadata information:

As a result, no information is available from Google Assistant on user selections within the context of a provided Slice.

Quality guidelines for Slices integration

This section highlights key requirements and best practices when you integrate App Actions with Slices.

General requirements

  • Make sure you define a primary action for each Slice.
  • While loading the content of the Slice, make sure to set the isLoading field in the Slices API to true if the Slice being returned is a loading Slice (and false otherwise).
  • To indicate error conditions, use the setIsError() method to let Google know that Slice content didn't load successfully or that the user's request can't be handled.

Content in Slices

  • (Required) Don't show ads in your Slices.
  • (Required) Don't add header or footer information in your Slices; Google automatically provides this.
  • Keep Slice content completely focused on fulfilling the intent. Don't try to fulfill multiple intents with one Slice or add irrelevant content.

Handle authentication

  • (Required) Where user authentication is needed to complete a user flow, return a Slice that explains that the user needs to continue in the app. Inline user authentication in Google Assistant is not supported for App Actions.
  • If users permit your app to show data using Slices, you can return an Error Slice at runtime for unauthorized users.

Deep linking

  • (Required) Deep-link the user to the correct screen in your app, not the home screen.
  • (Required) In your shortcuts.xml file, always provide fallback deep link fulfillment in addition to Slice fulfillment for a given built-in intent. The exception to this is where no parameter mappings are defined for that App Action, in which case only the Slice fulfillment is needed.