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(