1 /*
<lambda>null2  * Copyright 2019 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.compose.ui.semantics
18 
19 import androidx.compose.runtime.Immutable
20 import androidx.compose.ui.autofill.ContentDataType
21 import androidx.compose.ui.autofill.ContentType
22 import androidx.compose.ui.geometry.Offset
23 import androidx.compose.ui.state.ToggleableState
24 import androidx.compose.ui.text.AnnotatedString
25 import androidx.compose.ui.text.TextLayoutResult
26 import androidx.compose.ui.text.TextRange
27 import androidx.compose.ui.text.input.ImeAction
28 import kotlin.reflect.KProperty
29 
30 /**
31  * General semantics properties, mainly used for accessibility and testing.
32  *
33  * Each of these is intended to be set by the respective SemanticsPropertyReceiver extension instead
34  * of used directly.
35  */
36 /*@VisibleForTesting*/
37 object SemanticsProperties {
38     /** @see SemanticsPropertyReceiver.contentDescription */
39     val ContentDescription =
40         AccessibilityKey<List<String>>(
41             name = "ContentDescription",
42             mergePolicy = { parentValue, childValue ->
43                 parentValue?.toMutableList()?.also { it.addAll(childValue) } ?: childValue
44             }
45         )
46 
47     /** @see SemanticsPropertyReceiver.stateDescription */
48     val StateDescription = AccessibilityKey<String>("StateDescription")
49 
50     /** @see SemanticsPropertyReceiver.progressBarRangeInfo */
51     val ProgressBarRangeInfo = AccessibilityKey<ProgressBarRangeInfo>("ProgressBarRangeInfo")
52 
53     /** @see SemanticsPropertyReceiver.paneTitle */
54     val PaneTitle =
55         AccessibilityKey<String>(
56             name = "PaneTitle",
57             mergePolicy = { _, _ ->
58                 throw IllegalStateException(
59                     "merge function called on unmergeable property PaneTitle."
60                 )
61             }
62         )
63 
64     /** @see SemanticsPropertyReceiver.selectableGroup */
65     val SelectableGroup = AccessibilityKey<Unit>("SelectableGroup")
66 
67     /** @see SemanticsPropertyReceiver.collectionInfo */
68     val CollectionInfo = AccessibilityKey<CollectionInfo>("CollectionInfo")
69 
70     /** @see SemanticsPropertyReceiver.collectionItemInfo */
71     val CollectionItemInfo = AccessibilityKey<CollectionItemInfo>("CollectionItemInfo")
72 
73     /** @see SemanticsPropertyReceiver.heading */
74     val Heading = AccessibilityKey<Unit>("Heading")
75 
76     /** @see SemanticsPropertyReceiver.disabled */
77     val Disabled = AccessibilityKey<Unit>("Disabled")
78 
79     /** @see SemanticsPropertyReceiver.liveRegion */
80     val LiveRegion = AccessibilityKey<LiveRegionMode>("LiveRegion")
81 
82     /** @see SemanticsPropertyReceiver.focused */
83     val Focused = AccessibilityKey<Boolean>("Focused")
84 
85     /** @see SemanticsPropertyReceiver.isContainer */
86     @Deprecated(
87         "Use `isTraversalGroup` instead.",
88         replaceWith = ReplaceWith("IsTraversalGroup"),
89     )
90     // TODO(mnuzen): `isContainer` should not need to be an accessibility key after a new
91     //  pruning API is added. See b/347038246 for more details.
92     val IsContainer = AccessibilityKey<Boolean>("IsContainer")
93 
94     /** @see SemanticsPropertyReceiver.isTraversalGroup */
95     val IsTraversalGroup = SemanticsPropertyKey<Boolean>("IsTraversalGroup")
96 
97     /** @see SemanticsPropertyReceiver.invisibleToUser */
98     @Deprecated(
99         "Use `hideFromAccessibility` instead.",
100         replaceWith = ReplaceWith("HideFromAccessibility")
101     )
102     @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
103     // Retain for binary compatibility with aosp/3341487 in 1.7
104     val InvisibleToUser =
105         SemanticsPropertyKey<Unit>(
106             name = "InvisibleToUser",
107             mergePolicy = { parentValue, _ -> parentValue }
108         )
109 
110     /** @see SemanticsPropertyReceiver.hideFromAccessibility */
111     val HideFromAccessibility =
112         SemanticsPropertyKey<Unit>(
113             name = "HideFromAccessibility",
114             mergePolicy = { parentValue, _ -> parentValue }
115         )
116 
117     /** @see SemanticsPropertyReceiver.contentType */
118     val ContentType =
119         SemanticsPropertyKey<ContentType>(
120             name = "ContentType",
121             mergePolicy = { parentValue, _ ->
122                 // Never merge autofill types
123                 parentValue
124             }
125         )
126 
127     /** @see SemanticsPropertyReceiver.contentDataType */
128     val ContentDataType =
129         SemanticsPropertyKey<ContentDataType>(
130             name = "ContentDataType",
131             mergePolicy = { parentValue, _ ->
132                 // Never merge autofill data types
133                 parentValue
134             }
135         )
136 
137     /** @see SemanticsPropertyReceiver.traversalIndex */
138     val TraversalIndex =
139         SemanticsPropertyKey<Float>(
140             name = "TraversalIndex",
141             mergePolicy = { parentValue, _ ->
142                 // Never merge traversal indices
143                 parentValue
144             }
145         )
146 
147     /** @see SemanticsPropertyReceiver.horizontalScrollAxisRange */
148     val HorizontalScrollAxisRange = AccessibilityKey<ScrollAxisRange>("HorizontalScrollAxisRange")
149 
150     /** @see SemanticsPropertyReceiver.verticalScrollAxisRange */
151     val VerticalScrollAxisRange = AccessibilityKey<ScrollAxisRange>("VerticalScrollAxisRange")
152 
153     /** @see SemanticsPropertyReceiver.popup */
154     val IsPopup =
155         AccessibilityKey<Unit>(
156             name = "IsPopup",
157             mergePolicy = { _, _ ->
158                 throw IllegalStateException(
159                     "merge function called on unmergeable property IsPopup. " +
160                         "A popup should not be a child of a clickable/focusable node."
161                 )
162             }
163         )
164 
165     /** @see SemanticsPropertyReceiver.dialog */
166     val IsDialog =
167         AccessibilityKey<Unit>(
168             name = "IsDialog",
169             mergePolicy = { _, _ ->
170                 throw IllegalStateException(
171                     "merge function called on unmergeable property IsDialog. " +
172                         "A dialog should not be a child of a clickable/focusable node."
173                 )
174             }
175         )
176 
177     /**
178      * The type of user interface element. Accessibility services might use this to describe the
179      * element or do customizations. Most roles can be automatically resolved by the semantics
180      * properties of this element. But some elements with subtle differences need an exact role. If
181      * an exact role is not listed in [Role], this property should not be set and the framework will
182      * automatically resolve it.
183      *
184      * @see SemanticsPropertyReceiver.role
185      */
186     val Role = AccessibilityKey<Role>("Role") { parentValue, _ -> parentValue }
187 
188     /** @see SemanticsPropertyReceiver.testTag */
189     val TestTag =
190         SemanticsPropertyKey<String>(
191             name = "TestTag",
192             isImportantForAccessibility = false,
193             mergePolicy = { parentValue, _ ->
194                 // Never merge TestTags, to avoid leaking internal test tags to parents.
195                 parentValue
196             }
197         )
198 
199     /**
200      * Marks a link within a text node (a link is represented by a
201      * [androidx.compose.ui.text.LinkAnnotation]) for identification during automated testing. This
202      * property is for internal use only and not intended for general use by developers.
203      */
204     val LinkTestMarker =
205         SemanticsPropertyKey<Unit>(
206             name = "LinkTestMarker",
207             isImportantForAccessibility = false,
208             mergePolicy = { parentValue, _ -> parentValue }
209         )
210 
211     /** @see SemanticsPropertyReceiver.text */
212     val Text =
213         AccessibilityKey<List<AnnotatedString>>(
214             name = "Text",
215             mergePolicy = { parentValue, childValue ->
216                 parentValue?.toMutableList()?.also { it.addAll(childValue) } ?: childValue
217             }
218         )
219 
220     /** @see SemanticsPropertyReceiver.textSubstitution */
221     val TextSubstitution = SemanticsPropertyKey<AnnotatedString>(name = "TextSubstitution")
222 
223     /** @see SemanticsPropertyReceiver.isShowingTextSubstitution */
224     val IsShowingTextSubstitution = SemanticsPropertyKey<Boolean>("IsShowingTextSubstitution")
225 
226     /** @see SemanticsPropertyReceiver.inputText */
227     val InputText = AccessibilityKey<AnnotatedString>(name = "InputText")
228 
229     /** @see SemanticsPropertyReceiver.editableText */
230     val EditableText = AccessibilityKey<AnnotatedString>(name = "EditableText")
231 
232     /** @see SemanticsPropertyReceiver.textSelectionRange */
233     val TextSelectionRange = AccessibilityKey<TextRange>("TextSelectionRange")
234 
235     /** @see SemanticsPropertyReceiver.onImeAction */
236     val ImeAction = AccessibilityKey<ImeAction>("ImeAction")
237 
238     /** @see SemanticsPropertyReceiver.selected */
239     val Selected = AccessibilityKey<Boolean>("Selected")
240 
241     /** @see SemanticsPropertyReceiver.toggleableState */
242     val ToggleableState = AccessibilityKey<ToggleableState>("ToggleableState")
243 
244     /** @see SemanticsPropertyReceiver.password */
245     val Password = AccessibilityKey<Unit>("Password")
246 
247     /** @see SemanticsPropertyReceiver.error */
248     val Error = AccessibilityKey<String>("Error")
249 
250     /** @see SemanticsPropertyReceiver.indexForKey */
251     val IndexForKey = SemanticsPropertyKey<(Any) -> Int>("IndexForKey")
252 
253     /** @see SemanticsPropertyReceiver.isEditable */
254     val IsEditable = SemanticsPropertyKey<Boolean>("IsEditable")
255 
256     /** @see SemanticsPropertyReceiver.maxTextLength */
257     val MaxTextLength = SemanticsPropertyKey<Int>("MaxTextLength")
258 }
259 
260 /**
261  * Ths object defines keys of the actions which can be set in semantics and performed on the
262  * semantics node.
263  *
264  * Each of these is intended to be set by the respective SemanticsPropertyReceiver extension instead
265  * of used directly.
266  */
267 /*@VisibleForTesting*/
268 object SemanticsActions {
269     /** @see SemanticsPropertyReceiver.getTextLayoutResult */
270     val GetTextLayoutResult =
271         ActionPropertyKey<(MutableList<TextLayoutResult>) -> Boolean>("GetTextLayoutResult")
272 
273     /** @see SemanticsPropertyReceiver.onClick */
274     val OnClick = ActionPropertyKey<() -> Boolean>("OnClick")
275 
276     /** @see SemanticsPropertyReceiver.onLongClick */
277     val OnLongClick = ActionPropertyKey<() -> Boolean>("OnLongClick")
278 
279     /** @see SemanticsPropertyReceiver.scrollBy */
280     val ScrollBy = ActionPropertyKey<(x: Float, y: Float) -> Boolean>("ScrollBy")
281 
282     /** @see SemanticsPropertyReceiver.scrollByOffset */
283     val ScrollByOffset = SemanticsPropertyKey<suspend (offset: Offset) -> Offset>("ScrollByOffset")
284 
285     /** @see SemanticsPropertyReceiver.scrollToIndex */
286     val ScrollToIndex = ActionPropertyKey<(Int) -> Boolean>("ScrollToIndex")
287 
288     /** @see SemanticsPropertyReceiver.onAutofillText */
289     val OnAutofillText = ActionPropertyKey<(AnnotatedString) -> Boolean>("OnAutofillText")
290 
291     /** @see SemanticsPropertyReceiver.setProgress */
292     val SetProgress = ActionPropertyKey<(progress: Float) -> Boolean>("SetProgress")
293 
294     /** @see SemanticsPropertyReceiver.setSelection */
295     val SetSelection = ActionPropertyKey<(Int, Int, Boolean) -> Boolean>("SetSelection")
296 
297     /** @see SemanticsPropertyReceiver.setText */
298     val SetText = ActionPropertyKey<(AnnotatedString) -> Boolean>("SetText")
299 
300     /** @see SemanticsPropertyReceiver.setTextSubstitution */
301     val SetTextSubstitution = ActionPropertyKey<(AnnotatedString) -> Boolean>("SetTextSubstitution")
302 
303     /** @see SemanticsPropertyReceiver.showTextSubstitution */
304     val ShowTextSubstitution = ActionPropertyKey<(Boolean) -> Boolean>("ShowTextSubstitution")
305 
306     /** @see SemanticsPropertyReceiver.clearTextSubstitution */
307     val ClearTextSubstitution = ActionPropertyKey<() -> Boolean>("ClearTextSubstitution")
308 
309     /** @see SemanticsPropertyReceiver.insertTextAtCursor */
310     val InsertTextAtCursor = ActionPropertyKey<(AnnotatedString) -> Boolean>("InsertTextAtCursor")
311 
312     /** @see SemanticsPropertyReceiver.onImeAction */
313     val OnImeAction = ActionPropertyKey<() -> Boolean>("PerformImeAction")
314 
315     // b/322269946
316     @Suppress("unused")
317     @Deprecated(
318         message = "Use `SemanticsActions.OnImeAction` instead.",
319         replaceWith =
320             ReplaceWith(
321                 "OnImeAction",
322                 "androidx.compose.ui.semantics.SemanticsActions.OnImeAction",
323             ),
324         level = DeprecationLevel.ERROR,
325     )
326     val PerformImeAction = ActionPropertyKey<() -> Boolean>("PerformImeAction")
327 
328     /** @see SemanticsPropertyReceiver.copyText */
329     val CopyText = ActionPropertyKey<() -> Boolean>("CopyText")
330 
331     /** @see SemanticsPropertyReceiver.cutText */
332     val CutText = ActionPropertyKey<() -> Boolean>("CutText")
333 
334     /** @see SemanticsPropertyReceiver.pasteText */
335     val PasteText = ActionPropertyKey<() -> Boolean>("PasteText")
336 
337     /** @see SemanticsPropertyReceiver.expand */
338     val Expand = ActionPropertyKey<() -> Boolean>("Expand")
339 
340     /** @see SemanticsPropertyReceiver.collapse */
341     val Collapse = ActionPropertyKey<() -> Boolean>("Collapse")
342 
343     /** @see SemanticsPropertyReceiver.dismiss */
344     val Dismiss = ActionPropertyKey<() -> Boolean>("Dismiss")
345 
346     /** @see SemanticsPropertyReceiver.requestFocus */
347     val RequestFocus = ActionPropertyKey<() -> Boolean>("RequestFocus")
348 
349     /** @see SemanticsPropertyReceiver.customActions */
350     val CustomActions = AccessibilityKey<List<CustomAccessibilityAction>>("CustomActions")
351 
352     /** @see SemanticsPropertyReceiver.pageUp */
353     val PageUp = ActionPropertyKey<() -> Boolean>("PageUp")
354 
355     /** @see SemanticsPropertyReceiver.pageLeft */
356     val PageLeft = ActionPropertyKey<() -> Boolean>("PageLeft")
357 
358     /** @see SemanticsPropertyReceiver.pageDown */
359     val PageDown = ActionPropertyKey<() -> Boolean>("PageDown")
360 
361     /** @see SemanticsPropertyReceiver.pageRight */
362     val PageRight = ActionPropertyKey<() -> Boolean>("PageRight")
363 
364     /** @see SemanticsPropertyReceiver.getScrollViewportLength */
365     val GetScrollViewportLength =
366         ActionPropertyKey<(MutableList<Float>) -> Boolean>("GetScrollViewportLength")
367 }
368 
369 /**
370  * SemanticsPropertyKey is the infrastructure for setting key/value pairs inside semantics blocks in
371  * a type-safe way. Each key has one particular statically defined value type T.
372  */
373 class SemanticsPropertyKey<T>(
374     /** The name of the property. Should be the same as the constant from which it is accessed. */
375     val name: String,
childValuenull376     internal val mergePolicy: (T?, T) -> T? = { parentValue, childValue ->
377         parentValue ?: childValue
378     }
379 ) {
380     /**
381      * Whether this type of property provides information relevant to accessibility services.
382      *
383      * Most built-in semantics properties are relevant to accessibility, but a very common exception
384      * is testTag. Nodes with only a testTag still need to be included in the AccessibilityNodeInfo
385      * tree because UIAutomator tests rely on that, but we mark them `isImportantForAccessibility =
386      * false` on the AccessibilityNodeInfo to inform accessibility services that they are best
387      * ignored.
388      *
389      * The default value is false and it is not exposed as a public API. That's because it is
390      * impossible in the first place for `SemanticsPropertyKey`s defined outside the UI package to
391      * be relevant to accessibility, because for each accessibility-relevant SemanticsProperty type
392      * to get plumbed into the AccessibilityNodeInfo, the private `createNodeInfo` implementation
393      * must also have a line of code.
394      */
395     internal var isImportantForAccessibility = false
396         private set
397 
398     internal constructor(
399         name: String,
400         isImportantForAccessibility: Boolean,
401     ) : this(name) {
402         this.isImportantForAccessibility = isImportantForAccessibility
403     }
404 
405     internal constructor(
406         name: String,
407         isImportantForAccessibility: Boolean,
408         mergePolicy: (T?, T) -> T?
409     ) : this(name, mergePolicy) {
410         this.isImportantForAccessibility = isImportantForAccessibility
411     }
412 
413     /**
414      * Method implementing the semantics merge policy of a particular key.
415      *
416      * When mergeDescendants is set on a semantics node, then this function will called for each
417      * descendant node of a given key in depth-first-search order. The parent value accumulates the
418      * result of merging the values seen so far, similar to reduce().
419      *
420      * The default implementation returns the parent value if one exists, otherwise uses the child
421      * element. This means by default, a SemanticsNode with mergeDescendants = true winds up with
422      * the first value found for each key in its subtree in depth-first-search order.
423      */
mergenull424     fun merge(parentValue: T?, childValue: T): T? {
425         return mergePolicy(parentValue, childValue)
426     }
427 
428     /** Throws [UnsupportedOperationException]. Should not be called. */
429     // TODO(KT-6519): Remove this getter
430     // TODO(KT-32770): Cannot deprecate this either as the getter is considered called by "by"
getValuenull431     final operator fun getValue(thisRef: SemanticsPropertyReceiver, property: KProperty<*>): T {
432         return throwSemanticsGetNotSupported()
433     }
434 
setValuenull435     final operator fun setValue(
436         thisRef: SemanticsPropertyReceiver,
437         property: KProperty<*>,
438         value: T
439     ) {
440         thisRef[this] = value
441     }
442 
toStringnull443     override fun toString(): String {
444         return "AccessibilityKey: $name"
445     }
446 }
447 
throwSemanticsGetNotSupportednull448 private fun <T> throwSemanticsGetNotSupported(): T {
449     throw UnsupportedOperationException(
450         "You cannot retrieve a semantics property directly - " +
451             "use one of the SemanticsConfiguration.getOr* methods instead"
452     )
453 }
454 
455 @Suppress("NOTHING_TO_INLINE")
456 // inline to avoid different static initialization order on different targets.
457 // See https://youtrack.jetbrains.com/issue/KT-65040 for more information.
AccessibilityKeynull458 internal inline fun <T> AccessibilityKey(name: String) =
459     SemanticsPropertyKey<T>(name = name, isImportantForAccessibility = true)
460 
461 @Suppress("NOTHING_TO_INLINE")
462 // inline to avoid different static initialization order on different targets
463 // See https://youtrack.jetbrains.com/issue/KT-65040 for more information.
464 internal inline fun <T> AccessibilityKey(name: String, noinline mergePolicy: (T?, T) -> T?) =
465     SemanticsPropertyKey(name = name, isImportantForAccessibility = true, mergePolicy = mergePolicy)
466 
467 /**
468  * Standard accessibility action.
469  *
470  * @param label The description of this action
471  * @param action The function to invoke when this action is performed. The function should return a
472  *   boolean result indicating whether the action is successfully handled. For example, a scroll
473  *   forward action should return false if the widget is not enabled or has reached the end of the
474  *   list. If multiple semantics blocks with the same AccessibilityAction are provided, the
475  *   resulting AccessibilityAction's label/action will be the label/action of the outermost modifier
476  *   with this key and nonnull label/action, or null if no nonnull label/action is found.
477  */
478 class AccessibilityAction<T : Function<Boolean>>(val label: String?, val action: T?) {
479     override fun equals(other: Any?): Boolean {
480         if (this === other) return true
481         if (other !is AccessibilityAction<*>) return false
482 
483         if (label != other.label) return false
484         if (action != other.action) return false
485 
486         return true
487     }
488 
489     override fun hashCode(): Int {
490         var result = label?.hashCode() ?: 0
491         result = 31 * result + action.hashCode()
492         return result
493     }
494 
495     override fun toString(): String {
496         return "AccessibilityAction(label=$label, action=$action)"
497     }
498 }
499 
500 @Suppress("NOTHING_TO_INLINE")
501 // inline to break static initialization cycle issue
ActionPropertyKeynull502 private inline fun <T : Function<Boolean>> ActionPropertyKey(name: String) =
503     AccessibilityKey<AccessibilityAction<T>>(
504         name = name,
505         mergePolicy = { parentValue, childValue ->
506             AccessibilityAction(
507                 parentValue?.label ?: childValue.label,
508                 parentValue?.action ?: childValue.action
509             )
510         }
511     )
512 
513 /**
514  * Custom accessibility action.
515  *
516  * @param label The description of this action
517  * @param action The function to invoke when this action is performed. The function should have no
518  *   arguments and return a boolean result indicating whether the action is successfully handled.
519  */
520 class CustomAccessibilityAction(val label: String, val action: () -> Boolean) {
equalsnull521     override fun equals(other: Any?): Boolean {
522         if (this === other) return true
523         if (other !is CustomAccessibilityAction) return false
524 
525         if (label != other.label) return false
526         if (action !== other.action) return false
527 
528         return true
529     }
530 
hashCodenull531     override fun hashCode(): Int {
532         var result = label.hashCode()
533         result = 31 * result + action.hashCode()
534         return result
535     }
536 
toStringnull537     override fun toString(): String {
538         return "CustomAccessibilityAction(label=$label, action=$action)"
539     }
540 }
541 
542 /**
543  * Accessibility range information, to represent the status of a progress bar or seekable progress
544  * bar.
545  *
546  * @param current current value in the range. Must not be NaN.
547  * @param range range of this node
548  * @param steps if greater than `0`, specifies the number of discrete values, evenly distributed
549  *   between across the whole value range. If `0`, any value from the range specified can be chosen.
550  *   Cannot be less than `0`.
551  */
552 class ProgressBarRangeInfo(
553     val current: Float,
554     val range: ClosedFloatingPointRange<Float>,
555     /*@IntRange(from = 0)*/
556     val steps: Int = 0
557 ) {
558     init {
<lambda>null559         require(!current.isNaN()) { "current must not be NaN" }
560     }
561 
562     companion object {
563         /** Accessibility range information to present indeterminate progress bar */
564         val Indeterminate = ProgressBarRangeInfo(0f, 0f..0f)
565     }
566 
equalsnull567     override fun equals(other: Any?): Boolean {
568         if (this === other) return true
569         if (other !is ProgressBarRangeInfo) return false
570 
571         if (current != other.current) return false
572         if (range != other.range) return false
573         if (steps != other.steps) return false
574 
575         return true
576     }
577 
hashCodenull578     override fun hashCode(): Int {
579         var result = current.hashCode()
580         result = 31 * result + range.hashCode()
581         result = 31 * result + steps
582         return result
583     }
584 
toStringnull585     override fun toString(): String {
586         return "ProgressBarRangeInfo(current=$current, range=$range, steps=$steps)"
587     }
588 }
589 
590 /**
591  * Information about the collection.
592  *
593  * A collection of items has [rowCount] rows and [columnCount] columns. For example, a vertical list
594  * is a collection with one column, as many rows as the list items that are important for
595  * accessibility; A table is a collection with several rows and several columns.
596  *
597  * @param rowCount the number of rows in the collection, or -1 if unknown
598  * @param columnCount the number of columns in the collection, or -1 if unknown
599  */
600 class CollectionInfo(val rowCount: Int, val columnCount: Int)
601 
602 /**
603  * Information about the item of a collection.
604  *
605  * A collection item is contained in a collection, it starts at a given [rowIndex] and [columnIndex]
606  * in the collection, and spans one or more rows and columns. For example, a header of two related
607  * table columns starts at the first row and the first column, spans one row and two columns.
608  *
609  * @param rowIndex the index of the row at which item is located
610  * @param rowSpan the number of rows the item spans
611  * @param columnIndex the index of the column at which item is located
612  * @param columnSpan the number of columns the item spans
613  */
614 class CollectionItemInfo(
615     val rowIndex: Int,
616     val rowSpan: Int,
617     val columnIndex: Int,
618     val columnSpan: Int
619 )
620 
621 /**
622  * The scroll state of one axis if this node is scrollable.
623  *
624  * @param value current 0-based scroll position value (either in pixels, or lazy-item count)
625  * @param maxValue maximum bound for [value], or [Float.POSITIVE_INFINITY] if still unknown
626  * @param reverseScrolling for horizontal scroll, when this is `true`, 0 [value] will mean right,
627  *   when`false`, 0 [value] will mean left. For vertical scroll, when this is `true`, 0 [value] will
628  *   mean bottom, when `false`, 0 [value] will mean top
629  */
630 class ScrollAxisRange(
631     val value: () -> Float,
632     val maxValue: () -> Float,
633     val reverseScrolling: Boolean = false
634 ) {
toStringnull635     override fun toString(): String =
636         "ScrollAxisRange(value=${value()}, maxValue=${maxValue()}, " +
637             "reverseScrolling=$reverseScrolling)"
638 }
639 
640 /**
641  * The type of user interface element. Accessibility services might use this to describe the element
642  * or do customizations. Most roles can be automatically resolved by the semantics properties of
643  * this element. But some elements with subtle differences need an exact role. If an exact role is
644  * not listed, [SemanticsPropertyReceiver.role] should not be set and the framework will
645  * automatically resolve it.
646  */
647 @Immutable
648 @kotlin.jvm.JvmInline
649 value class Role private constructor(@Suppress("unused") private val value: Int) {
650     companion object {
651         /**
652          * This element is a button control. Associated semantics properties for accessibility:
653          * [SemanticsProperties.Disabled], [SemanticsActions.OnClick]
654          */
655         val Button = Role(0)
656 
657         /**
658          * This element is a Checkbox which is a component that represents two states (checked /
659          * unchecked). Associated semantics properties for accessibility:
660          * [SemanticsProperties.Disabled], [SemanticsProperties.StateDescription],
661          * [SemanticsActions.OnClick]
662          */
663         val Checkbox = Role(1)
664 
665         /**
666          * This element is a Switch which is a two state toggleable component that provides on/off
667          * like options. Associated semantics properties for accessibility:
668          * [SemanticsProperties.Disabled], [SemanticsProperties.StateDescription],
669          * [SemanticsActions.OnClick]
670          */
671         val Switch = Role(2)
672 
673         /**
674          * This element is a RadioButton which is a component to represent two states, selected and
675          * not selected. Associated semantics properties for accessibility:
676          * [SemanticsProperties.Disabled], [SemanticsProperties.StateDescription],
677          * [SemanticsActions.OnClick]
678          */
679         val RadioButton = Role(3)
680 
681         /**
682          * This element is a Tab which represents a single page of content using a text label and/or
683          * icon. A Tab also has two states: selected and not selected. Associated semantics
684          * properties for accessibility: [SemanticsProperties.Disabled],
685          * [SemanticsProperties.StateDescription], [SemanticsActions.OnClick]
686          */
687         val Tab = Role(4)
688 
689         /**
690          * This element is an image. Associated semantics properties for accessibility:
691          * [SemanticsProperties.ContentDescription]
692          */
693         val Image = Role(5)
694 
695         /**
696          * This element is associated with a drop down menu. Associated semantics properties for
697          * accessibility: [SemanticsActions.OnClick]
698          */
699         val DropdownList = Role(6)
700 
701         /**
702          * This element is a value picker. It should support the following accessibility actions to
703          * enable selection of the next and previous values:
704          *
705          * [android.view.accessibility.AccessibilityNodeInfo.ACTION_SCROLL_FORWARD]: Select the next
706          * value.
707          *
708          * [android.view.accessibility.AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD]: Select the
709          * previous value.
710          *
711          * These actions allow accessibility services to interact with this node programmatically on
712          * behalf of users, facilitating navigation within sets of selectable values.
713          */
714         val ValuePicker = Role(7)
715 
716         /**
717          * This element is a Carousel. This means that even if Pager actions are added, this element
718          * will behave like a regular List collection.
719          *
720          * Associated semantics properties for Pager accessibility actions:
721          * [SemanticsActions.PageUp],[SemanticsActions.PageDown],[SemanticsActions.PageLeft],
722          * [SemanticsActions.PageRight]
723          */
724         val Carousel = Role(8)
725     }
726 
727     override fun toString() =
728         when (this) {
729             Button -> "Button"
730             Checkbox -> "Checkbox"
731             Switch -> "Switch"
732             RadioButton -> "RadioButton"
733             Tab -> "Tab"
734             Image -> "Image"
735             DropdownList -> "DropdownList"
736             ValuePicker -> "Picker"
737             Carousel -> "Carousel"
738             else -> "Unknown"
739         }
740 }
741 
742 /**
743  * The mode of live region. Live region indicates to accessibility services they should
744  * automatically notify the user about changes to the node's content description or text, or to the
745  * content descriptions or text of the node's children (where applicable).
746  */
747 @Immutable
748 @kotlin.jvm.JvmInline
749 value class LiveRegionMode private constructor(@Suppress("unused") private val value: Int) {
750     companion object {
751         /**
752          * Live region mode specifying that accessibility services should announce changes to this
753          * node.
754          */
755         val Polite = LiveRegionMode(0)
756 
757         /**
758          * Live region mode specifying that accessibility services should interrupt ongoing speech
759          * to immediately announce changes to this node.
760          */
761         val Assertive = LiveRegionMode(1)
762     }
763 
toStringnull764     override fun toString() =
765         when (this) {
766             Polite -> "Polite"
767             Assertive -> "Assertive"
768             else -> "Unknown"
769         }
770 }
771 
772 /**
773  * SemanticsPropertyReceiver is the scope provided by semantics {} blocks, letting you set key/value
774  * pairs primarily via extension functions.
775  */
776 interface SemanticsPropertyReceiver {
setnull777     operator fun <T> set(key: SemanticsPropertyKey<T>, value: T)
778 }
779 
780 /**
781  * Developer-set content description of the semantics node.
782  *
783  * If this is not set, accessibility services will present the [text][SemanticsProperties.Text] of
784  * this node as the content.
785  *
786  * This typically should not be set directly by applications, because some screen readers will cease
787  * presenting other relevant information when this property is present. This is intended to be used
788  * via Foundation components which are inherently intractable to automatically describe, such as
789  * Image, Icon, and Canvas.
790  */
791 var SemanticsPropertyReceiver.contentDescription: String
792     get() = throwSemanticsGetNotSupported()
793     set(value) {
794         set(SemanticsProperties.ContentDescription, listOf(value))
795     }
796 
797 /**
798  * Developer-set state description of the semantics node.
799  *
800  * For example: on/off. If this not set, accessibility services will derive the state from other
801  * semantics properties, like [ProgressBarRangeInfo], but it is not guaranteed and the format will
802  * be decided by accessibility services.
803  */
804 var SemanticsPropertyReceiver.stateDescription by SemanticsProperties.StateDescription
805 
806 /**
807  * The semantics represents a range of possible values with a current value. For example, when used
808  * on a slider control, this will allow screen readers to communicate the slider's state.
809  */
810 var SemanticsPropertyReceiver.progressBarRangeInfo by SemanticsProperties.ProgressBarRangeInfo
811 
812 /**
813  * The node is marked as heading for accessibility.
814  *
815  * @see SemanticsProperties.Heading
816  */
SemanticsPropertyReceivernull817 fun SemanticsPropertyReceiver.heading() {
818     this[SemanticsProperties.Heading] = Unit
819 }
820 
821 /**
822  * Accessibility-friendly title for a screen's pane. For accessibility purposes, a pane is a
823  * visually distinct portion of a window, such as the contents of a open drawer. In order for
824  * accessibility services to understand a pane's window-like behavior, you should give descriptive
825  * titles to your app's panes. Accessibility services can then provide more granular information to
826  * users when a pane's appearance or content changes.
827  *
828  * @see SemanticsProperties.PaneTitle
829  */
830 var SemanticsPropertyReceiver.paneTitle by SemanticsProperties.PaneTitle
831 
832 /**
833  * Whether this semantics node is disabled. Note that proper [SemanticsActions] should still be
834  * added when this property is set.
835  *
836  * @see SemanticsProperties.Disabled
837  */
SemanticsPropertyReceivernull838 fun SemanticsPropertyReceiver.disabled() {
839     this[SemanticsProperties.Disabled] = Unit
840 }
841 
842 /**
843  * This node is marked as live region for accessibility. This indicates to accessibility services
844  * they should automatically notify the user about changes to the node's content description or
845  * text, or to the content descriptions or text of the node's children (where applicable). It should
846  * be used with caution, especially with assertive mode which immediately stops the current audio
847  * and the user does not hear the rest of the content. An example of proper use is a Snackbar which
848  * is marked as [LiveRegionMode.Polite].
849  *
850  * @see SemanticsProperties.LiveRegion
851  * @see LiveRegionMode
852  */
853 var SemanticsPropertyReceiver.liveRegion by SemanticsProperties.LiveRegion
854 
855 /**
856  * Whether this semantics node is focused. The presence of this property indicates this node is
857  * focusable
858  *
859  * @see SemanticsProperties.Focused
860  */
861 var SemanticsPropertyReceiver.focused by SemanticsProperties.Focused
862 
863 /**
864  * Whether this semantics node is a container. This is defined as a node whose function is to serve
865  * as a boundary or border in organizing its children.
866  *
867  * @see SemanticsProperties.IsContainer
868  */
869 @Deprecated(
870     "Use `isTraversalGroup` instead.",
871     replaceWith = ReplaceWith("isTraversalGroup"),
872 )
873 @Suppress("DEPRECATION")
874 var SemanticsPropertyReceiver.isContainer by SemanticsProperties.IsContainer
875 
876 /**
877  * Whether this semantics node is a traversal group.
878  *
879  * See https://developer.android.com/develop/ui/compose/accessibility/traversal
880  *
881  * @see SemanticsProperties.IsTraversalGroup
882  */
883 var SemanticsPropertyReceiver.isTraversalGroup by SemanticsProperties.IsTraversalGroup
884 
885 /**
886  * Whether this node is specially known to be invisible to the user.
887  *
888  * For example, if the node is currently occluded by a dark semitransparent pane above it, then for
889  * all practical purposes the node is invisible to the user, but the system cannot automatically
890  * determine that. To make the screen reader linear navigation skip over this type of invisible
891  * node, this property can be set.
892  *
893  * If looking for a way to hide semantics of small items from screen readers because they're
894  * redundant with semantics of their parent, consider [SemanticsModifier.clearAndSetSemantics]
895  * instead.
896  */
897 @Deprecated(
898     "Use `hideFromAccessibility()` instead.",
899     replaceWith = ReplaceWith("hideFromAccessibility()"),
900 )
901 @Suppress("DEPRECATION")
902 // Retain for binary compatibility with aosp/3341487 in 1.7
SemanticsPropertyReceivernull903 fun SemanticsPropertyReceiver.invisibleToUser() {
904     this[SemanticsProperties.InvisibleToUser] = Unit
905 }
906 
907 /**
908  * If present, this node is considered hidden from accessibility services.
909  *
910  * For example, if the node is currently occluded by a dark semitransparent pane above it, then for
911  * all practical purposes the node should not be announced to the user. Since the system cannot
912  * automatically determine that, this property can be set to make the screen reader linear
913  * navigation skip over this type of node.
914  *
915  * If looking for a way to clear semantics of small items from the UI tree completely because they
916  * are redundant with semantics of their parent, consider [SemanticsModifier.clearAndSetSemantics]
917  * instead.
918  */
SemanticsPropertyReceivernull919 fun SemanticsPropertyReceiver.hideFromAccessibility() {
920     this[SemanticsProperties.HideFromAccessibility] = Unit
921 }
922 
923 /**
924  * Content field type information.
925  *
926  * This API can be used to indicate to Autofill services what _kind of field_ is associated with
927  * this node. Not to be confused with the _data type_ to be entered into the field.
928  *
929  * @see SemanticsProperties.ContentType
930  */
931 var SemanticsPropertyReceiver.contentType by SemanticsProperties.ContentType
932 
933 /**
934  * Content data type information.
935  *
936  * This API can be used to indicate to Autofill services what _kind of data_ is meant to be
937  * suggested for this field. Not to be confused with the _type_ of the field.
938  *
939  * @see SemanticsProperties.ContentType
940  */
941 var SemanticsPropertyReceiver.contentDataType by SemanticsProperties.ContentDataType
942 
943 /**
944  * A value to manually control screenreader traversal order.
945  *
946  * This API can be used to customize TalkBack traversal order. When the `traversalIndex` property is
947  * set on a traversalGroup or on a screenreader-focusable node, then the sorting algorithm will
948  * prioritize nodes with smaller `traversalIndex`s earlier. The default traversalIndex value is
949  * zero, and traversalIndices are compared at a peer level.
950  *
951  * For example,` traversalIndex = -1f` can be used to force a top bar to be ordered earlier, and
952  * `traversalIndex = 1f` to make a bottom bar ordered last, in the edge cases where this does not
953  * happen by default. As another example, if you need to reorder two Buttons within a Row, then you
954  * can set `isTraversalGroup = true` on the Row, and set `traversalIndex` on one of the Buttons.
955  *
956  * Note that if `traversalIndex` seems to have no effect, be sure to set `isTraversalGroup = true`
957  * as well.
958  */
959 var SemanticsPropertyReceiver.traversalIndex by SemanticsProperties.TraversalIndex
960 
961 /** The horizontal scroll state of this node if this node is scrollable. */
962 var SemanticsPropertyReceiver.horizontalScrollAxisRange by
963     SemanticsProperties.HorizontalScrollAxisRange
964 
965 /** The vertical scroll state of this node if this node is scrollable. */
966 var SemanticsPropertyReceiver.verticalScrollAxisRange by SemanticsProperties.VerticalScrollAxisRange
967 
968 /**
969  * Whether this semantics node represents a Popup. Not to be confused with if this node is _part of_
970  * a Popup.
971  */
popupnull972 fun SemanticsPropertyReceiver.popup() {
973     this[SemanticsProperties.IsPopup] = Unit
974 }
975 
976 /**
977  * Whether this element is a Dialog. Not to be confused with if this element is _part of_ a Dialog.
978  */
dialognull979 fun SemanticsPropertyReceiver.dialog() {
980     this[SemanticsProperties.IsDialog] = Unit
981 }
982 
983 /**
984  * The type of user interface element. Accessibility services might use this to describe the element
985  * or do customizations. Most roles can be automatically resolved by the semantics properties of
986  * this element. But some elements with subtle differences need an exact role. If an exact role is
987  * not listed in [Role], this property should not be set and the framework will automatically
988  * resolve it.
989  */
990 var SemanticsPropertyReceiver.role by SemanticsProperties.Role
991 
992 /**
993  * Test tag attached to this semantics node.
994  *
995  * This can be used to find nodes in testing frameworks:
996  * - In Compose's built-in unit test framework, use with
997  *   [onNodeWithTag][androidx.compose.ui.test.onNodeWithTag].
998  * - For newer AccessibilityNodeInfo-based integration test frameworks, it can be matched in the
999  *   extras with key "androidx.compose.ui.semantics.testTag"
1000  * - For legacy AccessibilityNodeInfo-based integration tests, it's optionally exposed as the
1001  *   resource id if [testTagsAsResourceId] is true (for matching with 'By.res' in UIAutomator).
1002  */
1003 var SemanticsPropertyReceiver.testTag by SemanticsProperties.TestTag
1004 
1005 /**
1006  * Text of the semantics node. It must be real text instead of developer-set content description.
1007  *
1008  * @see SemanticsPropertyReceiver.editableText
1009  */
1010 var SemanticsPropertyReceiver.text: AnnotatedString
1011     get() = throwSemanticsGetNotSupported()
1012     set(value) {
1013         set(SemanticsProperties.Text, listOf(value))
1014     }
1015 
1016 /**
1017  * Text substitution of the semantics node. This property is only available after calling
1018  * [SemanticsActions.SetTextSubstitution].
1019  */
1020 var SemanticsPropertyReceiver.textSubstitution by SemanticsProperties.TextSubstitution
1021 
1022 /**
1023  * Whether this element is showing the text substitution. This property is only available after
1024  * calling [SemanticsActions.SetTextSubstitution].
1025  */
1026 var SemanticsPropertyReceiver.isShowingTextSubstitution by
1027     SemanticsProperties.IsShowingTextSubstitution
1028 
1029 /**
1030  * The raw value of the text field after input transformations have been applied.
1031  *
1032  * This is an actual user input of the fields, e.g. a real password, after any input transformations
1033  * that might change or reject that input have been applied. This value is not affected by visual
1034  * transformations.
1035  */
1036 var SemanticsPropertyReceiver.inputText by SemanticsProperties.InputText
1037 
1038 /**
1039  * A visual value of the text field after output transformations that change the visual
1040  * representation of the field's state have been applied.
1041  *
1042  * This is the value displayed to the user, for example "*******" in a password field.
1043  */
1044 var SemanticsPropertyReceiver.editableText by SemanticsProperties.EditableText
1045 
1046 /** Text selection range for the text field. */
1047 var SemanticsPropertyReceiver.textSelectionRange by SemanticsProperties.TextSelectionRange
1048 
1049 /**
1050  * Contains the IME action provided by the node.
1051  *
1052  * For example, "go to next form field" or "submit".
1053  *
1054  * A node that specifies an action should also specify a callback to perform the action via
1055  * [onImeAction].
1056  */
1057 @Deprecated("Pass the ImeAction to onImeAction instead.")
1058 @get:Deprecated("Pass the ImeAction to onImeAction instead.")
1059 @set:Deprecated("Pass the ImeAction to onImeAction instead.")
1060 var SemanticsPropertyReceiver.imeAction by SemanticsProperties.ImeAction
1061 
1062 /**
1063  * Whether this element is selected (out of a list of possible selections).
1064  *
1065  * The presence of this property indicates that the element is selectable.
1066  */
1067 var SemanticsPropertyReceiver.selected by SemanticsProperties.Selected
1068 
1069 /**
1070  * This semantics marks node as a collection and provides the required information.
1071  *
1072  * @see collectionItemInfo
1073  */
1074 var SemanticsPropertyReceiver.collectionInfo by SemanticsProperties.CollectionInfo
1075 
1076 /**
1077  * This semantics marks node as an items of a collection and provides the required information.
1078  *
1079  * If you mark items of a collection, you should also be marking the collection with
1080  * [collectionInfo].
1081  */
1082 var SemanticsPropertyReceiver.collectionItemInfo by SemanticsProperties.CollectionItemInfo
1083 
1084 /**
1085  * The state of a toggleable component.
1086  *
1087  * The presence of this property indicates that the element is toggleable.
1088  */
1089 var SemanticsPropertyReceiver.toggleableState by SemanticsProperties.ToggleableState
1090 
1091 /** Whether this semantics node is editable, e.g. an editable text field. */
1092 var SemanticsPropertyReceiver.isEditable by SemanticsProperties.IsEditable
1093 
1094 /** The node is marked as a password. */
passwordnull1095 fun SemanticsPropertyReceiver.password() {
1096     this[SemanticsProperties.Password] = Unit
1097 }
1098 
1099 /**
1100  * Mark semantics node that contains invalid input or error.
1101  *
1102  * @param [description] a localized description explaining an error to the accessibility user
1103  */
SemanticsPropertyReceivernull1104 fun SemanticsPropertyReceiver.error(description: String) {
1105     this[SemanticsProperties.Error] = description
1106 }
1107 
1108 /**
1109  * The index of an item identified by a given key. The key is usually defined during the creation of
1110  * the container. If the key did not match any of the items' keys, the [mapping] must return -1.
1111  */
SemanticsPropertyReceivernull1112 fun SemanticsPropertyReceiver.indexForKey(mapping: (Any) -> Int) {
1113     this[SemanticsProperties.IndexForKey] = mapping
1114 }
1115 
1116 /**
1117  * Limits the number of characters that can be entered, e.g. in an editable text field. By default
1118  * this value is -1, signifying there is no maximum text length limit.
1119  */
1120 var SemanticsPropertyReceiver.maxTextLength by SemanticsProperties.MaxTextLength
1121 
1122 /**
1123  * The node is marked as a collection of horizontally or vertically stacked selectable elements.
1124  *
1125  * Unlike [collectionInfo] which marks a collection of any elements and asks developer to provide
1126  * all the required information like number of elements etc., this semantics will populate the
1127  * number of selectable elements automatically. Note that if you use this semantics with lazy
1128  * collections, it won't get the number of elements in the collection.
1129  *
1130  * @see SemanticsPropertyReceiver.selected
1131  */
SemanticsPropertyReceivernull1132 fun SemanticsPropertyReceiver.selectableGroup() {
1133     this[SemanticsProperties.SelectableGroup] = Unit
1134 }
1135 
1136 /** Custom actions which are defined by app developers. */
1137 var SemanticsPropertyReceiver.customActions by SemanticsActions.CustomActions
1138 
1139 /**
1140  * Action to get a Text/TextField node's [TextLayoutResult]. The result is the first element of
1141  * layout (the argument of the AccessibilityAction).
1142  *
1143  * @param label Optional label for this action.
1144  * @param action Action to be performed when the [SemanticsActions.GetTextLayoutResult] is called.
1145  */
SemanticsPropertyReceivernull1146 fun SemanticsPropertyReceiver.getTextLayoutResult(
1147     label: String? = null,
1148     action: ((MutableList<TextLayoutResult>) -> Boolean)?
1149 ) {
1150     this[SemanticsActions.GetTextLayoutResult] = AccessibilityAction(label, action)
1151 }
1152 
1153 /**
1154  * Action to be performed when the node is clicked (single-tapped).
1155  *
1156  * @param label Optional label for this action.
1157  * @param action Action to be performed when the [SemanticsActions.OnClick] is called.
1158  */
onClicknull1159 fun SemanticsPropertyReceiver.onClick(label: String? = null, action: (() -> Boolean)?) {
1160     this[SemanticsActions.OnClick] = AccessibilityAction(label, action)
1161 }
1162 
1163 /**
1164  * Action to be performed when the node is long clicked (long-pressed).
1165  *
1166  * @param label Optional label for this action.
1167  * @param action Action to be performed when the [SemanticsActions.OnLongClick] is called.
1168  */
onLongClicknull1169 fun SemanticsPropertyReceiver.onLongClick(label: String? = null, action: (() -> Boolean)?) {
1170     this[SemanticsActions.OnLongClick] = AccessibilityAction(label, action)
1171 }
1172 
1173 /**
1174  * Action to asynchronously scroll by a specified amount.
1175  *
1176  * [scrollByOffset] should be preferred in most cases, since it is synchronous and returns the
1177  * amount of scroll that was actually consumed.
1178  *
1179  * Expected to be used in conjunction with [verticalScrollAxisRange]/[horizontalScrollAxisRange].
1180  *
1181  * @param label Optional label for this action.
1182  * @param action Action to be performed when [SemanticsActions.ScrollBy] is called.
1183  */
scrollBynull1184 fun SemanticsPropertyReceiver.scrollBy(
1185     label: String? = null,
1186     action: ((x: Float, y: Float) -> Boolean)?
1187 ) {
1188     this[SemanticsActions.ScrollBy] = AccessibilityAction(label, action)
1189 }
1190 
1191 /**
1192  * Action to scroll by a specified amount and return how much of the offset was actually consumed.
1193  * E.g. if the node can't scroll at all in the given direction, [Offset.Zero] should be returned.
1194  * The action should not return until the scroll operation has finished.
1195  *
1196  * Expected to be used in conjunction with [verticalScrollAxisRange]/[horizontalScrollAxisRange].
1197  *
1198  * Unlike [scrollBy], this action is synchronous, and returns the amount of scroll consumed.
1199  *
1200  * @param action Action to be performed when [SemanticsActions.ScrollByOffset] is called.
1201  */
SemanticsPropertyReceivernull1202 fun SemanticsPropertyReceiver.scrollByOffset(action: suspend (offset: Offset) -> Offset) {
1203     this[SemanticsActions.ScrollByOffset] = action
1204 }
1205 
1206 /**
1207  * Action to scroll a container to the index of one of its items.
1208  *
1209  * The [action] should throw an [IllegalArgumentException] if the index is out of bounds.
1210  */
scrollToIndexnull1211 fun SemanticsPropertyReceiver.scrollToIndex(label: String? = null, action: (Int) -> Boolean) {
1212     this[SemanticsActions.ScrollToIndex] = AccessibilityAction(label, action)
1213 }
1214 
1215 /**
1216  * Action to autofill a TextField.
1217  *
1218  * Expected to be used in conjunction with [contentType] and [contentDataType] properties.
1219  *
1220  * @param label Optional label for this action.
1221  * @param action Action to be performed when the [SemanticsActions.OnAutofillText] is called.
1222  */
SemanticsPropertyReceivernull1223 fun SemanticsPropertyReceiver.onAutofillText(
1224     label: String? = null,
1225     action: ((AnnotatedString) -> Boolean)?
1226 ) {
1227     this[SemanticsActions.OnAutofillText] = AccessibilityAction(label, action)
1228 }
1229 
1230 /**
1231  * Action to set the current value of the progress bar.
1232  *
1233  * Expected to be used in conjunction with progressBarRangeInfo.
1234  *
1235  * @param label Optional label for this action.
1236  * @param action Action to be performed when the [SemanticsActions.SetProgress] is called.
1237  */
SemanticsPropertyReceivernull1238 fun SemanticsPropertyReceiver.setProgress(label: String? = null, action: ((Float) -> Boolean)?) {
1239     this[SemanticsActions.SetProgress] = AccessibilityAction(label, action)
1240 }
1241 
1242 /**
1243  * Action to set the text contents of this node.
1244  *
1245  * Expected to be used on editable text fields.
1246  *
1247  * @param label Optional label for this action.
1248  * @param action Action to be performed when [SemanticsActions.SetText] is called.
1249  */
SemanticsPropertyReceivernull1250 fun SemanticsPropertyReceiver.setText(
1251     label: String? = null,
1252     action: ((AnnotatedString) -> Boolean)?
1253 ) {
1254     this[SemanticsActions.SetText] = AccessibilityAction(label, action)
1255 }
1256 
1257 /**
1258  * Action to set the text substitution of this node.
1259  *
1260  * Expected to be used on non-editable text.
1261  *
1262  * Note, this action doesn't show the text substitution. Please call
1263  * [SemanticsPropertyReceiver.showTextSubstitution] to show the text substitution.
1264  *
1265  * @param label Optional label for this action.
1266  * @param action Action to be performed when [SemanticsActions.SetTextSubstitution] is called.
1267  */
setTextSubstitutionnull1268 fun SemanticsPropertyReceiver.setTextSubstitution(
1269     label: String? = null,
1270     action: ((AnnotatedString) -> Boolean)?
1271 ) {
1272     this[SemanticsActions.SetTextSubstitution] = AccessibilityAction(label, action)
1273 }
1274 
1275 /**
1276  * Action to show or hide the text substitution of this node.
1277  *
1278  * Expected to be used on non-editable text.
1279  *
1280  * Note, this action only takes effect when the node has the text substitution.
1281  *
1282  * @param label Optional label for this action.
1283  * @param action Action to be performed when [SemanticsActions.ShowTextSubstitution] is called.
1284  */
SemanticsPropertyReceivernull1285 fun SemanticsPropertyReceiver.showTextSubstitution(
1286     label: String? = null,
1287     action: ((Boolean) -> Boolean)?
1288 ) {
1289     this[SemanticsActions.ShowTextSubstitution] = AccessibilityAction(label, action)
1290 }
1291 
1292 /**
1293  * Action to clear the text substitution of this node.
1294  *
1295  * Expected to be used on non-editable text.
1296  *
1297  * @param label Optional label for this action.
1298  * @param action Action to be performed when [SemanticsActions.ClearTextSubstitution] is called.
1299  */
SemanticsPropertyReceivernull1300 fun SemanticsPropertyReceiver.clearTextSubstitution(
1301     label: String? = null,
1302     action: (() -> Boolean)?
1303 ) {
1304     this[SemanticsActions.ClearTextSubstitution] = AccessibilityAction(label, action)
1305 }
1306 
1307 /**
1308  * Action to insert text into this node at the current cursor position, or replacing the selection
1309  * if text is selected.
1310  *
1311  * Expected to be used on editable text fields.
1312  *
1313  * @param label Optional label for this action.
1314  * @param action Action to be performed when [SemanticsActions.InsertTextAtCursor] is called.
1315  */
SemanticsPropertyReceivernull1316 fun SemanticsPropertyReceiver.insertTextAtCursor(
1317     label: String? = null,
1318     action: ((AnnotatedString) -> Boolean)?
1319 ) {
1320     this[SemanticsActions.InsertTextAtCursor] = AccessibilityAction(label, action)
1321 }
1322 
1323 /**
1324  * Action to invoke the IME action handler configured on the node, as well as specify the type of
1325  * IME action provided by the node.
1326  *
1327  * Expected to be used on editable text fields.
1328  *
1329  * @param imeActionType The IME type, such as [ImeAction.Next] or [ImeAction.Search]
1330  * @param label Optional label for this action.
1331  * @param action Action to be performed when [SemanticsActions.OnImeAction] is called.
1332  * @see SemanticsProperties.ImeAction
1333  * @see SemanticsActions.OnImeAction
1334  */
SemanticsPropertyReceivernull1335 fun SemanticsPropertyReceiver.onImeAction(
1336     imeActionType: ImeAction,
1337     label: String? = null,
1338     action: (() -> Boolean)?
1339 ) {
1340     this[SemanticsProperties.ImeAction] = imeActionType
1341     this[SemanticsActions.OnImeAction] = AccessibilityAction(label, action)
1342 }
1343 
1344 // b/322269946
1345 @Suppress("unused")
1346 @Deprecated(
1347     message = "Use `SemanticsPropertyReceiver.onImeAction` instead.",
1348     replaceWith =
1349         ReplaceWith(
1350             "onImeAction(imeActionType = ImeAction.Default, label = label, action = action)",
1351             "androidx.compose.ui.semantics.onImeAction",
1352             "androidx.compose.ui.text.input.ImeAction",
1353         ),
1354     level = DeprecationLevel.ERROR,
1355 )
SemanticsPropertyReceivernull1356 fun SemanticsPropertyReceiver.performImeAction(label: String? = null, action: (() -> Boolean)?) {
1357     this[SemanticsActions.OnImeAction] = AccessibilityAction(label, action)
1358 }
1359 
1360 /**
1361  * Action to set text selection by character index range.
1362  *
1363  * If this action is provided, the selection data must be provided using [textSelectionRange].
1364  *
1365  * @param label Optional label for this action.
1366  * @param action Action to be performed when the [SemanticsActions.SetSelection] is called. The
1367  *   parameters to the action are: `startIndex`, `endIndex`, and whether the indices are relative to
1368  *   the original text or the transformed text (when a `VisualTransformation` is applied).
1369  */
setSelectionnull1370 fun SemanticsPropertyReceiver.setSelection(
1371     label: String? = null,
1372     action: ((startIndex: Int, endIndex: Int, relativeToOriginalText: Boolean) -> Boolean)?
1373 ) {
1374     this[SemanticsActions.SetSelection] = AccessibilityAction(label, action)
1375 }
1376 
1377 /**
1378  * Action to copy the text to the clipboard.
1379  *
1380  * @param label Optional label for this action.
1381  * @param action Action to be performed when the [SemanticsActions.CopyText] is called.
1382  */
SemanticsPropertyReceivernull1383 fun SemanticsPropertyReceiver.copyText(label: String? = null, action: (() -> Boolean)?) {
1384     this[SemanticsActions.CopyText] = AccessibilityAction(label, action)
1385 }
1386 
1387 /**
1388  * Action to cut the text and copy it to the clipboard.
1389  *
1390  * @param label Optional label for this action.
1391  * @param action Action to be performed when the [SemanticsActions.CutText] is called.
1392  */
SemanticsPropertyReceivernull1393 fun SemanticsPropertyReceiver.cutText(label: String? = null, action: (() -> Boolean)?) {
1394     this[SemanticsActions.CutText] = AccessibilityAction(label, action)
1395 }
1396 
1397 /**
1398  * This function adds the [SemanticsActions.PasteText] to the [SemanticsPropertyReceiver]. Use it to
1399  * indicate that element is open for accepting paste data from the clipboard. There is no need to
1400  * check if the clipboard data available as this is done by the framework. For this action to be
1401  * triggered, the element must also have the [SemanticsProperties.Focused] property set.
1402  *
1403  * @param label Optional label for this action.
1404  * @param action Action to be performed when the [SemanticsActions.PasteText] is called.
1405  * @see focused
1406  */
SemanticsPropertyReceivernull1407 fun SemanticsPropertyReceiver.pasteText(label: String? = null, action: (() -> Boolean)?) {
1408     this[SemanticsActions.PasteText] = AccessibilityAction(label, action)
1409 }
1410 
1411 /**
1412  * Action to expand an expandable node.
1413  *
1414  * @param label Optional label for this action.
1415  * @param action Action to be performed when the [SemanticsActions.Expand] is called.
1416  */
expandnull1417 fun SemanticsPropertyReceiver.expand(label: String? = null, action: (() -> Boolean)?) {
1418     this[SemanticsActions.Expand] = AccessibilityAction(label, action)
1419 }
1420 
1421 /**
1422  * Action to collapse an expandable node.
1423  *
1424  * @param label Optional label for this action.
1425  * @param action Action to be performed when the [SemanticsActions.Collapse] is called.
1426  */
SemanticsPropertyReceivernull1427 fun SemanticsPropertyReceiver.collapse(label: String? = null, action: (() -> Boolean)?) {
1428     this[SemanticsActions.Collapse] = AccessibilityAction(label, action)
1429 }
1430 
1431 /**
1432  * Action to dismiss a dismissible node.
1433  *
1434  * @param label Optional label for this action.
1435  * @param action Action to be performed when the [SemanticsActions.Dismiss] is called.
1436  */
SemanticsPropertyReceivernull1437 fun SemanticsPropertyReceiver.dismiss(label: String? = null, action: (() -> Boolean)?) {
1438     this[SemanticsActions.Dismiss] = AccessibilityAction(label, action)
1439 }
1440 
1441 /**
1442  * Action that gives input focus to this node.
1443  *
1444  * @param label Optional label for this action.
1445  * @param action Action to be performed when the [SemanticsActions.RequestFocus] is called.
1446  */
requestFocusnull1447 fun SemanticsPropertyReceiver.requestFocus(label: String? = null, action: (() -> Boolean)?) {
1448     this[SemanticsActions.RequestFocus] = AccessibilityAction(label, action)
1449 }
1450 
1451 /**
1452  * Action to page up.
1453  *
1454  * Using [Role.Carousel] will prevent this action from being sent to accessibility services.
1455  *
1456  * @param label Optional label for this action.
1457  * @param action Action to be performed when the [SemanticsActions.PageUp] is called.
1458  * @see [Role.Carousel] for more information.
1459  */
SemanticsPropertyReceivernull1460 fun SemanticsPropertyReceiver.pageUp(label: String? = null, action: (() -> Boolean)?) {
1461     this[SemanticsActions.PageUp] = AccessibilityAction(label, action)
1462 }
1463 
1464 /**
1465  * Action to page down.
1466  *
1467  * Using [Role.Carousel] will prevent this action from being sent to accessibility services.
1468  *
1469  * @param label Optional label for this action.
1470  * @param action Action to be performed when the [SemanticsActions.PageDown] is called.
1471  * @see [Role.Carousel] for more information.
1472  */
pageDownnull1473 fun SemanticsPropertyReceiver.pageDown(label: String? = null, action: (() -> Boolean)?) {
1474     this[SemanticsActions.PageDown] = AccessibilityAction(label, action)
1475 }
1476 
1477 /**
1478  * Action to page left.
1479  *
1480  * Using [Role.Carousel] will prevent this action from being sent to accessibility services.
1481  *
1482  * @param label Optional label for this action.
1483  * @param action Action to be performed when the [SemanticsActions.PageLeft] is called.
1484  * @see [Role.Carousel] for more information.
1485  */
pageLeftnull1486 fun SemanticsPropertyReceiver.pageLeft(label: String? = null, action: (() -> Boolean)?) {
1487     this[SemanticsActions.PageLeft] = AccessibilityAction(label, action)
1488 }
1489 
1490 /**
1491  * Action to page right.
1492  *
1493  * Using [Role.Carousel] will prevent this action from being sent to accessibility services.
1494  *
1495  * @param label Optional label for this action.
1496  * @param action Action to be performed when the [SemanticsActions.PageRight] is called.
1497  * @see [Role.Carousel] for more information.
1498  */
SemanticsPropertyReceivernull1499 fun SemanticsPropertyReceiver.pageRight(label: String? = null, action: (() -> Boolean)?) {
1500     this[SemanticsActions.PageRight] = AccessibilityAction(label, action)
1501 }
1502 
1503 /**
1504  * Action to get a scrollable's active view port amount for scrolling actions.
1505  *
1506  * @param label Optional label for this action.
1507  * @param action Action to be performed when the [SemanticsActions.GetScrollViewportLength] is
1508  *   called.
1509  */
SemanticsPropertyReceivernull1510 fun SemanticsPropertyReceiver.getScrollViewportLength(
1511     label: String? = null,
1512     action: (() -> Float?)
1513 ) {
1514     this[SemanticsActions.GetScrollViewportLength] =
1515         AccessibilityAction(label) {
1516             val viewport = action.invoke()
1517             if (viewport == null) {
1518                 false
1519             } else {
1520                 it.add(viewport)
1521                 true
1522             }
1523         }
1524 }
1525