App Actions help you expand the reach of your Android app by letting users launch directly into specific app features from the Google Assistant. App Actions build on top of your app's deep links, giving users more ways to access the features they already want to use.

As an Android developer, you can implement one of the available built-in intents to help users get things done faster and more smoothly. If there isn't a built-in intent to support your use case, you can file a feature request on the public issues tracker.

This codelab covers intermediate-level concepts for developing with Actions on Google. You should have prior experience with developing Android apps and handling deep links to follow this codelab. Developers entirely new to Android may instead want to get started with one of the codelabs for Android developer fundamentals.

What you'll build

In this codelab, you'll add these features to a sample fitness Android app:

Four progressive screens where the Google Assistant starts a run tracker in an app.

What you'll learn

What you'll need

Before continuing, make sure you have the following tools in your environment:

Familiarity with Kotlin and XML are recommended, although not required, to understand the code used in this codelab.

In this codelab, you'll use an Android emulator with a Google Play image to test App Actions. If you instead want to test on a physical Android device, make sure it's connected to your local development machine. In both cases, you must be logged in to the device and to Android Studio using the same Google account.

App Actions connect users from the Google Assistant to your Android app. But how do they work?

When a user indicates to the Assistant that they want to use your app, the Assistant looks for App Actions registered to your app from an actions.xml file. App Actions are described in this file by using a combination of built-in intents (which semantically describe an app capability) and fulfillment instructions (like a deep link template).

An actions.xml file contains the following information for each App Action:

Based on information given to the Assistant by the user, App Actions create a deep link to fulfill the intent. Your Android activity then filters for and handles the provided deep link to take the user into their desired content.

Altogether, the result becomes a user experience where the Assistant invokes your app in response to a user's query.

The starting point for this codelab is a sample fitness app for Android. In the app, users can start and stop exercise timers, as well as view information about their exercise routines.

Download your base files

To get the base files for this codelab, run the following command to clone the GitHub repository:

git clone --branch codelab-start https://github.com/actions-on-google/appactions-fitness-kotlin.git

Using the --branch codelab-start option checks out the branch where this codelab starts.

Once you've cloned the repository, open it in Android Studio:

  1. In the Welcome to Android Studio dialog, click Open an existing Android Studio project (if you already have a project open, close it first).
  2. To start working on the fitness app, find and select the folder where you cloned the repository.

Change Android application ID

As you implement App Actions for the fitness app, you'll be tasked with testing inputs using an Android Studio plugin. You'll install this plugin later in the codelab, but you won't be able to use the test tool until your app is uploaded to a project in the Google Play Console.

To make your version of the sample fitness app unique in the console, change the application ID listed in the default Android configs of your app/build.gradle file:

build.gradle

android {
...
    defaultConfig {
        applicationId "com.MYUNIQUENAME.android.fitactions"
    ...
    }
}

Replace "MYUNIQUENAME" in the applicationId with something unique to you. Doing so changes the package name and prevents a later "Duplicate package name" issue when uploading to the Play Console.

Try out the app

Before making any other changes to the app, it may be helpful to get an idea of what the sample app can do. Try running the app on an emulator:

  1. In Android Studio, select Run > Run app or click Run in the toolbar.
  2. In the Select Deployment Target dialog, select a virtual device and click OK. The recommended OS version is Android 8 (API level 26) or higher, although Actions run on devices back to Android 5 (API level 21).

The emulator starts and boots just like a physical device, so it may take a while depending on the speed of your computer. Once your app builds and the emulator is ready, Android Studio uploads the app to the emulator and runs it.

Phone with the Fit Actions app open, showing exercise statistics.

Briefly explore the app to see what it can do. Tapping the Run icon starts an exercise timer, and tapping the X icon then stops the timer. These are the two tasks you'll enable with App Actions.

Before continuing, set up and verify that the Assistant is working by long-pressing on the Home button. You'll need to sign in to the Assistant on your virtual device if you haven't already.

Upload APK

Build your app in Android Studio and upload it to the Play Console as an internal release. Uploading the APK is a prerequisite for using the App Actions test tool in Android Studio.

In Android Studio, do the following steps:

  1. Go to Build > Generate Signed Bundle / APK.
  2. Select "Android App Bundle" and click Next.
  3. Enter details to digitally sign your app and click Next.
  4. Select the "release" build variant and click Finish.

In the Google Play Console, upload the app bundle you just created as a new app:

  1. On the All applications page, click Create application.
  2. Give the app any name you want and click Create. For this codelab, you won't need to fill out any of the app information once the app is created.
  3. From the sidebar menu, go to App releases and find the Internal test track section.
  4. Click Manage, then click Create release on the following page.
  5. In the Android App Bundles and APKs to add panel, upload the AAB file you generated earlier (it's likely in the app/release directory).
  6. Click Save.

Now that you've successfully uploaded your app to the Play Console, it's time to go back to Android Studio.

Install test plugin

The Assistant needs to be aware of App Actions registered to your app, so you need to communicate that information somehow. During development, you do that using the "App Actions Test Tool" Android Studio plugin.

If you don't already have the plugin, install it:

  1. Go to File > Settings (Android Studio > Preferences on MacOS).
  2. In the Plugins section, go to Marketplace and search for "App Actions Test Tool".
  3. Install the tool and restart Android Studio.

To set up an App Action, you need to find a built-in intent that maps to a function performed by your Android app. The built-in intents reference page lists the built-in intents available for the Google Assistant, each of which models a common way users express tasks they're trying to do.

In the reference, built-in intents that can be fulfilled by App Actions are marked with the APP_ACTION supported fulfillment type. If there isn't a built-in intent to support your use case, feel free to file a feature request on the public issues tracker.

For this codelab, you're implementing two built-in intents to help out your user:

This process involves setting up a deep link, defining the App Action in the app's XML resources, then connecting the two.

Implement deep links

Deep links take users directly to content, passing information from the intent to the app in the process. By default, the Assistant uses deep links to fulfill the intent and pass parameters to the app. For this codelab, incoming deep links use the "fit-actions.firebaseapp.com" host and "https" scheme.

In the Android manifest file, add an intent filter for the main activity to define the supported deep links:

AndroidManifest.xml

<activity android:name="com.devrel.android.fitactions.FitMainActivity" ...>
    ... // Other intent filters
    
    <!-- Define your supported deeplinks -->
    <intent-filter
        android:autoVerify="true"
        tools:targetApi="m">
        
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data
            android:host="fit-actions.firebaseapp.com"
            android:scheme="https" />
    </intent-filter>
</activity>

Now in the main activity, add the following function to define the behavior for an incoming deep link:

FitMainActivity.kt

private fun handleDeepLink(data: Uri?) {
    when (data?.path) {
        DeepLink.START -> {
            // Get the parameter defined as "exerciseType" and add it to the fragment arguments
            val exerciseType = data.getQueryParameter(DeepLink.Params.ACTIVITY_TYPE).orEmpty()
            val type = FitActivity.Type.find(exerciseType)
            val arguments = Bundle().apply { putSerializable(FitTrackingFragment.PARAM_TYPE, type) }

            updateView(FitTrackingFragment::class.java, arguments)
        }
        DeepLink.STOP -> {
            // Stop the tracking service if any and return to home screen.
            stopService(Intent(this, FitTrackingService::class.java))
            updateView(FitStatsFragment::class.java)
        }
        else -> {
            // Path is not supported or invalid, start normal flow.
            showDefaultView()
        }
    }
}

DeepLink.START and DeepLink.STOP are defined as constants in the sample app, and they map to the corresponding paths of an incoming deep link. In the DeepLink.START case, the handler also gets arguments that come in via deep link URL parameters.

In the onCreate function of the same file, add the deep link handler:

FitMainActivity.kt

override fun onCreate(...) {
    ...
    
    when (action) {
        // When the action is triggered by a deep-link, Intent.ACTION_VIEW will be used
        Intent.ACTION_VIEW -> handleDeepLink(data)
        
        // Otherwise start the app as you would normally do.
        else -> showDefaultView()
    }
}

Now when the app filters an intent of the format "https://fit-actions.firebaseapp.com/start", it starts an exercise timer.

If you're familiar with the Android Debug Bridge (adb), you can test the deep link filter on a running device (or virtual device). To do so, close the app and use the following shell command:

adb shell am start -a android.intent.action.VIEW -d "https://fit-actions.firebaseapp.com/start"

For App Actions to work, the Assistant needs to know which App Actions are registered to your app. You communicate this information by uploading an actions.xml file to the Play Console as part of your Android package.

It only takes a few steps to create the new resource and reference it:

  1. Create actions.xml to establish which built-in intents to connect and what parameters are needed.
  2. Map built-in intent parameters to deep link parameters in your activities.
  3. Add a reference to your actions.xml file in AndroidManifest.xml.

Create actions.xml

To describe the App Actions supported by the app, you create a new XML file named actions.xml in app/src/main/res/xml.

In Android Studio, do the following steps:

  1. Go to File > New > XML > App Actions XML File.
  2. Enter "actions" as the Actions File Name.
  3. Click Finish to create the new actions.xml file and add a reference to it in your AndroidManifest.xml.

Once the new file is created, replace the contents of actions.xml with the following code:

actions.xml

<?xml version="1.0" encoding="utf-8"?>

<actions>
    <action intentName="actions.intent.START_EXERCISE">
        <fulfillment urlTemplate="https://fit-actions.firebaseapp.com/start{?exerciseType}">
            <parameter-mapping
                intentParameter="exercise.name" 
                urlParameter="exerciseType" />
        </fulfillment>
    </action>

    <action intentName="actions.intent.STOP_EXERCISE">
        <fulfillment urlTemplate="https://fit-actions.firebaseapp.com/stop" />
    </action>
</actions>

In the above code, you use <action> elements to define App Actions for starting and stopping an exercise timer in the app. The intentName attributes correspond to the two built-in intents you're fulfilling using App Actions, and the <fulfillment> elements tell the Assistant how to use your app to achieve the action.

Here, both actions are fulfilled by constructing deep links using the urlTemplate attribute. The URL templates use the host and scheme you defined for deep links in your AndroidManifest.xml file. The paths in each URL template correspond to what the handleDeepLink function (added earlier to the main activity) expects.

Note that for starting the timer, you also map the exercise.name parameter from the built-in intent to the exerciseType URL parameter. This allows your deep link handler to get arguments for its business logic from the Assistant.

Confirm that your Android manifest references actions.xml

In the previous step, Android Studio added a reference to your actions.xml file in AndroidManifest.xml. Verify this by checking for the following <meta-data> element in the Android manifest:

AndroidManifest.xml

<application>
    ...
    
    <meta-data
        android:name="com.google.android.actions"
        android:resource="@xml/actions" />
</application>

If you see the above <meta-data> element in your manifest file, move on to testing your App Action.

If you don't see the above <meta-data> element in your manifest file, add it.

Test App Actions

It's time to try out your App Actions on your emulator or test device!

Use the test tool to test the App Action:

  1. Run your virtual device or plug in your testing device.
  2. Go to Tools > App Actions Test Tool. You may be asked to sign in to Android Studio. Use the same account used earlier with the Google Play Console.
  3. Enter "Fit Actions" in the Invocation Name field and click Create Preview.
  4. Select the built-in intent to test using the Configure drop-down list.
  5. Click Run. If given the option to open with Google, select "Always" to allow the Assistant to open supported links (you can change this later in your app settings).

When the test tool creates or updates a preview of your App Actions, it does so for a single Google account, temporarily registering your defined App Actions. Once the tool fetches the built-in intents for your app, you can directly provide the intents with parameter values and trigger the App Action from Android Studio.

As an alternative, you can use the invocation name directly in the Assistant app on your device to try out your App Action. For example, you could say "Hey Google, start running in Fit Actions" to launch the App Action that starts an exercise timer.

You can now start and stop exercise timers in your fitness app via the Google Assistant, but you may have noticed that only certain exercises are recognized. Requests like "Start my swim in Fit Actions" or "Start jogging in Fit Actions" lead to a timer that starts with the text "Start unknown in...".

That's because the only exercise types currently defined in your app are "Running", "Cycling", and "Walking." How can you better accommodate users who use different words to start their timer?

For this app, you can increase the number of recognized phrases in a couple of ways:

If you add exercise types, then you can connect to more pathways in your app based on the exercise a user wants to do. That's one way to help users, and it's the correct choice if your app can perform different tasks for different exercises.

For the fitness app, you can take the second option to make the Assistant immediately more accommodating. Once you do, users will be able to start a timer using "Start sprinting" or "Start jogging" in addition to "Start running".

Inline inventory and synonyms

Inline inventory, along with synonyms, define other ways your app expects users to trigger App Actions with the Assistant. You add inline inventory in your actions.xml file to directly create a listing of supported options for users. Inventory items are grouped in an <entity-set> element, and action definitions can reference entity sets to use them for mapping parameters.

Add more exercise types to the action that starts a timer by creating and referencing an entity set:

actions.xml

<actions>
    <action intentName="actions.intent.START_EXERCISE">
        ...
        
        <!-- Map a parameter to an entity set reference -->
        <parameter name="exercise.name">
            <entity-set-reference entitySetId="ExerciseEntitySet" />
        </parameter>
    </action>
    ...
    
    <!-- Define an inline inventory -->
    <!-- This sample maps supported entities with the class FitActivity.Type -->
    <entity-set entitySetId="ExerciseEntitySet">
        <entity
            name="@string/activity_running"
            identifier="RUNNING" />
        <entity
            name="@string/activity_walking"
            identifier="WALKING" />
        <entity
            name="@string/activity_cycling"
            identifier="CYCLING" />
    </entity-set>
</actions>

Each <entity> element in the inline inventory represents a unique match for the user's query. Entities allow the Assistant to distinguish between supported inputs like running, walking, and cycling. They're especially useful when including additional structured data, as you might do for menu items or services in another app.

Next, provide arrays of synonyms for each entity, so the Assistant can handle different phrasings of the same task. Use an array resource file (in app/src/main/res/values/) to contain alternatives for different entities separately:

arrays.xml

<resources>
    <array name="runningSynonyms">
        <item>Run</item>
        <item>Jog</item>
        <item>Jogging</item>
        <item>Sprint</item>
    </array>

    <array name="walkingSynonyms">
        <item>Walk</item>
        <item>Hike</item>
        <item>Hiking</item>
    </array>

    <array name="cyclingSynonyms">
        <item>Biking</item>
        <item>Riding</item>
        <item>Pedaling</item>
    </array>
</resources>

In the above code, you define synonyms separately for each entity. To connect an entity to an array of synonyms, reference the array in the alternateName attribute of the entity.

In the entity set that you created earlier, insert the alternateName attribute for each <entity> element:

actions.xml

<actions>
    ...
    
    <entity-set entitySetId="ExerciseEntitySet">
        <entity
            name="@string/activity_running"
            alternateName="@array/runningSynonyms"
            identifier="RUNNING" />
        <entity
            name="@string/activity_walking"
            alternateName="@array/walkingSynonyms"
            identifier="WALKING" />
        <entity
            name="@string/activity_cycling"
            alternateName="@array/cyclingSynonyms"
            identifier="CYCLING" />
    </entity-set>
</actions>

Test your synonyms

In the test tool, creating or updating a preview fetches the registered built-in intents for your app. You can then directly provide the intents with parameter values and trigger the App Action from Android Studio.

Try out your synonyms in the App Actions test tool:

  1. Click Update Preview.
  2. Select "actions.intent.START_EXERCISE" from the Configure drop-down list.
  3. Type "jogging" into the exercise.name field.
  4. Select your device from the Select Target Device drop-down list.
  5. Click Run.

Animation of App Actions test tool using the input \

Congratulations!

You've now covered the skills necessary to add App Actions to an Android app.

What we've covered

What's next

From here, you can try making further refinements to your fitness app. To give you some inspiration, we've provided an extended version of the app in the master branch of the repo on GitHub. The extended version uses Android Slices to additionally present exercise information through the Assistant. It also incorporates the App Action deployment requirements for production Android apps.

To learn more about Actions on Google, explore these resources:

Follow us on Twitter @ActionsOnGoogle to stay tuned to our latest announcements, and tweet to #AoGDevs to share what you have built!

Feedback survey

Before you go, please fill out this form to let us know how we're doing!