1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 @file:JvmName("LayoutStringUtil")
17 
18 package androidx.wear.protolayout.types
19 
20 import androidx.annotation.RestrictTo
21 import androidx.wear.protolayout.LayoutElementBuilders.TEXT_ALIGN_CENTER
22 import androidx.wear.protolayout.LayoutElementBuilders.TextAlignment
23 import androidx.wear.protolayout.TypeBuilders.StringLayoutConstraint
24 import androidx.wear.protolayout.TypeBuilders.StringProp
25 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString
26 import androidx.wear.protolayout.expression.RequiresSchemaVersion
27 import java.util.Objects
28 
29 /**
30  * Static or dynamic string value for layout fields.
31  *
32  * This can be used on layout string fields with data binding support.
33  */
34 class LayoutString
35 private constructor(
36     @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) val prop: StringProp,
37     /**
38      * When [dynamicValue] is used, this allows correctly measuring layout Text element size and
39      * aligning text to ensure that the layout is of a known size during the layout pass regardless
40      * of the [dynamicValue] String.
41      */
42     val layoutConstraint: StringLayoutConstraint? = null
43 ) {
44     /**
45      * The static value. If [dynamicValue] is not null this will be used as the default value for
46      * when [dynamicValue] can't be resolved.
47      */
48     val staticValue: String = prop.value
49     /** The dynamic value. If this can't be resolved [staticValue] will be used during rendering. */
50     val dynamicValue: DynamicString? = prop.dynamicValue
51 
52     /** Creates an instance for a static String value. */
53     constructor(staticValue: String) : this(StringProp.Builder(staticValue).build())
54 
55     /**
56      * Creates an instance for a [DynamicString] value with a static value fallback and a set of
57      * layout constraints for the dynamic value.
58      *
59      * @param staticValue the static value that can be used when the `dynamicValue` can't be
60      *   resolved.
61      * @param dynamicValue the dynamic value. If this value can be resolved, the `staticValue` won't
62      *   be used.
63      * @param layoutConstraint used to correctly measure layout Text element size and align text to
64      *   ensure that the layout is of a known size during the layout pass regardless of the
65      *   `dynamicValue` String.
66      */
67     @RequiresSchemaVersion(major = 1, minor = 200)
68     constructor(
69         staticValue: String,
70         dynamicValue: DynamicString,
71         layoutConstraint: StringLayoutConstraint
72     ) : this(
73         StringProp.Builder(staticValue).setDynamicValue(dynamicValue).build(),
74         layoutConstraint
75     )
76 
equalsnull77     override fun equals(other: Any?) =
78         this === other ||
79             (other is LayoutString &&
80                 prop == other.prop &&
81                 layoutConstraint == other.layoutConstraint)
82 
83     override fun hashCode() = Objects.hash(prop, layoutConstraint)
84 
85     override fun toString() = "LayoutString(prop=$prop, layoutConstraint=$layoutConstraint)"
86 }
87 
88 /** Extension for creating a [LayoutString] from a String. */
89 val String.layoutString: LayoutString
90     @JvmName("createLayoutString") get() = LayoutString(this)
91 
92 /**
93  * Extension for creating a [LayoutString] from a [DynamicString]
94  *
95  * @param staticValue the static value that can be used when the `dynamicValue` can't be resolved.
96  * @param layoutConstraint used to correctly measure layout Text element size and align text to
97  *   ensure that the layout is of a known size during the layout pass regardless of the
98  *   `dynamicValue` String.
99  */
100 @JvmName("createLayoutString")
101 @RequiresSchemaVersion(major = 1, minor = 200)
102 fun DynamicString.asLayoutString(staticValue: String, layoutConstraint: StringLayoutConstraint) =
103     LayoutString(staticValue, this, layoutConstraint)
104 
105 /**
106  * Specifies layout constraints for to use for layout measurement in presence of dynamic values.
107  *
108  * @param longestPattern the text string to use as the pattern for the (graphically) longest text
109  *   that can be laid out. Used to ensure that the layout is of a known size during the layout pass.
110  * @param alignment the alignment of the actual text within the space reserved by `longestPattern`
111  */
112 @JvmOverloads
113 @RequiresSchemaVersion(major = 1, minor = 200)
114 fun stringLayoutConstraint(
115     longestPattern: String,
116     @TextAlignment alignment: Int = TEXT_ALIGN_CENTER
117 ) = StringLayoutConstraint.Builder(longestPattern).setAlignment(alignment).build()
118 
119 /**
120  * Extension for creating a [StringLayoutConstraint] from a String. `this` will be used as
121  * `longestPattern`.
122  *
123  * @param alignment the alignment of the actual text within the space reserved by `longestPattern`
124  */
125 @RequiresSchemaVersion(major = 1, minor = 200)
126 fun String.asLayoutConstraint(@TextAlignment alignment: Int = TEXT_ALIGN_CENTER) =
127     stringLayoutConstraint(this, alignment)
128