This codelab shows how easy it is to create your own interactive Slice on Android. By the end of the codelab, you'll have customized an interactive Slice that runs outside of your app.

Concepts and setup

Concepts

Slices are UI templates that can display rich, dynamic, and interactive content outside of your app in other surfaces, such as the Google Assistant.

Slices help users perform tasks faster by enabling engagement outside of the fullscreen app experience.

Example 1: Check-in or check-out of your rental:

Example 2: Control music on a surface outside of your app:

A Sice represents a piece of app content; you should design your Slices with the following in mind:

Let's get started!

Clone the starter project repo

You can clone our starter project to help you get you started quickly.

If you have Git installed, run the command below. You can check by typing git --version in the terminal / command line and verify that it executes correctly.

 git clone https://github.com/googlecodelabs/slices-basic-codelab

If you do not have Git, you can download the project as a zip file.

Import the project

Start Android Studio, and select Open an existing Android Studio project from the Welcome screen. Open the project directory and double click the build.gradle file in the slices-basic-codelab directory:

After the project has loaded, you might see an alert that says "Unregistered VCS root detected". We won't be pushing any changes to the repo, so you can click "Ignore" or the "X" in the upper-right corner.

If you are in the Android view, you should see a set of folder icons in the upper-left corner of the project window. These icons should look similar to those in the screenshot below. If you are in the Project view, expand the slices-basic-codelab project to see the set of folder icons.

There are two folder icons: base and complete. Each folder icon represents a module.

Please note that Android Studio might take several seconds to compile the project in the background for the first time. During this time, you'll see a spinner in the status bar at the bottom of Android Studio:

Android Studio needs to pull in some necessary components, so be sure to wait until the processes finishes before making code changes.

In addition, if you get a prompt that says something similar to "Reload for language changes to take effect?", choose "Yes".

Understand the starter project

Once Android Studio builds the project for the first time, you're ready to create a Slice. You'll start in the base module and build a Slice from scratch. You'll then add code from each step to base.

The complete module can be used for checking your work or for you to reference if you encounter any issues.

Here's an overview of key components:

Emulator setup

If you need help setting up an Android emulator, see Launch a emulator and run your app.

Install the Slice Viewer APK

First, we need to install the Slice Viewer so we can view our Slice outside of our app.

  1. Connect your Android device to your computer or start an emulator.
  2. After the device/emulator is connected, open a terminal.
  3. In the terminal, navigate to the root of the Slice codelab, and install the Slice Viewer APK. Once installed, your terminal output should look similar to the following:
$ cd ~/local/path/to/slices-basic-codelab
$ adb install -r -t slice-viewer.apk
Performing Streamed Install
Success

Run the starter project

Let's run our app and trigger the Slice Viewer. Please note that the Android device or emulator must be running Android KitKat or higher.


Summary

In this step, you've learned the following:

Next up

Let's look at the building block of Slices, the SliceProvider.

Code step 2

In this section, you'll create the scaffolding needed for a generic Slice.

Every Slice has a corresponding Uri. Our particular Uri is below.

content://com.android.example.slicecodelab/temperature

We'll talk more about the Uri later in the codelab.

Let's now discuss design.

Slice design

Remember, we'll create a Slice that mimics our main temperature control activity.

Ideally, the Slice would be hooked up to an actual thermostat, but for this codelab, the temperature controls will just adjust a number within our app. Our Slice will contain the following components:

  1. The current temperature
  2. Controls to raise and lower the temperature
  3. A primary action to launch the app (triggered by tapping on the slice)

We should end up with something like the screenshot below. Note that the Slice is just the small rectangle that displays the temperature:

Review the Dependencies

First we need to add the right dependencies. Remember, everything will be done in the base module. If you run into issues along the way, check the complete module to see a complete and working version of the codelab.

If you're using Java, instead of the KTX dependency, you'll need to add slice-core and slice-builders to your app's gradle file:

dependencies {  
    implementation 'androidx.slice:slice-core:1.0.0'
    implementation 'androidx.slice:slice-builders:1.0.0'
}

Because we are using Kotlin, we can use the Kotlin slice-builders-ktx library. Part of the Android KTX extension libraries, this library provides extensions that simplify creating Slices.

dependencies {  
    implementation 'androidx.slice:slice-builders-ktx:1.0.0-alpha6'
}

In the base module, we've added this dependency for you.If you'd like to take a look at the dependencies, open the build.gradle file.

Add SliceProvider to the Manifest

To build Slices, we must extend SliceProvider. We've provided a barebones class, but we need to add it to the manifest.

In the base module, open AndroidManifest, and search for TODO: Step 2.1, Add SliceProvider to Manifest.

Below that comment, add the following code snippet:

<!-- TODO: Step 2.1, Add SliceProvider to Manifest. -->
<provider
   android:authorities="com.example.android.slicesbasiccodelab"
   android:name=".TemperatureSliceProvider"
   android:grantUriPermissions="true"
   android:exported="true">

   <intent-filter>
       <action android:name="android.intent.action.VIEW" />
       <category android:name="android.app.slice.category.SLICE" />
   </intent-filter>
</provider>

As you might have guessed, our SliceProvider is named TemperatureSliceProvider.

Each Slice has a Uri, and this provider performs the mapping between Uris and Slices. When a surface wants to display your Slice, it will do so by binding to a Uri.

While the path of the Uri is usually handled within the SliceProvider itself, the host is in the authorities field.

The additional attributes help other surfaces to find and request our Slice (more info).

Alert TemperatureSliceProvider when the Temperature Changes

Next we need to alert the TemperatureSliceProvider (a ContentProvider) when the temperature changes. This will update our temperature in both our MainActivity and our Slice.

In the base module, open the MainActivity file, and search for TODO: Step 2.2, Notify TemperatureSliceProvider the temperature changed.

Add the following:

// TODO: Step 2.2, Notify TemperatureSliceProvider the temperature changed.
if (temperature != newTemperature) {
   temperature = newTemperature

   // Notify slice via URI that the temperature has changed so they can update views.
   // NOTE: TemperatureSliceProvider is derived from ContentProvider, so we are
   // actually assigning a ContentProvider to this authority (URI).
   val uri = TemperatureSliceProvider.getUri(context, "temperature")
   context.contentResolver.notifyChange(uri, null)
}

Now we are ready to start building out our TemperatureSliceProvider.

Review Basics of the SliceProvider

Let's continue to implement TemperatureSliceProvider.

In the base module, open the TemperatureSliceProvider file and search for TODO: Step 2.3, Review non-nullable Context variable.

It should look like this:

override fun onCreateSliceProvider(): Boolean {
   // TODO: Step 2.3, Review non-nullable Context variable.
   contextNonNull = context ?: return false
   return true
}

The onCreateSliceProvider() method is where you initialize all variables for your Slice. In our case, we want to make sure our context is non-nullable. This simplifies code much later in our class, but more importantly, we can't create Slices without the Context.

Define a Slice path

In the base module, search for TODO: Step 2.4, Define a slice path in onBindSlice() within TemperatureSliceProvider, and add the code below.

// TODO: Step 2.4, Define a slice path.
when (sliceUri.path) {
   "/temperature" -> return createTemperatureSlice(sliceUri)
}

When a surface needs a Slice, the system will trigger the onBindSlice() method and pass in a Uri.

When onBindSlice() is triggered, we should return a Slice if we have a matching Uri. We need to check the path to differentiate between different Slices. In this case, we have only one Slice available for the temperature/ path.

Check your progress

Run the base module. You should see a screen similar to the following:

Click the Launch Slice Viewer button. You should see the Slice that we just created, similar to the image below:

Summary

In this step, you've learned the following:

Next up

Next, we'll dive into building an interactive Slice.

Code step 3

In this section, we'll build an interactive Slice.

You probably noticed in the last section that the Slice was built automatically in the method createTemperatureSlice().

This is just a simple private method where we construct our Slice. Let's take a more detailed look at how to build a Slice.

Review Slice's ListBuilder

In the base module, open the TemperatureSliceProvider file, and search for Step 3.1, Review Slice's ListBuilder.

You should see the following code:

// TODO: Step 3.1, Review Slice's ListBuilder.
return list(contextNonNull, sliceUri, ListBuilder.INFINITY) {
   setAccentColor(ContextCompat.getColor(contextNonNull, R.color.slice_accent_color))
...

The main building block of a Slice is the ListBuilder.

ListBuilder allows you to add different types of rows that are displayed in your Slice, and

because we are using the Slice KTX library, we can use the DSL version of ListBuilder which allows us to just use list() and include some general arguments before defining the

structure of our Slice (the main part below).

Here we're constructing our Slice's ListBuilder with the context, Uri, and duration. Since our Slice is not time-sensitive, we can use a time-to-live value of INFINITY. For more information, check out the ListBuilder documentation.

Next, let's customize our content.

Update Slice's ListBuilder

In the base module, open the TemperatureSliceProvider file, and search for TODO: Step 3.2, Create a Slice Header (title and primary action).

Replace the current header{} block with the code below.

// TODO: Step 3.2, Create a Slice Header (title and primary action).
header {
   title = getTemperatureString(contextNonNull)
   // Launches the main Activity associated with the Slice.
   primaryAction = SliceAction.create(
       PendingIntent.getActivity(
           contextNonNull,
           sliceUri.hashCode(),
           Intent(contextNonNull, MainActivity::class.java),
           0
       ),
       IconCompat.createWithResource(contextNonNull, R.drawable.ic_home),
       ListBuilder.ICON_IMAGE,
       contextNonNull.getString(R.string.slice_action_primary_title)
   )
}

The first row of your Slice should always be a header. The header supports a title, subtitle, and a tappable action (usually used to launch an Activity).

If you read through the code, we are setting two things:

If we wanted additional rows, we could add them via a RowBuilder or GridBuilder.

In our case, we only want one row, but we do want to add a couple of additional actions.

Add Temperature up as Slice Action

In the base module, open the TemperatureSliceProvider file, and search for TODO: Step 3.3, Add Temperature Up Slice Action.

Below the comment, add the following code.

// TODO: Step 3.3, Add Temperature Up Slice Action.
addAction(
   SliceAction.create(
       createTemperatureChangePendingIntent(getTemperature() + 1),
       IconCompat.createWithResource(contextNonNull, R.drawable.ic_temp_up),
       ListBuilder.ICON_IMAGE,
       contextNonNull.getString(R.string.increase_temperature)
   )
)

A SliceAction represents an action. It supports tappable icons, custom toggle icons, and default toggles. You can use a SliceAction to add extra buttons to your row.

In our case, we're adding a button (increase temperature) to our header.

Now let's add the other button to decrease the temperature.

Add a Slice Action to decrease temperature

In the base module, open the TemperatureSliceProvider file, and search for Step 3.4, Add Temperature Down Slice Action.

Below the comment, add the following code.

// TODO: Step 3.4, Add Temperature Down Slice Action.
addAction(
   SliceAction.create(
       createTemperatureChangePendingIntent(getTemperature() - 1),
       IconCompat.createWithResource(contextNonNull, R.drawable.ic_temp_down),
       ListBuilder.ICON_IMAGE,
       contextNonNull.getString(R.string.decrease_temperature)
   )
)

Now we have two SliceActions built right into our Slice.

Now our Slice is finished, but before we test it, let's review the PendingIntent code.

Review PendingIntent

In the base module, open the TemperatureSliceProvider file, and search for Step 3.4, Review Pending Intent Creation.

You should see the following code:

// TODO: Step 3.4, Review Pending Intent Creation.
// PendingIntent that triggers an increase/decrease in temperature.
private fun createTemperatureChangePendingIntent(value: Int): PendingIntent {
   val intent = Intent(ACTION_CHANGE_TEMPERATURE)
       .setClass(contextNonNull, TemperatureBroadcastReceiver::class.java)
       .putExtra(EXTRA_TEMPERATURE_VALUE, value)

   return PendingIntent.getBroadcast(
       contextNonNull, requestCode++, intent,
       PendingIntent.FLAG_UPDATE_CURRENT
   )
}

You don't need to add any code here. We are simply using a PendingIntent to trigger the TemperatureBroadcastReceiver in our app. This increases/decreases the temperature.

Let's run our app and see how this looks.

Check your progress

Run the base module. After the app has launched, click the Launch Slice Viewer button.

After that action, you should see a screen similar to the following:

Feel free to play with the temperature, and then launch the main activity by clicking on the Slice. You should see the same value in the app!

Summary

In this step you've learned:

Next up

Congratulations! You're a Slices wizard! The earth trembles at your newfound ability!

To learn more about Slices, check out our getting started guide.

Learn More

For more Slices info, watch these great videos: