Fix redundant calls to loadData with timelines
Previously selectComplicationDataForInstant was calling
toApiComplicationData for timelines, meaning the === reference
equality test below would always fail. This meant complications
where getting reloaded every frame leading to battery drain.
Bug: 331220998
Test: Manual testing plus new unit test
Change-Id: I89c984d1d1d3c22ea8b6535b87d5f063f80e4737
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
index 57bcabe..5d3c6df 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
@@ -22,7 +22,6 @@
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
-import android.support.wearable.complications.ComplicationData as WireComplicationData
import androidx.annotation.ColorInt
import androidx.annotation.IntDef
import androidx.annotation.Px
@@ -50,6 +49,7 @@
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime
+import java.util.ArrayList
import java.util.Objects
import kotlin.math.abs
import kotlin.math.atan2
@@ -1047,7 +1047,13 @@
* [complicationData] is selected.
*/
private var timelineComplicationData: ComplicationData = NoDataComplicationData()
- private var timelineEntries: List<WireComplicationData>? = null
+ private var timelineEntries: List<ApiTimelineEntry>? = null
+
+ private class ApiTimelineEntry(
+ val timelineStartEpochSecond: Long?,
+ val timelineEndEpochSecond: Long?,
+ val complicationData: ComplicationData
+ )
/**
* Sets the current [ComplicationData] and if it's a timeline, the correct override for
@@ -1098,7 +1104,15 @@
private fun setTimelineData(data: ComplicationData, instant: Instant) {
lastComplicationUpdate = instant
timelineComplicationData = data
- timelineEntries = data.asWireComplicationData().timelineEntries?.toList()
+ timelineEntries = data.asWireComplicationData()
+ .timelineEntries
+ ?.mapTo(ArrayList<ApiTimelineEntry>()) {
+ ApiTimelineEntry(
+ it.timelineStartEpochSecond,
+ it.timelineEndEpochSecond,
+ it.toApiComplicationData()
+ )
+ }
}
private fun loadData(data: ComplicationData, loadDrawablesAsynchronous: Boolean = false) {
@@ -1121,14 +1135,14 @@
// Select the shortest valid timeline entry.
timelineEntries?.let {
- for (wireEntry in it) {
- val start = wireEntry.timelineStartEpochSecond
- val end = wireEntry.timelineEndEpochSecond
+ for (entry in it) {
+ val start = entry.timelineStartEpochSecond
+ val end = entry.timelineEndEpochSecond
if (start != null && end != null && time >= start && time < end) {
val duration = end - start
if (duration < previousShortest) {
previousShortest = duration
- best = wireEntry.toApiComplicationData()
+ best = entry.complicationData
}
}
}
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index 16c1bc4..9c34467 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -5681,6 +5681,44 @@
}
@Test
+ public fun selectComplicationDataForInstant_withTimelineDoesntUpdateWithNoChange() {
+ val complication =
+ WireComplicationData.Builder(WireComplicationData.TYPE_SHORT_TEXT)
+ .setShortText(WireComplicationText.plainText("A"))
+ .build()
+ complication.setTimelineEntryCollection(
+ listOf(
+ WireComplicationData.Builder(WireComplicationData.TYPE_SHORT_TEXT)
+ .setShortText(WireComplicationText.plainText("B"))
+ .build()
+ .apply {
+ timelineStartEpochSecond = 1000
+ timelineEndEpochSecond = 4000
+ },
+ WireComplicationData.Builder(WireComplicationData.TYPE_SHORT_TEXT)
+ .setShortText(WireComplicationText.plainText("C"))
+ .build()
+ .apply {
+ timelineStartEpochSecond = 2000
+ timelineEndEpochSecond = 3000
+ }
+ )
+ )
+ initWallpaperInteractiveWatchFaceInstance(complicationSlots = listOf(mockComplication))
+ engineWrapper.setComplicationDataList(
+ listOf(IdAndComplicationDataWireFormat(LEFT_COMPLICATION_ID, complication))
+ )
+ complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(1000))
+ reset(mockCanvasComplication)
+
+ // Calling selectComplicationDataForInstant again with another time inside the same timeline
+ // entry should not result in a call to loadData.
+ complicationSlotsManager.selectComplicationDataForInstant(Instant.ofEpochSecond(1050))
+
+ verifyNoMoreInteractions(mockCanvasComplication)
+ }
+
+ @Test
@Config(sdk = [Build.VERSION_CODES.R])
public fun renderParameters_isScreenshot() {
initWallpaperInteractiveWatchFaceInstance(