Get started with tiles

To start providing tiles from your app, include the following dependencies in your app's build.gradle file.

Groovy

dependencies {
    // Use to implement support for wear tiles
    implementation "androidx.wear.tiles:tiles:1.5.0-alpha07"

    // Use to utilize standard components and layouts in your tiles
    implementation "androidx.wear.protolayout:protolayout:1.3.0-alpha07"

    // Use to utilize components and layouts with Material Design in your tiles
    implementation "androidx.wear.protolayout:protolayout-material:1.3.0-alpha07"

    // Use to include dynamic expressions in your tiles
    implementation "androidx.wear.protolayout:protolayout-expression:1.3.0-alpha07"

    // Use to preview wear tiles in your own app
    debugImplementation "androidx.wear.tiles:tiles-renderer:1.5.0-alpha07"

    // Use to fetch tiles from a tile provider in your tests
    testImplementation "androidx.wear.tiles:tiles-testing:1.5.0-alpha07"
}

Kotlin

dependencies {
    // Use to implement support for wear tiles
    implementation("androidx.wear.tiles:tiles:1.5.0-alpha07")

    // Use to utilize standard components and layouts in your tiles
    implementation("androidx.wear.protolayout:protolayout:1.3.0-alpha07")

    // Use to utilize components and layouts with Material Design in your tiles
    implementation("androidx.wear.protolayout:protolayout-material:1.3.0-alpha07")

    // Use to include dynamic expressions in your tiles
    implementation("androidx.wear.protolayout:protolayout-expression:1.3.0-alpha07")

    // Use to preview wear tiles in your own app
    debugImplementation("androidx.wear.tiles:tiles-renderer:1.5.0-alpha07")

    // Use to fetch tiles from a tile provider in your tests
    testImplementation("androidx.wear.tiles:tiles-testing:1.5.0-alpha07")
}

Key concepts

Tiles aren't built in the same way as Android apps, and make use of different concepts:

  • Layout templates: Define the overall arrangement of visual elements on the display. A tile uses either an EdgeContentLayout layout template, which includes a progress indicator around the edge of the display, or a PrimaryLayout, which doesn't show this indicator.
  • Layout elements: Represent an individual graphical element, such a Button or Chip, or several such elements grouped together using a Column, MultiButtonLayout, MultiSlotLayout or similar. These are embedded within a layout template.
  • Resources: ResourceBuilders.Resources objects consist of a map of key-value pairs of the Android resources (images) that are required to render a layout, and a version.
  • Timeline: A TimelineBuilders.Timeline object is a list of one or more instances of a layout object. You can provide various mechanisms and expressions to indicate when the renderer should switch from one layout object to another, such as to stop showing a layout at a specific time.
  • State: A data structure of type StateBuilders.State that is passed between tile and app, to enable the two components to communicate with each other. For example, if a button is tapped on the tile, the state holds the ID of the button. You can also exchange data types using a map.
  • Tile: A TileBuilders.Tile object representing a tile, consisting of a timeline, a resources version ID, freshness interval and state.
  • Protolayout: This term appears in the name of various tiles-related classes and refers to the Wear OS Protolayout library, a graphics library that's used across various Wear OS surfaces.

Create a tile

To provide a tile from your app, implement a service of type TileService and register it in your manifest. From this, the system requests the necessary tiles during calls to onTileRequest() and resources during calls to onTileResourcesRequest().

class MyTileService : TileService() {

    override fun onTileRequest(requestParams: RequestBuilders.TileRequest) =
        Futures.immediateFuture(
            Tile.Builder()
                .setResourcesVersion(RESOURCES_VERSION)
                .setTileTimeline(
                    Timeline.fromLayoutElement(
                        Text.Builder(this, "Hello World!")
                            .setTypography(Typography.TYPOGRAPHY_BODY1)
                            .setColor(argb(0xFFFFFFFF.toInt()))
                            .build()
                    )
                )
                .build()
        )

    override fun onTileResourcesRequest(requestParams: ResourcesRequest) =
        Futures.immediateFuture(
            Resources.Builder()
                .setVersion(RESOURCES_VERSION)
                .build()
        )
}

Next, add a service inside the <application> tag of your AndroidManifest.xml file.

<service
    android:name=".snippets.tile.MyTileService"
    android:label="@string/tile_label"
    android:description="@string/tile_description"
    android:icon="@mipmap/ic_launcher"
    android:exported="true"
    android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER">
    <intent-filter>
        <action android:name="androidx.wear.tiles.action.BIND_TILE_PROVIDER" />
    </intent-filter>

    <meta-data android:name="androidx.wear.tiles.PREVIEW"
        android:resource="@drawable/tile_preview" />
</service>

The permission and intent filter register this service as a tile provider.

The icon, label, description and preview resource is shown to the user when they configure tiles on their phone or watch.

Deploy your app, and add the Tile to the Tiles Carousel (there's are a more developer-friendly way to preview a Tile too, but for now just do it manually).

Figure 1. "Hello World" Tile.

For a complete example, see the code sample on GitHub or the codelab.

Create UI for tiles

The layout of a tile is written using a builder pattern. A tile's layout is built up like a tree that consists of layout containers and basic layout elements. Each layout element has properties, which you can set through various setter methods.

Basic layout elements

The following visual elements from the protolayout library are supported, along with Material components:

  • Text: renders a string of text, optionally wrapping.
  • Image: renders an image.
  • Spacer: provides padding between elements or can act as a divider when you set its background color.

Material components

In addition to basic elements, the protolayout-material library provides components that ensure a tile design in line with Material Design user interface recommendations.

  • Button: clickable circular component designed to contain an icon.
  • Chip: clickable stadium-shaped component designed to contain up to two lines of text and an optional icon.

  • CompactChip: clickable stadium-shaped component designed to contain a line of text.

  • TitleChip: clickable stadium-shaped component similar to Chip but with a larger height to accommodate title text.

  • CircularProgressIndicator: circular progress indicator that can be placed inside a EdgeContentLayout to display progress around the edges of the screen.

Layout containers

The following containers are supported, along with Material layouts:

  • Row: lays child elements out horizontally, one after another.
  • Column: lays child elements out vertically, one after another.
  • Box: overlays child elements on top of one another.
  • Arc: lays child elements out in a circle.
  • Spannable: applies specific FontStyles to sections of text along with interleaving text and images. For more information, see Spannables.

Every container can contain one or more children, which themselves can also be containers. For example, a Column can contain multiple Row elements as children, resulting in a grid-like layout.

As an example, a tile with a container layout and two child layout elements could look like this:

Kotlin

private fun myLayout(): LayoutElement =
    Row.Builder()
        .setWidth(wrap())
        .setHeight(expand())
        .setVerticalAlignment(VERTICAL_ALIGN_BOTTOM)
        .addContent(Text.Builder()
            .setText("Hello world")
            .build()
        )
        .addContent(Image.Builder()
            .setResourceId("image_id")
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .build()
        ).build()

Java

private LayoutElement myLayout() {
    return new Row.Builder()
        .setWidth(wrap())
        .setHeight(expand())
        .setVerticalAlignment(VALIGN_BOTTOM)
        .addContent(new Text.Builder()
            .setText("Hello world")
            .build()
        )
        .addContent(new Image.Builder()
            .setResourceId("image_id")
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .build()
        ).build();
}

Material layouts

In addition to basic layouts, the protolayout-material library provides a few opinionated layouts made to hold elements in specific "slots".

  • PrimaryLayout: positions a single primary action CompactChip at the bottom with the content centered above it.

  • MultiSlotLayout: positions primary and secondary labels with optional content in between and an optional CompactChip at the bottom.

  • MultiButtonLayout: positions a set of buttons that are arranged according to Material guidelines.

  • EdgeContentLayout: positions content around the edge of a screen, such as a CircularProgressIndicator. When using this layout, the content within it has the appropriate margins and padding applied automatically.

Arcs

The following Arc container children are supported:

  • ArcLine: renders a curved line around the Arc.
  • ArcText: renders curved text in the Arc.
  • ArcAdapter: renders a basic layout element in the arc, drawn at a tangent to the arc.

For more information, see the reference documentation for each of the element types.

Modifiers

Every available layout element can optionally have modifiers applied to it. Use these modifiers for the following purposes:

  • Change the visual appearance of the layout. For example, add a background, border, or padding to your layout element.
  • Add metadata about the layout. For example, add a semantics modifier to your layout element for use with screen readers.
  • Add functionality. For example, add a clickable modifier to your layout element to make your tile interactive. For more information, see Interact with tiles.

For example, we can customize the default look and metadata of an Image, as shown in the following code sample:

Kotlin

private fun myImage(): LayoutElement =
    Image.Builder()
        .setWidth(dp(24f))
        .setHeight(dp(24f))
        .setResourceId("image_id")
        .setModifiers(Modifiers.Builder()
            .setBackground(Background.Builder().setColor(argb(0xFFFF0000)).build())
            .setPadding(Padding.Builder().setStart(dp(12f)).build())
            .setSemantics(Semantics.builder()
                .setContentDescription("Image description")
                .build()
            ).build()
        ).build()

Java

private LayoutElement myImage() {
   return new Image.Builder()
           .setWidth(dp(24f))
           .setHeight(dp(24f))
           .setResourceId("image_id")
           .setModifiers(new Modifiers.Builder()
                   .setBackground(new Background.Builder().setColor(argb(0xFFFF0000)).build())
                   .setPadding(new Padding.Builder().setStart(dp(12f)).build())
                   .setSemantics(new Semantics.Builder()
                           .setContentDescription("Image description")
                           .build()
                   ).build()
           ).build();
}

Spannables

A Spannable is a special type of container that lays out elements similarly to text. This is useful when you want to apply a different style to only one substring in a larger block of text, something that isn't possible with the Text element.

A Spannable container is filled with Span children. Other children, or nested Spannable instances, aren't allowed.

There are two types of Span children:

  • SpanText: renders text with a specific style.
  • SpanImage: renders an image inline with text.

For example, you could italicize "world" in a "Hello world" tile and insert an image between the words, as shown in the following code sample:

Kotlin

private fun mySpannable(): LayoutElement =
    Spannable.Builder()
        .addSpan(SpanText.Builder()
            .setText("Hello ")
            .build()
        )
        .addSpan(SpanImage.Builder()
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .setResourceId("image_id")
            .build()
        )
        .addSpan(SpanText.Builder()
            .setText("world")
            .setFontStyle(FontStyle.Builder()
                .setItalic(true)
                .build())
            .build()
        ).build()

Java

private LayoutElement mySpannable() {
   return new Spannable.Builder()
        .addSpan(new SpanText.Builder()
            .setText("Hello ")
            .build()
        )
        .addSpan(new SpanImage.Builder()
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .setResourceId("image_id")
            .build()
        )
        .addSpan(new SpanText.Builder()
            .setText("world")
            .setFontStyle(newFontStyle.Builder()
                .setItalic(true)
                .build())
            .build()
        ).build();
}

Work with resources

Tiles don't have access to any of your app's resources. This means that you can't pass an Android image ID to an Image layout element and expect it to resolve. Instead, override the onTileResourcesRequest() method and provide any resources manually.

There are two ways to provide images within the onTileResourcesRequest() method:

Kotlin

override fun onTileResourcesRequest(
    requestParams: ResourcesRequest
) = Futures.immediateFuture(
Resources.Builder()
    .setVersion("1")
    .addIdToImageMapping("image_from_resource", ImageResource.Builder()
        .setAndroidResourceByResId(AndroidImageResourceByResId.Builder()
            .setResourceId(R.drawable.image_id)
            .build()
        ).build()
    )
    .addIdToImageMapping("image_inline", ImageResource.Builder()
        .setInlineResource(InlineImageResource.Builder()
            .setData(imageAsByteArray)
            .setWidthPx(48)
            .setHeightPx(48)
            .setFormat(ResourceBuilders.IMAGE_FORMAT_RGB_565)
            .build()
        ).build()
    ).build()
)

Java

@Override
protected ListenableFuture<Resources> onTileResourcesRequest(
       @NonNull ResourcesRequest requestParams
) {
return Futures.immediateFuture(
    new Resources.Builder()
        .setVersion("1")
        .addIdToImageMapping("image_from_resource", new ImageResource.Builder()
            .setAndroidResourceByResId(new AndroidImageResourceByResId.Builder()
                .setResourceId(R.drawable.image_id)
                .build()
            ).build()
        )
        .addIdToImageMapping("image_inline", new ImageResource.Builder()
            .setInlineResource(new InlineImageResource.Builder()
                .setData(imageAsByteArray)
                .setWidthPx(48)
                .setHeightPx(48)
                .setFormat(ResourceBuilders.IMAGE_FORMAT_RGB_565)
                .build()
            ).build()
        ).build()
);
}