1 /* <lambda>null2 * Copyright 2022 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 17 package androidx.glance.semantics 18 19 /** 20 * General semantics properties, mainly used for accessibility and testing. 21 * 22 * Each property is intended to be set by the respective SemanticsPropertyReceiver extension instead 23 * of used directly. 24 */ 25 object SemanticsProperties { 26 /** @see SemanticsPropertyReceiver.contentDescription */ 27 val ContentDescription = 28 SemanticsPropertyKey<List<String>>( 29 name = "ContentDescription", 30 mergePolicy = { parentValue, childValue -> 31 parentValue?.toMutableList()?.also { it.addAll(childValue) } ?: childValue 32 } 33 ) 34 35 /** @see SemanticsPropertyReceiver.testTag */ 36 val TestTag = 37 SemanticsPropertyKey<String>( 38 name = "TestTag", 39 mergePolicy = { parentValue, _ -> 40 // No merge 41 parentValue 42 } 43 ) 44 } 45 46 /** 47 * SemanticsPropertyKey is the infrastructure for setting key/value pairs inside semantics block in 48 * a type-safe way. Each key has one particular statically defined value type T. 49 */ 50 class SemanticsPropertyKey<T>( 51 /** The name of the property. Should be the same as the constant from shich it is accessed. */ 52 val name: String, childValuenull53 internal val mergePolicy: (T?, T) -> T? = { parentValue, childValue -> 54 parentValue ?: childValue 55 } 56 ) { mergenull57 fun merge(parentValue: T?, childValue: T): T? { 58 return mergePolicy(parentValue, childValue) 59 } 60 } 61 62 /** 63 * SemanticsPropertyReceiver is the scope provided by semantics {} blocks, letting you set key/value 64 * pairs primarily via extension functions. 65 */ 66 interface SemanticsPropertyReceiver { setnull67 operator fun <T> set(key: SemanticsPropertyKey<T>, value: T) 68 } 69 70 /** 71 * Developer-set content description of the semantics node, for use in testing, accessibility and 72 * similar use cases. 73 */ 74 var SemanticsPropertyReceiver.contentDescription: String 75 /** Throws [UnsupportedOperationException]. Should not be called. */ 76 get() { 77 throw UnsupportedOperationException("You cannot retrieve a semantics property directly") 78 } 79 set(value) { 80 set(SemanticsProperties.ContentDescription, listOf(value)) 81 } 82 83 /** 84 * Test tag attached to this Glance composable node. 85 * 86 * This is a free form String and can be used to find nodes in testing frameworks. 87 */ 88 var SemanticsPropertyReceiver.testTag: String 89 /** Throws [UnsupportedOperationException]. Should not be called. */ 90 get() { 91 throw UnsupportedOperationException("You cannot retrieve a semantics property directly") 92 } 93 set(value) { 94 set(SemanticsProperties.TestTag, value) 95 } 96 97 /** Describes the semantics information associated with the owning component. */ 98 class SemanticsConfiguration : SemanticsPropertyReceiver { 99 private val props: MutableMap<SemanticsPropertyKey<*>, Any?> = mutableMapOf() 100 setnull101 override fun <T> set(key: SemanticsPropertyKey<T>, value: T) { 102 props[key] = value 103 } 104 105 /** 106 * Retrieves the value for the given property, if one has been set, If a value has not been set, 107 * throws [IllegalStateException] 108 */ 109 // Unavoidable, guaranteed by [set] 110 @Suppress("UNCHECKED_CAST") getnull111 operator fun <T> get(key: SemanticsPropertyKey<T>): T { 112 return props.getOrElse(key) { 113 throw java.lang.IllegalStateException("Key not present: $key") 114 } as T 115 } 116 117 /** 118 * Retrieves the value for the given property, if one has been set, If a value has not been set, 119 * returns the provided default value. 120 */ 121 // Unavoidable, guaranteed by [set] 122 @Suppress("UNCHECKED_CAST") getOrElseNullablenull123 fun <T> getOrElseNullable(key: SemanticsPropertyKey<T>, defaultValue: () -> T?): T? { 124 return props.getOrElse(key, defaultValue) as T? 125 } 126 127 /** 128 * Retrieves the value for the given property, if one has been set, If a value has not been set, 129 * returns null 130 */ getOrNullnull131 fun <T> getOrNull(key: SemanticsPropertyKey<T>): T? { 132 return getOrElseNullable(key) { null } 133 } 134 } 135