diff --git a/litho-core/src/main/java/com/facebook/litho/CommonProps.kt b/litho-core/src/main/java/com/facebook/litho/CommonProps.kt index 464eefd33d..bc78cf938f 100644 --- a/litho-core/src/main/java/com/facebook/litho/CommonProps.kt +++ b/litho-core/src/main/java/com/facebook/litho/CommonProps.kt @@ -355,6 +355,10 @@ class CommonProps : LayoutProps, Equivalence { val screenReaderFocusable: Boolean get() = _nodeInfo?.screenReaderFocusState == NodeInfo.SCREEN_READER_FOCUS_SET_TRUE + fun setMinDurationBetweenContentChangesMillis(duration: Long) { + getOrCreateNodeInfo().minDurationBetweenContentChangesMillis = duration + } + fun clickable(isClickable: Boolean) { getOrCreateNodeInfo().setClickable(isClickable) } diff --git a/litho-core/src/main/java/com/facebook/litho/ComponentAccessibilityDelegate.java b/litho-core/src/main/java/com/facebook/litho/ComponentAccessibilityDelegate.java index 850b7d344c..037ff4448d 100644 --- a/litho-core/src/main/java/com/facebook/litho/ComponentAccessibilityDelegate.java +++ b/litho-core/src/main/java/com/facebook/litho/ComponentAccessibilityDelegate.java @@ -142,6 +142,11 @@ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCo mNodeInfo.getAccessibilityHeadingState() == NodeInfo.ACCESSIBILITY_HEADING_SET_TRUE); } + if (mNodeInfo != null && mNodeInfo.getMinDurationBetweenContentChangesMillis() != null) { + node.setMinDurationBetweenContentChangesMillis( + mNodeInfo.getMinDurationBetweenContentChangesMillis()); + } + if (mNodeInfo != null && mNodeInfo.getFocusOrder() != null && mountItem != null) { final ComponentContext scopedContext = getComponentContext(mountItem.getRenderTreeNode()); final FocusOrderModel focusOrder = mNodeInfo.getFocusOrder(); diff --git a/litho-core/src/main/java/com/facebook/litho/NodeInfo.kt b/litho-core/src/main/java/com/facebook/litho/NodeInfo.kt index b2e2ab3c75..0d6f69e72e 100644 --- a/litho-core/src/main/java/com/facebook/litho/NodeInfo.kt +++ b/litho-core/src/main/java/com/facebook/litho/NodeInfo.kt @@ -123,6 +123,7 @@ class NodeInfo : Equivalence { private var _sendAccessibilityEventUncheckedHandler: EventHandler? = null + private var _minDurationBetweenContentChangesMillis: Long? = null var visibility: Visibility? = null internal set(value) { @@ -404,6 +405,13 @@ class NodeInfo : Equivalence { _sendAccessibilityEventUncheckedHandler = sendAccessibilityEventUncheckedHandler } + var minDurationBetweenContentChangesMillis: Long? + get() = _minDurationBetweenContentChangesMillis + set(duration) { + flags = flags or PFLAG_MIN_DURATION_BETWEEN_CHANGES_IS_SET + _minDurationBetweenContentChangesMillis = duration + } + fun needsAccessibilityDelegate(): Boolean = _onInitializeAccessibilityEventHandler != null || _onInitializeAccessibilityNodeInfoHandler != null || @@ -671,6 +679,12 @@ class NodeInfo : Equivalence { return false } + if (!equals( + this.minDurationBetweenContentChangesMillis, + other.minDurationBetweenContentChangesMillis)) { + return false + } + return equals(this.viewTags, other.viewTags) } @@ -1012,5 +1026,9 @@ class NodeInfo : Equivalence { private const val PFLAG_VISIBILITY_IS_SET = 1L shl 34 private const val PFLAG_FOCUS_ORDER_IS_SET = 1L shl 35 + + // When this flag is set, setMinDurationBetweenContentChangesMillis was explicitly set on this + // node. + private const val PFLAG_MIN_DURATION_BETWEEN_CHANGES_IS_SET = 1L shl 36 } } diff --git a/litho-core/src/main/java/com/facebook/litho/accessibility/AccessibilityStyles.kt b/litho-core/src/main/java/com/facebook/litho/accessibility/AccessibilityStyles.kt index d9d2667e2a..58d8a49ba8 100644 --- a/litho-core/src/main/java/com/facebook/litho/accessibility/AccessibilityStyles.kt +++ b/litho-core/src/main/java/com/facebook/litho/accessibility/AccessibilityStyles.kt @@ -60,6 +60,7 @@ internal enum class AccessibilityField : StyleItemField { ON_PERFORM_ACTION_FOR_VIRTUAL_VIEW, ON_VIRTUAL_VIEW_KEYBOARD_FOCUS_CHANGED, SCREEN_READER_FOCUSABLE, + MIN_DURATION_BETWEEN_CONTENT_CHANGES, } @PublishedApi @@ -111,6 +112,8 @@ internal data class AccessibilityStyleItem( eventHandler(value as (VirtualViewKeyboardFocusChangedEvent) -> Unit)) AccessibilityField.SCREEN_READER_FOCUSABLE -> commonProps.screenReaderFocusable(value as Boolean) + AccessibilityField.MIN_DURATION_BETWEEN_CONTENT_CHANGES -> + commonProps.setMinDurationBetweenContentChangesMillis(value as Long) } } } @@ -312,6 +315,16 @@ inline fun Style.onInitializeAccessibilityNodeInfo( inline fun Style.screenReaderFocusable(isFocusable: Boolean): Style = this + AccessibilityStyleItem(AccessibilityField.SCREEN_READER_FOCUSABLE, isFocusable) +/** + * Sets the minimum time duration between two content change events, which is used in throttling + * content change events in accessibility services. + * + * See + * [androidx.core.view.accessibility.AccessibilityNodeInfoCompat.setMinDurationBetweenContentChangesMillis]. + */ +inline fun Style.minDurationBetweenContentChangesMillis(duration: Long): Style = + this + AccessibilityStyleItem(AccessibilityField.MIN_DURATION_BETWEEN_CONTENT_CHANGES, duration) + /** * Enum values for [importantForAccessibility]. *