Refactor handleProp methods out to a separate file.
Relnote: None
Test: Unit tests
Bug: 383308699
Change-Id: Id8cbc136de90b74f1eb84bb9ebafa84388a50c62
diff --git a/wear/protolayout/protolayout-renderer/build.gradle b/wear/protolayout/protolayout-renderer/build.gradle
index 5ba8d45..3fff1ce 100644
--- a/wear/protolayout/protolayout-renderer/build.gradle
+++ b/wear/protolayout/protolayout-renderer/build.gradle
@@ -26,10 +26,12 @@
plugins {
id("AndroidXPlugin")
id("com.android.library")
+ id("org.jetbrains.kotlin.android")
}
dependencies {
api(libs.jspecify)
+ api(libs.kotlinStdlib)
annotationProcessor(libs.nullaway)
api("androidx.annotation:annotation:1.8.1")
api("androidx.core:core:1.7.0")
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/PropHelpers.kt b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/PropHelpers.kt
new file mode 100644
index 0000000..35ec5a4
--- /dev/null
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/PropHelpers.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout.renderer.inflater
+
+import android.util.Log
+import androidx.wear.protolayout.proto.ColorProto.ColorProp
+import androidx.wear.protolayout.proto.DimensionProto.DegreesProp
+import androidx.wear.protolayout.proto.DimensionProto.DpProp
+import androidx.wear.protolayout.proto.TypesProto.BoolProp
+import androidx.wear.protolayout.proto.TypesProto.FloatProp
+import androidx.wear.protolayout.proto.TypesProto.StringProp
+import androidx.wear.protolayout.renderer.dynamicdata.ProtoLayoutDynamicDataPipeline.PipelineMaker
+import java.util.Locale
+import java.util.Optional
+import java.util.function.Consumer
+
+/** Helpers for handling Prop classes' static and dynamic values. */
+internal object PropHelpers {
+ const val TAG = "ProtolayoutPropHelpers"
+
+ /** Handles a StringProp. */
+ @JvmStatic
+ fun handleProp(
+ stringProp: StringProp,
+ locale: Locale,
+ consumer: Consumer<String>,
+ posId: String,
+ pipelineMaker: Optional<PipelineMaker>,
+ ) {
+ if (stringProp.hasDynamicValue() && pipelineMaker.isPresent()) {
+ try {
+ pipelineMaker
+ .get()
+ .addPipelineFor(
+ stringProp.dynamicValue,
+ stringProp.value,
+ locale,
+ posId,
+ consumer
+ )
+ } catch (ex: RuntimeException) {
+ Log.e(TAG, "Error building pipeline", ex)
+ consumer.accept(stringProp.value)
+ }
+ } else {
+ consumer.accept(stringProp.value)
+ }
+ }
+
+ /** Handles a DegreesProp. */
+ @JvmStatic
+ fun handleProp(
+ degreesProp: DegreesProp,
+ consumer: Consumer<Float>,
+ posId: String,
+ pipelineMaker: Optional<PipelineMaker>,
+ ) {
+ if (degreesProp.hasDynamicValue() && pipelineMaker.isPresent()) {
+ try {
+ pipelineMaker.get().addPipelineFor(degreesProp, degreesProp.value, posId, consumer)
+ } catch (ex: RuntimeException) {
+ Log.e(TAG, "Error building pipeline", ex)
+ consumer.accept(degreesProp.value)
+ }
+ } else {
+ consumer.accept(degreesProp.value)
+ }
+ }
+
+ /** Handles a DpProp. */
+ @JvmStatic
+ fun handleProp(
+ dpProp: DpProp,
+ consumer: Consumer<Float>,
+ posId: String,
+ pipelineMaker: Optional<PipelineMaker>,
+ ) = handleProp(dpProp, consumer, consumer, posId, pipelineMaker)
+
+ /** Handles a DpProp. */
+ @JvmStatic
+ fun handleProp(
+ dpProp: DpProp,
+ staticValueConsumer: Consumer<Float>,
+ dynamicValueConsumer: Consumer<Float>,
+ posId: String,
+ pipelineMaker: Optional<PipelineMaker>,
+ ) {
+ if (dpProp.hasDynamicValue() && pipelineMaker.isPresent()) {
+ try {
+ pipelineMaker
+ .get()
+ .addPipelineFor(dpProp, dpProp.value, posId, dynamicValueConsumer)
+ } catch (ex: RuntimeException) {
+ Log.e(TAG, "Error building pipeline", ex)
+ staticValueConsumer.accept(dpProp.value)
+ }
+ } else {
+ staticValueConsumer.accept(dpProp.value)
+ }
+ }
+
+ /** Handles a ColorProp. */
+ @JvmStatic
+ fun handleProp(
+ colorProp: ColorProp,
+ consumer: Consumer<Int>,
+ posId: String,
+ pipelineMaker: Optional<PipelineMaker>,
+ ) {
+ if (colorProp.hasDynamicValue() && pipelineMaker.isPresent()) {
+ try {
+ pipelineMaker.get().addPipelineFor(colorProp, colorProp.argb, posId, consumer)
+ } catch (ex: RuntimeException) {
+ Log.e(TAG, "Error building pipeline", ex)
+ consumer.accept(colorProp.argb)
+ }
+ } else {
+ consumer.accept(colorProp.argb)
+ }
+ }
+
+ /** Handles a BoolProp. */
+ @JvmStatic
+ fun handleProp(
+ boolProp: BoolProp,
+ consumer: Consumer<Boolean>,
+ posId: String,
+ pipelineMaker: Optional<PipelineMaker>,
+ ) {
+ if (boolProp.hasDynamicValue() && pipelineMaker.isPresent()) {
+ try {
+ pipelineMaker.get().addPipelineFor(boolProp, boolProp.value, posId, consumer)
+ } catch (ex: RuntimeException) {
+ Log.e(TAG, "Error building pipeline", ex)
+ consumer.accept(boolProp.value)
+ }
+ } else {
+ consumer.accept(boolProp.value)
+ }
+ }
+
+ /** Handles a FloatProp. */
+ @JvmStatic
+ fun handleProp(
+ floatProp: FloatProp,
+ consumer: Consumer<Float>,
+ posId: String,
+ pipelineMaker: Optional<PipelineMaker>,
+ ) = handleProp(floatProp, consumer, consumer, posId, pipelineMaker)
+
+ /** Handles a FloatProp. */
+ @JvmStatic
+ fun handleProp(
+ floatProp: FloatProp,
+ staticValueConsumer: Consumer<Float>,
+ dynamicValueconsumer: Consumer<Float>,
+ posId: String,
+ pipelineMaker: Optional<PipelineMaker>,
+ ) {
+ if (floatProp.hasDynamicValue() && pipelineMaker.isPresent()) {
+ try {
+ pipelineMaker
+ .get()
+ .addPipelineFor(
+ floatProp.dynamicValue,
+ floatProp.value,
+ posId,
+ dynamicValueconsumer
+ )
+ } catch (ex: RuntimeException) {
+ Log.e(TAG, "Error building pipeline", ex)
+ staticValueConsumer.accept(floatProp.value)
+ }
+ } else {
+ staticValueConsumer.accept(floatProp.value)
+ }
+ }
+}
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
index 012a095..41612f8 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
@@ -26,6 +26,7 @@
import static androidx.wear.protolayout.renderer.common.ProtoLayoutDiffer.FIRST_CHILD_INDEX;
import static androidx.wear.protolayout.renderer.common.ProtoLayoutDiffer.ROOT_NODE_ID;
import static androidx.wear.protolayout.renderer.common.ProtoLayoutDiffer.getParentNodePosId;
+import static androidx.wear.protolayout.renderer.inflater.PropHelpers.handleProp;
import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
import static com.google.common.util.concurrent.Futures.immediateFuture;
@@ -105,7 +106,6 @@
import androidx.wear.protolayout.proto.AlignmentProto.TextAlignment;
import androidx.wear.protolayout.proto.AlignmentProto.VerticalAlignment;
import androidx.wear.protolayout.proto.AlignmentProto.VerticalAlignmentProp;
-import androidx.wear.protolayout.proto.ColorProto.ColorProp;
import androidx.wear.protolayout.proto.DimensionProto.AngularDimension;
import androidx.wear.protolayout.proto.DimensionProto.ArcLineLength;
import androidx.wear.protolayout.proto.DimensionProto.ContainerDimension;
@@ -2829,6 +2829,7 @@
handleProp(
text.getText(),
+ mUiContext.getResources().getConfiguration().getLocales().get(0),
t -> {
// Underlines are applied using a Spannable here, rather than setting paint bits
// (or
@@ -4238,152 +4239,6 @@
}
/**
- * Either yield the constant value stored in stringProp, or register for updates if it is
- * dynamic property.
- *
- * <p>If both are set, this routine will yield the constant value if and only if this renderer
- * has a dynamic pipeline (i.e. {code mDataPipeline} is non-null), otherwise it will only
- * subscribe for dynamic updates. If the dynamic pipeline ever yields an invalid value (via
- * {@code onStateInvalid}), then stringProp's static valid will be used instead.
- */
- private void handleProp(
- StringProp stringProp,
- Consumer<String> consumer,
- String posId,
- Optional<PipelineMaker> pipelineMaker) {
- if (stringProp.hasDynamicValue() && pipelineMaker.isPresent()) {
- try {
- pipelineMaker
- .get()
- .addPipelineFor(
- stringProp.getDynamicValue(),
- stringProp.getValue(),
- mUiContext.getResources().getConfiguration().getLocales().get(0),
- posId,
- consumer);
- } catch (RuntimeException ex) {
- Log.e(TAG, "Error building pipeline", ex);
- consumer.accept(stringProp.getValue());
- }
- } else {
- consumer.accept(stringProp.getValue());
- }
- }
-
- private void handleProp(
- DegreesProp degreesProp,
- Consumer<Float> consumer,
- String posId,
- Optional<PipelineMaker> pipelineMaker) {
- if (degreesProp.hasDynamicValue() && pipelineMaker.isPresent()) {
- try {
- pipelineMaker
- .get()
- .addPipelineFor(degreesProp, degreesProp.getValue(), posId, consumer);
- } catch (RuntimeException ex) {
- Log.e(TAG, "Error building pipeline", ex);
- consumer.accept(degreesProp.getValue());
- }
- } else {
- consumer.accept(degreesProp.getValue());
- }
- }
-
- private void handleProp(
- DpProp dpProp,
- Consumer<Float> consumer,
- String posId,
- Optional<PipelineMaker> pipelineMaker) {
- handleProp(dpProp, consumer, consumer, posId, pipelineMaker);
- }
-
- private void handleProp(
- DpProp dpProp,
- Consumer<Float> staticValueConsumer,
- Consumer<Float> dynamicValueConsumer,
- String posId,
- Optional<PipelineMaker> pipelineMaker) {
- if (dpProp.hasDynamicValue() && pipelineMaker.isPresent()) {
- try {
- pipelineMaker
- .get()
- .addPipelineFor(dpProp, dpProp.getValue(), posId, dynamicValueConsumer);
- } catch (RuntimeException ex) {
- Log.e(TAG, "Error building pipeline", ex);
- staticValueConsumer.accept(dpProp.getValue());
- }
- } else {
- staticValueConsumer.accept(dpProp.getValue());
- }
- }
-
- private void handleProp(
- ColorProp colorProp,
- Consumer<Integer> consumer,
- String posId,
- Optional<PipelineMaker> pipelineMaker) {
- if (colorProp.hasDynamicValue() && pipelineMaker.isPresent()) {
- try {
- pipelineMaker.get().addPipelineFor(colorProp, colorProp.getArgb(), posId, consumer);
- } catch (RuntimeException ex) {
- Log.e(TAG, "Error building pipeline", ex);
- consumer.accept(colorProp.getArgb());
- }
- } else {
- consumer.accept(colorProp.getArgb());
- }
- }
-
- private void handleProp(
- BoolProp boolProp,
- Consumer<Boolean> consumer,
- String posId,
- Optional<PipelineMaker> pipelineMaker) {
- if (boolProp.hasDynamicValue() && pipelineMaker.isPresent()) {
- try {
- pipelineMaker.get().addPipelineFor(boolProp, boolProp.getValue(), posId, consumer);
- } catch (RuntimeException ex) {
- Log.e(TAG, "Error building pipeline", ex);
- consumer.accept(boolProp.getValue());
- }
- } else {
- consumer.accept(boolProp.getValue());
- }
- }
-
- private void handleProp(
- FloatProp floatProp,
- Consumer<Float> consumer,
- String posId,
- Optional<PipelineMaker> pipelineMaker) {
- handleProp(floatProp, consumer, consumer, posId, pipelineMaker);
- }
-
- private void handleProp(
- FloatProp floatProp,
- Consumer<Float> staticValueConsumer,
- Consumer<Float> dynamicValueconsumer,
- String posId,
- Optional<PipelineMaker> pipelineMaker) {
- if (floatProp.hasDynamicValue() && pipelineMaker.isPresent()) {
- try {
- pipelineMaker
- .get()
- .addPipelineFor(
- floatProp.getDynamicValue(),
- floatProp.getValue(),
- posId,
- dynamicValueconsumer);
- } catch (RuntimeException ex) {
- Log.e(TAG, "Error building pipeline", ex);
- staticValueConsumer.accept(floatProp.getValue());
- }
- } else {
- staticValueConsumer.accept(floatProp.getValue());
- }
- }
-
- /**
* Resolves the value for layout to be used in a Size Wrapper for elements containing dynamic
* values. Returns null if no size wrapper is needed.
*/
@@ -4959,6 +4814,7 @@
if (semantics.hasContentDescription()) {
handleProp(
semantics.getContentDescription(),
+ mUiContext.getResources().getConfiguration().getLocales().get(0),
view::setContentDescription,
posId,
pipelineMaker);
@@ -4970,6 +4826,7 @@
if (semantics.hasStateDescription()) {
handleProp(
semantics.getStateDescription(),
+ mUiContext.getResources().getConfiguration().getLocales().get(0),
(state) -> ViewCompat.setStateDescription(view, state),
posId,
pipelineMaker);