AndroidX implementation for generic PlatformDataProvider.

Test: Unit tests
Bug: 337187364
Change-Id: I8a43669887698f487c2a142fdc774dcc981b0193
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/DynamicBoolDataPlatformProvider.kt b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/DynamicTypePlatformDataProvider.kt
similarity index 63%
rename from wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/DynamicBoolDataPlatformProvider.kt
rename to wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/DynamicTypePlatformDataProvider.kt
index 811624c..848f088 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/DynamicBoolDataPlatformProvider.kt
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/DynamicTypePlatformDataProvider.kt
@@ -19,6 +19,8 @@
 import androidx.annotation.RestrictTo.Scope
 import androidx.annotation.UiThread
 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicInt32
+import androidx.wear.protolayout.expression.DynamicBuilders.DynamicType
 import androidx.wear.protolayout.expression.DynamicDataBuilders.DynamicDataValue
 import androidx.wear.protolayout.expression.PlatformDataKey
 import androidx.wear.protolayout.expression.PlatformDataValues
@@ -26,25 +28,28 @@
 import androidx.wear.protolayout.expression.pipeline.PlatformDataReceiver
 import java.util.concurrent.Executor
 
-/** A [PlatformDataProvider] that provides a boolean value. */
+/** A [PlatformDataProvider] that provides a dynamic type. */
 @RestrictTo(Scope.LIBRARY_GROUP)
-public class DynamicBoolPlatformDataProvider(
-    private val key: PlatformDataKey<DynamicBool>,
-    initialValue: Boolean,
+public class DynamicTypePlatformDataProvider<RawT, T : DynamicType>
+private constructor(
+    private val key: PlatformDataKey<T>,
+    initialValue: RawT,
+    private val transform: (RawT) -> DynamicDataValue<T>,
 ) : PlatformDataProvider {
 
     private var receiver: PlatformDataReceiver? = null
     private var executor: Executor? = null
 
-    private var value: Boolean = initialValue
     private var updatesEnabled: Boolean = true
 
-    /** Sets and notifies a new value */
-    @UiThread
-    public fun setValue(value: Boolean) {
-        this.value = value
-        notifyReceiver()
-    }
+    @set:UiThread
+    @get:UiThread
+    public var value: RawT = initialValue
+        /** Sets and notifies a new value */
+        set(value) {
+            field = value
+            notifyReceiver()
+        }
 
     /** Sets whether this consumer can send updates on the registered receiver. */
     @UiThread
@@ -79,7 +84,26 @@
             return
         }
 
-        val data = PlatformDataValues.of(key, DynamicDataValue.fromBool(value))
+        val data = PlatformDataValues.of(key, transform(value))
         receiver?.let { executor?.execute { it.onData(data) } }
     }
+
+    public companion object {
+
+        /** Creates a new [PlatformDataProvider] for a [DynamicBool]. */
+        @JvmStatic
+        public fun forDynamicBool(
+            key: PlatformDataKey<DynamicBool>,
+            initialValue: Boolean
+        ): DynamicTypePlatformDataProvider<Boolean, DynamicBool> =
+            DynamicTypePlatformDataProvider(key, initialValue) { DynamicDataValue.fromBool(it) }
+
+        /** Creates a new [PlatformDataProvider] for a [DynamicInt32]. */
+        @JvmStatic
+        public fun forDynamicInt32(
+            key: PlatformDataKey<DynamicInt32>,
+            initialValue: Int,
+        ): DynamicTypePlatformDataProvider<Int, DynamicInt32> =
+            DynamicTypePlatformDataProvider(key, initialValue) { DynamicDataValue.fromInt(it) }
+    }
 }
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java
index 7b8d57f..639fab4 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java
@@ -40,6 +40,7 @@
 import androidx.collection.ArrayMap;
 import androidx.collection.ArraySet;
 import androidx.vectordrawable.graphics.drawable.SeekableAnimatedVectorDrawable;
+import androidx.wear.protolayout.expression.DynamicBuilders;
 import androidx.wear.protolayout.expression.PlatformDataKey;
 import androidx.wear.protolayout.expression.PlatformEventSources;
 import androidx.wear.protolayout.expression.ProtoLayoutExperimental;
@@ -108,8 +109,10 @@
     final @NonNull QuotaManager mAnimationQuotaManager;
     private final @NonNull DynamicTypeEvaluator mEvaluator;
     private final @NonNull PlatformTimeUpdateNotifierImpl mTimeNotifier;
-    private final @NonNull DynamicBoolPlatformDataProvider mVisibilityStatusDataProvider;
-    private final @NonNull DynamicBoolPlatformDataProvider mLayoutUpdatePendingDataProvider;
+    private final @NonNull DynamicTypePlatformDataProvider<Boolean, DynamicBuilders.DynamicBool>
+            mVisibilityStatusDataProvider;
+    private final @NonNull DynamicTypePlatformDataProvider<Boolean, DynamicBuilders.DynamicBool>
+            mLayoutUpdatePendingDataProvider;
 
     /** Creates a {@link ProtoLayoutDynamicDataPipeline} without animation support. */
     @RestrictTo(Scope.LIBRARY_GROUP)
@@ -168,15 +171,15 @@
         // Add additional provider for visibility status. It's not needed to come from external
         // callers, as this pipeline knows visibility status
         mVisibilityStatusDataProvider =
-                new DynamicBoolPlatformDataProvider(
+                DynamicTypePlatformDataProvider.forDynamicBool(
                         PlatformEventSources.Keys.LAYOUT_VISIBILITY, mFullyVisible);
         evaluatorConfigBuilder.addPlatformDataProvider(
                 mVisibilityStatusDataProvider,
                 ImmutableSet.of(PlatformEventSources.Keys.LAYOUT_VISIBILITY));
 
-        // Add an additional provider for Platform loading status.
+        // Add an additional provider for platform layout update state.
         mLayoutUpdatePendingDataProvider =
-                new DynamicBoolPlatformDataProvider(
+                DynamicTypePlatformDataProvider.forDynamicBool(
                         PlatformEventSources.Keys.LAYOUT_UPDATE_PENDING, /* initialValue= */ false);
         evaluatorConfigBuilder.addPlatformDataProvider(
                 mLayoutUpdatePendingDataProvider,
diff --git a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/DynamicBoolPlatformDataProviderTest.kt b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/DynamicTypePlatformDataProviderTest.kt
similarity index 90%
rename from wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/DynamicBoolPlatformDataProviderTest.kt
rename to wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/DynamicTypePlatformDataProviderTest.kt
index f175f15..701e436 100644
--- a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/DynamicBoolPlatformDataProviderTest.kt
+++ b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/DynamicTypePlatformDataProviderTest.kt
@@ -31,10 +31,13 @@
 import org.mockito.kotlin.verify
 
 @RunWith(AndroidJUnit4::class)
-class DynamicBoolPlatformDataProviderTest {
+class DynamicTypePlatformDataProviderTest {
 
     private val provider =
-        DynamicBoolPlatformDataProvider(PlatformDataKey<DynamicBuilders.DynamicBool>("key"), false)
+        DynamicTypePlatformDataProvider.forDynamicBool(
+            PlatformDataKey<DynamicBuilders.DynamicBool>("key"),
+            false
+        )
     private val mockReceiver = mock<PlatformDataReceiver>()
     private val executor = directExecutor()
 
@@ -43,7 +46,7 @@
         provider.clearReceiver()
 
         provider.setUpdatesEnabled(true)
-        provider.setValue(false)
+        provider.value = false
 
         reset(mockReceiver)
     }
@@ -55,7 +58,7 @@
 
         reset(mockReceiver)
 
-        provider.setValue(true)
+        provider.value = true
         verify(mockReceiver, times(1)).onData(any())
     }
 
@@ -66,7 +69,7 @@
         provider.setReceiver(executor, mockReceiver)
         verify(mockReceiver, never()).onData(any())
 
-        provider.setValue(true)
+        provider.value = true
         verify(mockReceiver, never()).onData(any())
     }
 
@@ -94,7 +97,7 @@
         provider.setUpdatesEnabled(true)
         verify(mockReceiver, never()).onData(any())
 
-        provider.setValue(true)
+        provider.value = true
         verify(mockReceiver, never()).onData(any())
     }
 }