lightbulb_outline Help shape the future of the Google Play Console, Android Studio, and Firebase. Start survey

Slice templates

This document provides details on how to use the template builders in Android Jetpack to construct Slices.

Define your Slice template

Slices are constructed by using a ListBuilder. ListBuilder allows you to add different types of rows that are displayed in a list. This section describes each of those row types and how they are constructed.

SliceAction

The most basic element of a Slice template is a SliceAction. A SliceAction contains a label along with a PendingIntent and is one of the following:

  • Icon button
  • Default toggle
  • Custom toggle (a drawable with an on/off state)

SliceAction is used by the template builders described in the rest of this section. A SliceAction can have an image mode defined that determines how the image is presented for the action:

  • ICON_IMAGE: tiny size and tintable
  • SMALL_IMAGE: small size and non-tintable
  • LARGE_IMAGE: largest size and non-tintable

HeaderBuilder

In most cases, you should set a header for your template using a HeaderBuilder. A header can support the following:

  • Title
  • Subtitle
  • Summary subtitle
  • Primary action

Some example header configurations are shown below. Note that gray boxes show potential icon and padding locations:




Header rendering on different surfaces

When a Slice is needed, the displaying surface determines how to render the Slice. Note that rendering might differ somewhat between hosting surfaces.

In smaller formats, usually only the header is displayed, if one exists. If you’ve specified a summary for the header, the summary text is shown instead of the subtitle text.

If you have not specified a header in your template, the first row added to your ListBuilder is usually displayed instead.

HeaderBuilder example - Simple list Slice with header

Kotlin

fun createSliceWithHeader(sliceUri: Uri) =
        ListBuilder(context, sliceUri, ListBuilder.INFINITY)
                .setAccentColor(0xff0F9D) // Specify color for tinting icons
                .setHeader {
                    it.apply {
                        setTitle("Get a ride")
                        setSubtitle("Ride in 4 min")
                        setSummary("Work in 1 hour 45 min | Home in 12 min")
                    }
                }.addRow {
                    it.apply {
                        setTitle("Home")
                        setSubtitle("12 miles | 12 min | $9.00")
                        addEndItem(IconCompat.createWithResource(context, R.drawable.ic_home), SliceHints.ICON_IMAGE)
                    }
                }

Java

public Slice createSliceWithHeader(Uri sliceUri) {
    // Construct the parent.
    ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
            .setAccentColor(0xff0F9D58); // Specify color for tinting icons.

    // Create the header.
    HeaderBuilder headerBuilder = new HeaderBuilder(listBuilder)
            .setTitle("Get a ride")
            .setSubtitle("Ride in 4 min.")
            .setSummary("Work in 1 hour 45 min | Home in 12 min.");

    // Add it to the list.
    listBuilder.setHeader(headerBuilder);

    // Construct the rows.
    RowBuilder rowBuilder = new RowBuilder(listBuilder)
            .setTitle("Home")
            .setSubtitle("12 miles | 12 min | $9.00")
            .addEndItem(IconCompat.createWithResource(getContext(), R.drawable.ic_home), SliceHints.ICON_IMAGE);

    listBuilder.addRow(rowBuilder);

    // Add more rows if needed...

    return listBuilder.build();
}

Note: for simplicity, the example code omits some of the coloring that is rendered in the images above

SliceActions in headers

Slice headers can also display SliceActions:

Kotlin

fun createSliceWithActionInHeader(sliceUri: Uri): Slice {
    // Construct our slice actions.
    val noteAction = SliceAction(takeNoteIntent,
            IconCompat.createWithResource(context, R.drawable.ic_pencil),
            ICON_IMAGE, "Take note")

    val voiceNoteAction = SliceAction(voiceNoteIntent,
            IconCompat.createWithResource(context, R.drawable.ic_mic),
            ICON_IMAGE,
            "Take voice note")

    val cameraNoteAction = SliceAction(cameraNoteIntent,
            IconCompat.createWithResource(context, R.drawable.ic_camera),
            ICON_IMAGE,
            "Create photo note")


    // Construct the list.
    return ListBuilder(context, sliceUri, ListBuilder.INFINITY)
            .setAccentColor(0xfff4b4) // Specify color for tinting icons
            .setHeader {
                it.apply {
                    setTitle("Create new note")
                    setSubtitle("Easily done with this note taking app")
                }
            }
            .addAction(noteAction)
            .addAction(voiceNoteAction)
            .addAction(cameraNoteAction)
            .build()
}

Java

public Slice createSliceWithActionInHeader(Uri sliceUri) {

    // Construct the list.
    ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
            .setAccentColor(0xfff4b400); // Specify color for tinting icons

    // Construct the header.
    HeaderBuilder headerBuilder = new HeaderBuilder(listBuilder)
            .setTitle("Create new note")
            .setSubtitle("Easily done with this note taking app");

    // Construct our slice actions.
    SliceAction noteAction = new SliceAction(takeNoteIntent,
            IconCompat.createWithResource(getContext(), R.drawable.ic_pencil),
            ICON_IMAGE, "Take note");

    SliceAction voiceNoteAction = new SliceAction(voiceNoteIntent,
            IconCompat.createWithResource(getContext(), R.drawable.ic_mic),
            ICON_IMAGE,
            "Take voice note");

    SliceAction cameraNoteAction = new SliceAction(cameraNoteIntent,
            IconCompat.createWithResource(getContext(), R.drawable.ic_camera),
            ICON_IMAGE,
            "Create photo note");

    // Set the header.
    listBuilder.setHeader(headerBuilder);

    // Add the actions to the list builder.
    listBuilder.addAction(noteAction);
    listBuilder.addAction(voiceNoteAction);
    listBuilder.addAction(cameraNoteAction);

    return listBuilder.build();
}

RowBuilder

You can construct a row of content by using a RowBuilder. A row can support any of the following:

  • Title
  • Subtitle
  • Start item: SliceAction, Icon, or a timestamp
  • End items: SliceAction, Icon, or a timestamp
  • Primary action

You can combine row content in a number of ways, subject to the following restrictions:

  • End items cannot be a mix of SliceActions and Icons
  • A row can contain only one timestamp

Example rows of content are shown in the images below. Note that gray boxes show potential icon and padding locations:




RowBuilder example - Wi-Fi toggle

The example below demonstrates a row with a primary action and a default toggle.

Kotlin

fun createActionWithActionInRow(sliceUri: Uri): Slice {
    // Primary action - open wifi settings.
    val primaryAction = SliceAction(wifiSettingsPendingIntent,
            IconCompat.createWithResource(context, R.drawable.ic_wifi),
            "Wi-Fi Settings")

    // Toggle action - toggle wifi.
    val toggleAction = SliceAction(wifiTogglePendingIntent,
            "Toggle Wi-Fi", isConnected /* isChecked */)

    // Create the parent builder.
    return ListBuilder(context, wifiUri, ListBuilder.INFINITY)
            .setAccentColor(0xff4285) // Specify color for tinting icons / controls.
            .addRow {
                it.apply {
                    setTitle("Wi-Fi")
                    setPrimaryAction(primaryAction)
                    addEndItem(toggleAction)
                }
            }
            .build()
}

Java

public Slice createActionWithActionInRow(Uri sliceUri) {
    // Primary action - open wifi settings.
    SliceAction primaryAction = new SliceAction(wifiSettingsPendingIntent,
            IconCompat.createWithResource(getContext(), R.drawable.ic_wifi),
            "Wi-Fi Settings");

    // Toggle action - toggle wifi.
    SliceAction toggleAction = new SliceAction(wifiTogglePendingIntent,
            "Toggle Wi-Fi", isConnected /* isChecked */);

    // Create the parent builder.
    ListBuilder listBuilder = new ListBuilder(getContext(), wifiUri, ListBuilder.INFINITY);

    // Specify color for tinting icons / controls.
    listBuilder.setAccentColor(0xff4285f4);

    // Create the row.
    RowBuilder rowBuilder = new RowBuilder(listBuilder)
            .setTitle("Wi-Fi")
            .setPrimaryAction(primaryAction)
            .addEndItem(toggleAction);

    // Add the row.
    listBuilder.addRow(rowBuilder);

    // Build the slice.
    return listBuilder.build();
}

GridBuilder

You can construct a grid of content by using a GridBuilder. A grid can support the following image types:

  • ICON_IMAGE: tiny size and tintable
  • SMALL_IMAGE: small size and non-tintable
  • LARGE_IMAGE: largest size and non-tintable

A grid cell is constructed by using a CellBuilder. A cell can support up to two lines of text and one image. A cell cannot be empty.

Grid examples are shown in the images below:



GridRowBuilder example - Nearby restaurants

The example below demonstrates a grid row that contains images and text.

Kotlin

fun createSliceWithGridRow(sliceUri: Uri): Slice {
    // Create the parent builder.
    return ListBuilder(context, sliceUri, ListBuilder.INFINITY)
            .setHeader {
                it.apply {
                    setTitle("Nearby restaurant")
                    setPrimaryAction(SliceAction(pendingIntent, icon, "Nearby restaurant"))
                }
            }
            .addGridRow {
                it.apply {
                    addCell {
                        it.apply {
                            addImage(image1, LARGE_IMAGE)
                            addTitleText("Frances")
                            addText("0.3 mil")
                            setContentIntent(intent1)
                        }
                    }
                    addCell {
                        it.apply {
                            addImage(image2, LARGE_IMAGE)
                            addTitleText("Starbelly")
                            addText("0.5 mil")
                            setContentIntent(intent2)
                        }
                    }
                    addCell {
                        it.apply {
                            addImage(image3, LARGE_IMAGE)
                            addTitleText("Julia's Cafe")
                            addText("0.9 mi")
                            setContentIntent(intent3)
                        }
                    }
                    addCell {
                        it.apply {
                            addImage(image4, LARGE_IMAGE)
                            addTitleText("Ushio Ramen")
                            addText("1.2 mi")
                            setContentIntent(intent4)
                        }
                    }
                }
            }
            .build()
}

Java

public Slice createSliceWithGridRow(Uri sliceUri) {
    // Create the parent builder.
    ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY);

    // Create the header.
    HeaderBuilder hb = new HeaderBuilder(listBuilder);
    hb.setTitle("Famous restaurants");
    hb.setPrimaryAction(new SliceAction(pendingIntent, icon, "Nearby restaurant"));
    listBuilder.setHeader(hb);

    // Create the grid.
    GridRowBuilder gb = new GridRowBuilder(listBuilder);

    // Create the cells.
    GridRowBuilder.CellBuilder cell1 = new GridRowBuilder.CellBuilder(gb)
            .addImage(image1, LARGE_IMAGE)
            .addTitleText("Frances")
            .addText("0.3 mil")
            .setContentIntent(intent1);
    GridRowBuilder.CellBuilder cell2 = new GridRowBuilder.CellBuilder(gb)
            .addImage(image2, LARGE_IMAGE)
            .addTitleText("Starbelly")
            .addText("0.5 mil")
            .setContentIntent(intent2);
    GridRowBuilder.CellBuilder cell3 = new GridRowBuilder.CellBuilder(gb)
            .addImage(image3, LARGE_IMAGE)
            .addTitleText("Julia's Cafe")
            .addText("0.9 mi")
            .setContentIntent(intent3);
    GridRowBuilder.CellBuilder cell4 = new GridRowBuilder.CellBuilder(gb)
            .addImage(image4, LARGE_IMAGE)
            .addTitleText("Ushio Ramen")
            .addText("1.2 mi")
            .setContentIntent(intent4);

    // Add cells to the grid.
    gb.addCell(cell1).addCell(cell2).addCell(cell3).addCell(cell4);

    // Add the grid to the list.
    listBuilder.addGridRow(gb);

    return listBuilder.build();
}

RangeBuilder

With a RangeBuilder, you can create a row that contains either a progress bar or an input range, such as a slider.

Progress and slider examples are shown in the images below:


RangeBuilder example - Slider

The example below demonstrates how to build a Slice that contains a volume slider by using an InputRangeBuilder. To construct a progress row, use ListBuilder#addRange(RangeBuilder).

Kotlin

fun createSliceWithRange(sliceUri: Uri): Slice {
    return ListBuilder(context, sliceUri, ListBuilder.INFINITY)
            .addInputRange {
                it.apply {
                    setTitle("Ring Volume")
                    setInputAction(volumeChangedPendingIntent)
                    setMax(100)
                    setValue(30)
                }
            }
            .build()
}

Java

public Slice createSliceWithRange(Uri sliceUri) {
    // Construct the parent.
    ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY);

    // Create the input row.
    ListBuilder.InputRangeBuilder volumeSliderRow = new ListBuilder.InputRangeBuilder(listBuilder)
            .setTitle("Ring Volume")
            .setInputAction(volumeChangedPendingIntent)
            .setMax(100)
            .setValue(30);

    // Add the row to the list.
    listBuilder.addInputRange(volumeSliderRow);

    return listBuilder.build();
}

Delayed content

You should return a Slice as quickly as possible from SliceProvider#onBindSlice. Time-consuming calls can lead to display issues, such as flickering and abrupt resizing.

If you have Slice content that cannot be loaded quickly, you can construct your Slice with null or placeholder content while noting in the builder that the content is loading. Once the content is ready to be displayed, call getContentResolver().notifyChange(sliceUri) using your Slice URI. This results in another call to SliceProvider#onBindSlice, where you can construct the Slice again with fresh content.

Delayed content example - Ride to work

In the Ride to work row below, the distance to work is determined dynamically and might not be available immediately. The example code demonstrates using a null subtitle as a placeholder while the content loads:

Kotlin

fun createSliceShowingLoading(sliceUri: Uri): Slice {
    // We’re waiting to load the time to work so indicate that on the slice by
    // setting the subtitle with the overloaded method and indicate true.
    return ListBuilder(context, sliceUri, ListBuilder.INFINITY)
            .addRow {
                it.apply {
                    setTitle("Ride to work")
                    setSubtitle(null, true)
                    addEndItem(IconCompat.createWithResource(context, R.drawable.ic_work), ICON_IMAGE)
                }
            }
            .build()
}

Java

public Slice createSliceShowingLoading(Uri sliceUri) {
    // Construct the parent.
    ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY);

    // Construct the row.
    RowBuilder rowBuilder1 = new RowBuilder(listBuilder)
            .setTitle("Ride to work");

    // We’re waiting to load the time to work so indicate that on the slice by
    // setting the subtitle with the overloaded method and indicate true.
    rowBuilder1.setSubtitle(null, true);
    rowBuilder1.addEndItem(IconCompat.createWithResource(getContext(), R.drawable.ic_work), ICON_IMAGE);

    // Add the rows to the list
    listBuilder.addRow(rowBuilder1);

    return listBuilder.build();
}

Handle disabled scrolling within your Slice

The surface presenting your Slice template may not support scrolling within the template. In this case, some of your content might not be displayed.

As an example, consider a Slice showing a list of WiFi networks:

If the WiFi list is long, and if scrolling is disabled, you can add a See more button to ensure that users have a way to see all items in the list. You can add this button by using addSeeMoreAction:

Kotlin

listBuilder.addSeeMoreAction(seeAllNetworksPendingIntent)

Java

listBuilder.addSeeMoreAction(seeAllNetworksPendingIntent);

This displays as shown in the example image below:

Tapping on See more sends seeAllNetworksPendingIntent.

Alternatively, if you’d like to provide a custom message or row, consider adding a RowBuilder:

Kotlin

listBuilder.setSeeMoreRow {
    it.apply {
        setTitle("See all available networks")
        addEndItem(IconCompat.createWithResource(context, R.drawable.ic_right_caret), ICON_IMAGE)
        setPrimaryAction(SliceAction(seeAllNetworksPendingIntent,
                IconCompat.createWithResource(context, R.drawable.ic_wifi),
                "Wi-Fi Networks"))
    }
}

Java

RowBuilder rowBuilder = new RowBuilder(listBuilder)
        .setTitle("See all available networks")
        .addEndItem(IconCompat.createWithResource(getContext(), R.drawable.ic_right_caret), ICON_IMAGE)
        .setPrimaryAction(new SliceAction(seeAllNetworksPendingIntent,
                IconCompat.createWithResource(getContext(), R.drawable.ic_wifi),
                "Wi-Fi Networks"));
listBuilder.setSeeMoreRow(rowBuilder);

The row or action added via this method is displayed only when one of the following conditions is met:

  • The presenter of your Slice has disabled scrolling on the view
  • Not all of your rows can be displayed in the space available

Combine templates

You can create a rich, dynamic Slice by combining multiple row types. For example, the Slice below contains a header row, a grid with a single image, and a grid with two cells of text.

The Slice below contains a header row along with a grid that contains three cells.