タイルに定期更新を表示する

時間の経過とともに変化するコンテンツを含むタイルを作成します。

タイムラインと連携する

タイムラインは 1 つ以上の TimelineEntry インスタンスで構成されます。各インスタンスには、特定の時間間隔で表示されるレイアウトが含まれます。すべてのタイルにタイムラインが必要です。

タイルのタイムラインの図

単一エントリのタイル

多くの場合、タイルは 1 つの TimelineEntry で記述できます。レイアウトは固定であり、レイアウト内の情報だけが変化します。たとえば、その日のフィットネスの進捗状況を表示するタイルには、常に同じ進行状況レイアウトが表示されますが、レイアウトを調整してさまざまな値を表示することもできます。この場合、コンテンツが変化するタイミングを事前に知ることはできません。

TimelineEntry が 1 つであるタイルの例を次に示します。

Kotlin

override fun onTileRequest(
    requestParams: TileRequest
): ListenableFuture<Tile> {
    val tile = Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)

        // We add a single timeline entry when our layout is fixed, and
        // we don't know in advance when its contents might change.
        .setTileTimeline(
            Timeline.fromLayoutElement(...)
        ).build()
    return Futures.immediateFuture(tile)
}

Java

@Override
protected ListenableFuture<Tile> onTileRequest(
       @NonNull TileRequest requestParams
) {
   Tile tile = new Tile.Builder()
       .setResourcesVersion(RESOURCES_VERSION)
       
       // We add a single timeline entry when our layout is fixed, and
       // we don't know in advance when its contents might change.
       .setTileTimeline(
            Timeline.fromLayoutElement(...)
       ).build();
   return Futures.immediateFuture(tile);
}

期限付きのタイムライン エントリ

TimelineEntry はオプションで有効期間を定義できます。これにより、アプリが新しいタイルをプッシュしなくても、タイルのレイアウトを所定のタイミングで変更できます。

典型的な例としては、タイムラインに今後のイベントのリストが含まれる予定リストのタイルがあります。今後の各イベントには表示するタイミングを指定する有効期間が含まれます。

Tiles API を使用すると、有効期間を重複させることができます。残り時間の最も短いものが表示されます。一度に表示されるイベントは 1 つだけです。

デベロッパーはデフォルトのフォールバック エントリを提供できます。たとえば、次のコードサンプルに示すように、予定リストのタイルに有効期間が無限のタイルを設定できます。これは、他のタイムライン エントリが有効でない場合に使用されます。

Kotlin

public override fun onTileRequest(
    requestParams: TileRequest
): ListenableFuture<Tile> {
    val timeline = Timeline.Builder()

    // Add fallback "no meetings" entry
    // Use the version of TimelineEntry that's in androidx.wear.protolayout.
    timeline.addTimelineEntry(TimelineEntry.Builder()
        .setLayout(getNoMeetingsLayout())
        .build()
    )

    // Retrieve a list of scheduled meetings
    val meetings = MeetingsRepo.getMeetings()
    // Add a timeline entry for each meeting
    meetings.forEach { meeting ->
        timeline.addTimelineEntry(TimelineEntry.Builder()
            .setLayout(getMeetingLayout(meeting))
            .setValidity(
                // The tile should disappear when the meeting begins
                // Use the version of TimeInterval that's in
                // androidx.wear.protolayout.
                TimeInterval.Builder()
                    .setEndMillis(meeting.dateTimeMillis).build()
            ).build()
        )
    }

    val tile = Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setTileTimeline(timeline.build())
        .build()
    return Futures.immediateFuture(tile)
}

Java

@Override
protected ListenableFuture<Tile> onTileRequest(
       @NonNull RequestBuilders.TileRequest requestParams
) {
   Timeline.Builder timeline = new Timeline.Builder();
   // Add fallback "no meetings" entry
   // Use the version of TimelineEntry that's in androidx.wear.protolayout.
   timeline.addTimelineEntry(new TimelineEntry.Builder().setLayout(getNoMeetingsLayout()).build());
   // Retrieve a list of scheduled meetings
   List<Meeting> meetings = MeetingsRepo.getMeetings();
   // Add a timeline entry for each meeting
   for(Meeting meeting : meetings) {
        timeline.addTimelineEntry(new TimelineEntry.Builder()
            .setLayout(getMeetingLayout(meeting))
            .setValidity(
                // The tile should disappear when the meeting begins
                // Use the version of TimeInterval that's in
                // androidx.wear.protolayout.
                new TimeInterval.builder()
                    .setEndMillis(meeting.getDateTimeMillis()).build()
            ).build()
        );
    }

    Tile tile = new Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setTileTimeline(timeline.build())
        .build();
    return Futures.immediateFuture(tile);
}

タイルを更新する

タイルに表示される情報は、時間が経つと失効することがあります。たとえば、一日中同じ気温を表示する天気タイルは正確ではありません。

データの失効を処理するには、タイルを作成するときに更新間隔を設定して、タイルの有効期間を指定します。天気タイルの例では、次のコードサンプルに示すように、コンテンツを 1 時間に 1 回更新します。

Kotlin

override fun onTileRequest(requestParams: RequestBuilders.TileRequest) =
    Futures.immediateFuture(Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setFreshnessIntervalMillis(60 * 60 * 1000) // 60 minutes
        .setTileTimeline(Timeline.fromLayoutElement(
            getWeatherLayout())
        ).build()
    )

Java

@Override
protected ListenableFuture<Tile> onTileRequest(
       @NonNull TileRequest requestParams
) {
    return Futures.immediateFuture(new Tile.Builder()
        .setResourcesVersion(RESOURCES_VERSION)
        .setFreshnessIntervalMillis(60 * 60 * 1000) // 60 minutes
        .setTimeline(Timeline.fromLayoutElement(
            getWeatherLayout())
        ).build());
}

更新間隔を設定すると、更新間隔が終了した直後に onTileRequest() が呼び出されます。更新間隔を設定しなかった場合、onTileRequest() は呼び出されません。

タイルは、外部のイベントによって失効することもあります。たとえば、ユーザーがカレンダーから会議を削除しても、タイルが更新されなければ、削除した会議がタイルに表示されたままになります。この場合は、次のコードサンプルに示すように、アプリコードの任意の場所から更新をリクエストします。

Kotlin

fun eventDeletedCallback() {
     TileService.getUpdater(context)
             .requestUpdate(MyTileService::class.java)
}

Java

public void eventDeletedCallback() {
   TileService.getUpdater(context)
           .requestUpdate(MyTileService.class);
}

更新ワークフローを選択する

次のベスト プラクティスを使用して、タイルの更新方法を決定してください。

  • ユーザーのカレンダーの次の予定など、更新が予測できる場合は、タイムラインを使用します。
  • プラットフォームのデータを取得するときは、データ バインディングを使用して、システムが自動的にデータを更新するようにします。
  • 日の出タイルの画像位置の更新など、デバイス上で更新を短時間で計算できる場合は、onTileRequest() を使用します。

    これはすべての画像を事前に生成する必要がある場合に特に便利です。後で新しい画像を生成する必要がある場合は、setFreshnessIntervalMillis() を呼び出します。

  • 気象データのポーリングなど、集中的なバックグラウンドでの作業を繰り返し行う場合は、WorkManager を使用してタイルに更新をプッシュします。

  • ライトのオン、メールの受信、メモの更新などの外部イベントに対応して更新する場合は、Firebase Cloud Messaging(FCM)メッセージを送信してアプリを再度アクティブにし、更新をタイルにプッシュします

  • タイルデータの同期プロセスが高コストになる可能性がある場合は、次のように対処します。

    1. データ同期のスケジュールを設定します。
    2. タイマー(1~2 秒)を開始します。
    3. 時間切れになる前にリモートのデータソースから更新を受信した場合は、データ同期からの更新された値を表示します。それ以外の場合は、キャッシュに保存されたローカル値を表示します。