Accessibility refactoring
commit_hash:22623192cdabbd2dcedf862b9a6d9aa8219e422b
@@ -1205,7 +1205,6 @@
|
||||
"client/android/div/src/main/java/com/yandex/div/core/util/validator/ExpressionValidator.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/util/validator/ExpressionValidator.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/util/validator/RegexValidator.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/util/validator/RegexValidator.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/util/validator/ValidatorItemData.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/util/validator/ValidatorItemData.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/view2/AccessibilityDelegateWrapper.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/view2/AccessibilityDelegateWrapper.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/view2/AccessibilityListDelegate.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/view2/AccessibilityListDelegate.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/view2/BindingContext.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/view2/BindingContext.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/view2/CompositeLogId.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/view2/CompositeLogId.kt",
|
||||
@@ -1480,8 +1479,10 @@
|
||||
"client/android/div/src/main/java/com/yandex/div/internal/widget/menu/NonScrollImageView.java":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/widget/menu/NonScrollImageView.java",
|
||||
"client/android/div/src/main/java/com/yandex/div/internal/widget/menu/OverflowMenuSubscriber.java":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/widget/menu/OverflowMenuSubscriber.java",
|
||||
"client/android/div/src/main/java/com/yandex/div/internal/widget/menu/OverflowMenuWrapper.java":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/widget/menu/OverflowMenuWrapper.java",
|
||||
"client/android/div/src/main/java/com/yandex/div/internal/widget/slider/SliderAccessibilityHelper.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/widget/slider/SliderAccessibilityHelper.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/internal/widget/slider/SliderDrawDelegate.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/widget/slider/SliderDrawDelegate.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/internal/widget/slider/SliderTextStyle.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/widget/slider/SliderTextStyle.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/internal/widget/slider/SliderThumbAnimatorListener.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/widget/slider/SliderThumbAnimatorListener.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/internal/widget/slider/SliderView.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/widget/slider/SliderView.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/internal/widget/slider/shapes/TextDrawDelegate.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/widget/slider/shapes/TextDrawDelegate.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/internal/widget/slider/shapes/TextDrawable.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/internal/widget/slider/shapes/TextDrawable.kt",
|
||||
@@ -1574,6 +1575,7 @@
|
||||
"client/android/div/src/test/java/com/yandex/div/core/view2/GlobalVariableScopesTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/core/view2/GlobalVariableScopesTest.kt",
|
||||
"client/android/div/src/test/java/com/yandex/div/core/view2/SetVariableValueTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/core/view2/SetVariableValueTest.kt",
|
||||
"client/android/div/src/test/java/com/yandex/div/core/view2/TestHelpers.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/core/view2/TestHelpers.kt",
|
||||
"client/android/div/src/test/java/com/yandex/div/core/view2/TypeAutoAccessibilityTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/core/view2/TypeAutoAccessibilityTest.kt",
|
||||
"client/android/div/src/test/java/com/yandex/div/core/view2/VariableUpdatesTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/core/view2/VariableUpdatesTest.kt",
|
||||
"client/android/div/src/test/java/com/yandex/div/core/view2/animations/DivComparatorTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/core/view2/animations/DivComparatorTest.kt",
|
||||
"client/android/div/src/test/java/com/yandex/div/core/view2/animations/DivStateComparatorTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/core/view2/animations/DivStateComparatorTest.kt",
|
||||
@@ -1649,7 +1651,6 @@
|
||||
"client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/SliderTests.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/SliderTests.kt",
|
||||
"client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/SuperLineHeightTextViewTest.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/SuperLineHeightTextViewTest.kt",
|
||||
"client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/TabsSwipeTest.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/TabsSwipeTest.kt",
|
||||
"client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/TypeAutoAccessibilityTest.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/TypeAutoAccessibilityTest.kt",
|
||||
"client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/TypedFocusActionsTest.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/TypedFocusActionsTest.kt",
|
||||
"client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/rule/ActivityParamsTestRule.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/rule/ActivityParamsTestRule.kt",
|
||||
"client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/rule/Rules.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/rule/Rules.kt",
|
||||
@@ -1679,7 +1680,6 @@
|
||||
"client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/steps/SliderSteps.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/steps/SliderSteps.kt",
|
||||
"client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/steps/SliderViews.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/steps/SliderViews.kt",
|
||||
"client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/steps/SuperLineHeightTextViewSteps.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/steps/SuperLineHeightTextViewSteps.kt",
|
||||
"client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/steps/TypeAutoAccessibilitySteps.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/steps/TypeAutoAccessibilitySteps.kt",
|
||||
"client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/steps/VisibilityActionsSteps.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/steps/VisibilityActionsSteps.kt",
|
||||
"client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/CharSequences.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/CharSequences.kt",
|
||||
"client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/OutsideActions.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/OutsideActions.kt",
|
||||
@@ -2389,8 +2389,6 @@
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API24_HDPI_540x1200/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API24_HDPI_540x1200/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API24_HDPI_540x1200/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API24_HDPI_540x1200/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API24_HDPI_540x1200/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API24_HDPI_540x1200/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API24_HDPI_540x1200/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API24_HDPI_540x1200/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API24_HDPI_540x1200/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API24_HDPI_540x1200/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API24_HDPI_540x1200/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API24_HDPI_540x1200/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API24_HDPI_540x1200/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API24_HDPI_540x1200/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API24_HDPI_540x1200/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API24_HDPI_540x1200/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png",
|
||||
@@ -3532,8 +3530,6 @@
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png",
|
||||
@@ -4675,8 +4671,6 @@
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png",
|
||||
@@ -5818,8 +5812,6 @@
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png",
|
||||
@@ -6961,8 +6953,6 @@
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png",
|
||||
@@ -8104,8 +8094,6 @@
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png",
|
||||
@@ -9247,8 +9235,6 @@
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png",
|
||||
@@ -10390,8 +10376,6 @@
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png",
|
||||
@@ -11533,8 +11517,6 @@
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step6.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step7.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text-properties/step8.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_focused_text_color/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step0.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-text/text_range_without_bound/step1.png",
|
||||
"client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png":"divkit/public/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.Div2InteractiveScreenshotTest/div-timer/timer-end-actions-tick-actions/step0.png",
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.yandex.div.core.actions
|
||||
|
||||
import android.view.View
|
||||
import com.yandex.div.core.view2.Div2View
|
||||
import com.yandex.div.core.view2.divs.gainAccessibilityFocus
|
||||
import com.yandex.div.core.view2.divs.widgets.DivInputView
|
||||
import com.yandex.div.json.expressions.ExpressionResolver
|
||||
import com.yandex.div2.DivActionFocusElement
|
||||
@@ -36,7 +35,6 @@ internal class DivActionTypedFocusElementHandler @Inject constructor() : DivActi
|
||||
?: return
|
||||
|
||||
requestedView.requestFocus()
|
||||
requestedView.gainAccessibilityFocus()
|
||||
when (requestedView) {
|
||||
is DivInputView -> requestedView.openKeyboard()
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import android.view.Gravity
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
|
||||
import android.widget.PopupWindow
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
@@ -30,7 +31,6 @@ import com.yandex.div.core.util.isActuallyLaidOut
|
||||
import com.yandex.div.core.view2.BindingContext
|
||||
import com.yandex.div.core.view2.Div2View
|
||||
import com.yandex.div.core.view2.DivVisibilityActionTracker
|
||||
import com.yandex.div.core.view2.divs.sendAccessibilityEventUnchecked
|
||||
import com.yandex.div.core.view2.divs.toLayoutParamsSize
|
||||
import com.yandex.div.core.view2.divs.toPx
|
||||
import com.yandex.div.core.view2.errors.ErrorCollectors
|
||||
@@ -432,3 +432,19 @@ private fun Div2View.getWindowFrame(): Rect {
|
||||
getWindowVisibleDisplayFrame(windowFrame)
|
||||
return windowFrame
|
||||
}
|
||||
|
||||
private fun sendAccessibilityEventUnchecked(
|
||||
event: Int,
|
||||
view: View?,
|
||||
accessibilityStateProvider: AccessibilityStateProvider
|
||||
) {
|
||||
view ?: return
|
||||
if (!accessibilityStateProvider.isAccessibilityEnabled(view.context)) return
|
||||
val accessibilityEvent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
AccessibilityEvent(event)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
AccessibilityEvent.obtain(event)
|
||||
}
|
||||
view.sendAccessibilityEventUnchecked(accessibilityEvent)
|
||||
}
|
||||
|
||||
@@ -14,22 +14,22 @@ internal class AccessibilityStateProvider @Inject constructor(
|
||||
fun isAccessibilityEnabled(context: Context): Boolean {
|
||||
return when {
|
||||
!a11yConfigurationEnabled -> false
|
||||
touchModeEnabled != null -> touchModeEnabled!!
|
||||
touchExplorationEnabled != null -> touchExplorationEnabled!!
|
||||
else -> {
|
||||
evaluateTouchModeEnabled(context)
|
||||
touchModeEnabled!!
|
||||
touchExplorationEnabled!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
var touchModeEnabled: Boolean? = null
|
||||
var touchExplorationEnabled: Boolean? = null
|
||||
|
||||
fun evaluateTouchModeEnabled(context: Context) {
|
||||
if (touchModeEnabled != null) return
|
||||
if (touchExplorationEnabled != null) return
|
||||
val accessibilityManager = context.getSystemService(
|
||||
Context.ACCESSIBILITY_SERVICE) as? AccessibilityManager
|
||||
touchModeEnabled = accessibilityManager?.isTouchExplorationEnabled ?: false
|
||||
touchExplorationEnabled = accessibilityManager?.isTouchExplorationEnabled ?: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.yandex.div.core.util
|
||||
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import android.view.View.OnAttachStateChangeListener
|
||||
import androidx.core.view.ViewCompat
|
||||
@@ -39,14 +38,6 @@ inline fun View.doOnActualLayout(crossinline action: (view: View) -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
internal fun View.makeFocusable() {
|
||||
isFocusable = true
|
||||
isFocusableInTouchMode = true
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
defaultFocusHighlightEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun View.doOnHierarchyLayout(
|
||||
crossinline action: (view: View) -> Unit,
|
||||
onEnqueuedAction: () -> Unit
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
package com.yandex.div.core.view2
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import androidx.core.view.AccessibilityDelegateCompat
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
|
||||
import androidx.core.view.accessibility.AccessibilityNodeProviderCompat
|
||||
|
||||
internal class AccessibilityDelegateWrapper(
|
||||
private val originalDelegate: AccessibilityDelegateCompat?,
|
||||
|
||||
var initializeAccessibilityNodeInfo:
|
||||
(host: View?, info: AccessibilityNodeInfoCompat?) -> Unit = { _, _ -> },
|
||||
|
||||
var actionsAccessibilityNodeInfo:
|
||||
(host: View?, info: AccessibilityNodeInfoCompat?) -> Unit = { _, _ -> },
|
||||
|
||||
) : AccessibilityDelegateCompat() {
|
||||
|
||||
override fun sendAccessibilityEvent(host: View, eventType: Int) {
|
||||
originalDelegate?.sendAccessibilityEvent(host, eventType)
|
||||
?: super.sendAccessibilityEvent(host, eventType)
|
||||
}
|
||||
|
||||
override fun sendAccessibilityEventUnchecked(host: View, event: AccessibilityEvent) {
|
||||
originalDelegate?.sendAccessibilityEventUnchecked(host, event)
|
||||
?: super.sendAccessibilityEventUnchecked(host, event)
|
||||
}
|
||||
|
||||
override fun dispatchPopulateAccessibilityEvent(
|
||||
host: View,
|
||||
event: AccessibilityEvent
|
||||
): Boolean {
|
||||
return originalDelegate?.dispatchPopulateAccessibilityEvent(host, event)
|
||||
?: super.dispatchPopulateAccessibilityEvent(host, event)
|
||||
}
|
||||
|
||||
override fun onPopulateAccessibilityEvent(host: View, event: AccessibilityEvent) {
|
||||
originalDelegate?.onPopulateAccessibilityEvent(host, event)
|
||||
?: super.onPopulateAccessibilityEvent(host, event)
|
||||
}
|
||||
|
||||
override fun onInitializeAccessibilityEvent(host: View, event: AccessibilityEvent) {
|
||||
originalDelegate?.onInitializeAccessibilityEvent(host, event)
|
||||
?: super.onInitializeAccessibilityEvent(host, event)
|
||||
}
|
||||
|
||||
override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
|
||||
originalDelegate?.onInitializeAccessibilityNodeInfo(host, info)
|
||||
?: super.onInitializeAccessibilityNodeInfo(host, info)
|
||||
|
||||
initializeAccessibilityNodeInfo(host, info)
|
||||
|
||||
actionsAccessibilityNodeInfo(host, info)
|
||||
}
|
||||
|
||||
override fun onRequestSendAccessibilityEvent(
|
||||
host: ViewGroup,
|
||||
child: View,
|
||||
event: AccessibilityEvent
|
||||
): Boolean {
|
||||
return originalDelegate?.onRequestSendAccessibilityEvent(host, child, event)
|
||||
?: super.onRequestSendAccessibilityEvent(host, child, event)
|
||||
}
|
||||
|
||||
override fun getAccessibilityNodeProvider(host: View): AccessibilityNodeProviderCompat? {
|
||||
return originalDelegate?.getAccessibilityNodeProvider(host)
|
||||
?: super.getAccessibilityNodeProvider(host)
|
||||
}
|
||||
|
||||
override fun performAccessibilityAction(host: View, action: Int, args: Bundle?): Boolean {
|
||||
return originalDelegate?.performAccessibilityAction(host, action, args)
|
||||
?: super.performAccessibilityAction(host, action, args)
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,6 @@ import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewTreeObserver
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import android.view.accessibility.AccessibilityNodeInfo
|
||||
import android.widget.Button
|
||||
import androidx.core.view.AccessibilityDelegateCompat
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
|
||||
|
||||
@@ -89,7 +89,6 @@ import com.yandex.div.json.expressions.ExpressionResolver
|
||||
import com.yandex.div.util.INVALID_STATE_ID
|
||||
import com.yandex.div.util.getInitialStateId
|
||||
import com.yandex.div2.Div
|
||||
import com.yandex.div2.DivAccessibility
|
||||
import com.yandex.div2.DivAction
|
||||
import com.yandex.div2.DivData
|
||||
import com.yandex.div2.DivPatch
|
||||
@@ -131,7 +130,6 @@ class Div2View private constructor(
|
||||
private val divDataChangedObservers = mutableListOf<DivDataChangedObserver>()
|
||||
private val persistentDivDataObservers = ObserverList<PersistentDivDataObserver>()
|
||||
private val viewToDivBindings = WeakHashMap<View, Div>()
|
||||
private val propagatedAccessibilityModes = WeakHashMap<View, DivAccessibility.Mode>()
|
||||
private val bulkActionsHandler = BulkActionHandler()
|
||||
private val divVideoActionHandler: DivVideoActionHandler
|
||||
get() = div2Component.divVideoActionHandler
|
||||
@@ -709,7 +707,6 @@ class Div2View private constructor(
|
||||
|
||||
private fun stopLoadAndSubscriptions() {
|
||||
viewToDivBindings.clear()
|
||||
propagatedAccessibilityModes.clear()
|
||||
cancelTooltips() // Depends on children, should be called before removing them
|
||||
clearSubscriptions()
|
||||
divDataChangedObservers.clear()
|
||||
@@ -1160,20 +1157,6 @@ class Div2View private constructor(
|
||||
|
||||
internal fun takeBindingDiv(view: View) = viewToDivBindings[view]
|
||||
|
||||
internal fun setPropagatedAccessibilityMode(view: View, mode: DivAccessibility.Mode) {
|
||||
propagatedAccessibilityModes[view] = mode
|
||||
}
|
||||
|
||||
internal fun getPropagatedAccessibilityMode(view: View): DivAccessibility.Mode? {
|
||||
return propagatedAccessibilityModes[view]
|
||||
}
|
||||
|
||||
internal fun isDescendantAccessibilityMode(view: View): Boolean {
|
||||
return (view.parent as? View)?.let { parent ->
|
||||
propagatedAccessibilityModes[parent] == propagatedAccessibilityModes[view]
|
||||
} ?: false
|
||||
}
|
||||
|
||||
/**
|
||||
* @return exception if setting variable failed, null otherwise.
|
||||
*/
|
||||
|
||||
@@ -1,26 +1,35 @@
|
||||
package com.yandex.div.core.view2
|
||||
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import android.widget.CheckBox
|
||||
import android.widget.RadioButton
|
||||
import android.view.View.OnLayoutChangeListener
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.AccessibilityDelegateCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.isVisible
|
||||
import com.yandex.div.core.Disposable
|
||||
import com.yandex.div.core.annotations.Mockable
|
||||
import com.yandex.div.core.dagger.DivScope
|
||||
import com.yandex.div.core.dagger.ExperimentFlag
|
||||
import com.yandex.div.core.experiments.Experiment.ACCESSIBILITY_ENABLED
|
||||
import com.yandex.div.core.util.AccessibilityStateProvider
|
||||
import com.yandex.div.core.util.expressionSubscriber
|
||||
import com.yandex.div.core.view2.backbutton.BackHandlingRecyclerView
|
||||
import com.yandex.div.core.view2.divs.widgets.DivInputView
|
||||
import com.yandex.div.core.view2.divs.widgets.DivSliderView
|
||||
import com.yandex.div.core.view2.divs.widgets.DivCollectionHolder
|
||||
import com.yandex.div.internal.core.ExpressionSubscriber
|
||||
import com.yandex.div.json.expressions.ExpressionResolver
|
||||
import com.yandex.div.json.expressions.equalsToConstant
|
||||
import com.yandex.div.json.expressions.isConstantOrNull
|
||||
import com.yandex.div2.DivAccessibility
|
||||
import com.yandex.div2.DivBase
|
||||
import com.yandex.div2.DivContainer
|
||||
import com.yandex.div2.DivGallery
|
||||
import com.yandex.div2.DivGifImage
|
||||
import com.yandex.div2.DivImage
|
||||
import com.yandex.div2.DivInput
|
||||
import com.yandex.div2.DivSelect
|
||||
import com.yandex.div2.DivSeparator
|
||||
import com.yandex.div2.DivSlider
|
||||
import com.yandex.div2.DivTabs
|
||||
import com.yandex.div2.DivText
|
||||
@@ -35,173 +44,78 @@ internal class DivAccessibilityBinder @Inject constructor(
|
||||
@ExperimentFlag(ACCESSIBILITY_ENABLED) val enabled: Boolean,
|
||||
private val accessibilityStateProvider: AccessibilityStateProvider,
|
||||
) {
|
||||
fun bindAccessibilityMode(
|
||||
|
||||
fun bind(
|
||||
view: View,
|
||||
divView: Div2View,
|
||||
mode: DivAccessibility.Mode?,
|
||||
divBase: DivBase,
|
||||
newDiv: DivBase,
|
||||
oldDiv: DivBase?,
|
||||
resolver: ExpressionResolver,
|
||||
subscriber: ExpressionSubscriber,
|
||||
) {
|
||||
if (!enabled) {
|
||||
return
|
||||
}
|
||||
val parentMode = (view.parent as? View)?.let {
|
||||
divView.getPropagatedAccessibilityMode(it)
|
||||
}
|
||||
|
||||
if (parentMode != null) {
|
||||
val propagatedMode = getPropagatedMode(parentMode, mode ?: divBase.getDefaultAccessibilityMode)
|
||||
view.applyAccessibilityMode(
|
||||
propagatedMode,
|
||||
divView,
|
||||
isDescendant = parentMode == propagatedMode
|
||||
)
|
||||
} else {
|
||||
view.applyAccessibilityMode(mode ?: divBase.getDefaultAccessibilityMode, divView, isDescendant = false)
|
||||
}
|
||||
}
|
||||
|
||||
fun bindType(view: View, divBase: DivBase, type: DivAccessibility.Type, resolver: ExpressionResolver) {
|
||||
if (!accessibilityStateProvider.isAccessibilityEnabled(view.context)) {
|
||||
if (newDiv.accessibility == null && oldDiv?.accessibility == null) {
|
||||
// Shortcut for empty accessibility binding
|
||||
if (enabled) {
|
||||
view.applyMode()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val originalDelegate = ViewCompat.getAccessibilityDelegate(view)
|
||||
val accessibilityType = type.toAccessibilityType(divBase, resolver)
|
||||
view.bindType(newDiv, oldDiv)
|
||||
view.bindDescriptionAndHint(newDiv, oldDiv, resolver, subscriber)
|
||||
view.bindMode(newDiv, oldDiv, resolver, subscriber)
|
||||
view.bindStateDescription(newDiv, oldDiv, resolver, subscriber)
|
||||
//TODO: bind 'muteAfterAction' property
|
||||
}
|
||||
|
||||
val accessibilityDelegate =
|
||||
if (accessibilityType == AccessibilityType.LIST && view is BackHandlingRecyclerView) {
|
||||
AccessibilityListDelegate(view)
|
||||
} else if (originalDelegate is AccessibilityDelegateWrapper) {
|
||||
originalDelegate.apply {
|
||||
initializeAccessibilityNodeInfo = { _, info ->
|
||||
info?.bindType(accessibilityType)
|
||||
}
|
||||
// region Type
|
||||
|
||||
private fun View.bindType(newDiv: DivBase, oldDiv: DivBase?) {
|
||||
if (!accessibilityStateProvider.isAccessibilityEnabled(context)) return
|
||||
|
||||
if (oldDiv != null && newDiv.accessibility?.type == oldDiv.accessibility?.type) return
|
||||
applyType(newDiv, newDiv.accessibility?.type)
|
||||
}
|
||||
|
||||
private fun View.applyType(divBase: DivBase, accessibilityType: DivAccessibility.Type? = null) {
|
||||
val type = accessibilityType ?: DivAccessibility.Type.AUTO
|
||||
getAccessibilityDelegate(this, type.toAccessibilityType(divBase))?.let {
|
||||
ViewCompat.setAccessibilityDelegate(this, it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAccessibilityDelegate(view: View, type: AccessibilityType): AccessibilityDelegateCompat? {
|
||||
if (type == AccessibilityType.LIST && view is BackHandlingRecyclerView) {
|
||||
return AccessibilityListDelegate(view)
|
||||
}
|
||||
|
||||
val className = type.toClassName
|
||||
val heading = type == AccessibilityType.HEADER
|
||||
val autoClassName = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) view.accessibilityClassName else null
|
||||
if ((className.isEmpty() || className == autoClassName) && !heading) return null
|
||||
|
||||
return object : AccessibilityDelegateCompat() {
|
||||
override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
|
||||
super.onInitializeAccessibilityNodeInfo(host, info)
|
||||
if (className.isNotEmpty()) {
|
||||
info.className = className
|
||||
}
|
||||
} else {
|
||||
AccessibilityDelegateWrapper(
|
||||
originalDelegate,
|
||||
initializeAccessibilityNodeInfo = { _, info ->
|
||||
info?.bindType(accessibilityType)
|
||||
})
|
||||
info.isHeading = heading
|
||||
}
|
||||
|
||||
ViewCompat.setAccessibilityDelegate(view, accessibilityDelegate)
|
||||
}
|
||||
|
||||
private val DivBase.getDefaultAccessibilityMode: DivAccessibility.Mode
|
||||
get() = when (this) {
|
||||
is DivImage -> if (accessibility == null && doubletapActions.isNullOrEmpty() &&
|
||||
actions.isNullOrEmpty() && longtapActions.isNullOrEmpty()) {
|
||||
DivAccessibility.Mode.EXCLUDE
|
||||
} else {
|
||||
DivAccessibility.Mode.DEFAULT
|
||||
}
|
||||
|
||||
is DivSeparator -> if (accessibility == null && doubletapActions.isNullOrEmpty() &&
|
||||
actions.isNullOrEmpty() && longtapActions.isNullOrEmpty()) {
|
||||
DivAccessibility.Mode.EXCLUDE
|
||||
} else {
|
||||
DivAccessibility.Mode.DEFAULT
|
||||
}
|
||||
|
||||
else -> DivAccessibility.Mode.DEFAULT
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets [AccessibilityNodeInfoCompat]'s className so that TalkBack could
|
||||
* properly recognize role of View provided by [DivAccessibility.Type].
|
||||
* For example, if [type] is [DivAccessibility.Type.BUTTON], TalkBack announces View as "Button".
|
||||
*/
|
||||
private fun AccessibilityNodeInfoCompat.bindType(type: AccessibilityType) {
|
||||
this.className = when (type) {
|
||||
AccessibilityType.NONE -> ""
|
||||
AccessibilityType.BUTTON -> "android.widget.Button"
|
||||
AccessibilityType.EDIT_TEXT -> "android.widget.EditText"
|
||||
AccessibilityType.HEADER -> "android.widget.TextView"
|
||||
AccessibilityType.IMAGE -> "android.widget.ImageView"
|
||||
AccessibilityType.LIST -> ""
|
||||
AccessibilityType.PAGER -> "androidx.viewpager.widget.ViewPager"
|
||||
AccessibilityType.SLIDER -> "android.widget.SeekBar"
|
||||
AccessibilityType.SELECT -> "android.widget.Spinner"
|
||||
AccessibilityType.TAB_WIDGET -> "android.widget.TabWidget"
|
||||
AccessibilityType.TEXT -> "android.widget.TextView"
|
||||
AccessibilityType.CHECK_BOX -> "android.widget.CheckBox"
|
||||
AccessibilityType.RADIO_BUTTON -> "android.widget.RadioButton"
|
||||
}
|
||||
|
||||
if (AccessibilityType.HEADER == type) {
|
||||
this.isHeading = true
|
||||
}
|
||||
}
|
||||
|
||||
private val DivAccessibility.Mode.priority
|
||||
get() = when (this) {
|
||||
DivAccessibility.Mode.EXCLUDE -> 0
|
||||
DivAccessibility.Mode.MERGE -> 1
|
||||
DivAccessibility.Mode.DEFAULT -> 2
|
||||
}
|
||||
|
||||
private fun getPropagatedMode(
|
||||
parentMode: DivAccessibility.Mode,
|
||||
mode: DivAccessibility.Mode
|
||||
): DivAccessibility.Mode {
|
||||
return if (parentMode.priority < mode.priority) parentMode else mode
|
||||
}
|
||||
|
||||
private fun View.applyAccessibilityMode(
|
||||
mode: DivAccessibility.Mode,
|
||||
divView: Div2View,
|
||||
isDescendant: Boolean
|
||||
) {
|
||||
when (mode) {
|
||||
DivAccessibility.Mode.MERGE -> {
|
||||
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
|
||||
if (!isDescendant) {
|
||||
isFocusable = this !is DivSliderView
|
||||
} else {
|
||||
setActionable(false)
|
||||
}
|
||||
}
|
||||
DivAccessibility.Mode.EXCLUDE -> {
|
||||
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
|
||||
isFocusable = false
|
||||
if (this is DivInputView) isFocusableInTouchMode = true
|
||||
}
|
||||
DivAccessibility.Mode.DEFAULT -> {
|
||||
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
|
||||
isFocusable = this !is DivSliderView
|
||||
}
|
||||
}
|
||||
divView.setPropagatedAccessibilityMode(this, mode)
|
||||
}
|
||||
|
||||
private fun View.setActionable(actionable: Boolean) {
|
||||
isClickable = actionable
|
||||
isLongClickable = actionable
|
||||
isFocusable = actionable
|
||||
}
|
||||
|
||||
private fun DivImage.isClickable(resolver: ExpressionResolver): Boolean =
|
||||
if (action != null && action?.isEnabled?.evaluate(resolver) == true) {
|
||||
true
|
||||
} else if (actions != null && actions?.any { it.isEnabled.evaluate(resolver) } == true) {
|
||||
true
|
||||
} else longtapActions != null && longtapActions?.any { it.isEnabled.evaluate(resolver) } == true
|
||||
|
||||
private fun DivAccessibility.Type.toAccessibilityType(
|
||||
div: DivBase, resolver: ExpressionResolver
|
||||
): AccessibilityType = when (this) {
|
||||
private fun DivAccessibility.Type.toAccessibilityType(div: DivBase): AccessibilityType {
|
||||
return when (this) {
|
||||
DivAccessibility.Type.AUTO -> when {
|
||||
div.accessibility?.mode?.evaluate(resolver) == DivAccessibility.Mode.EXCLUDE -> AccessibilityType.NONE
|
||||
div is DivInput -> AccessibilityType.EDIT_TEXT
|
||||
div is DivText -> AccessibilityType.TEXT
|
||||
div is DivTabs -> AccessibilityType.TAB_WIDGET
|
||||
div is DivSelect -> AccessibilityType.SELECT
|
||||
div is DivSlider -> AccessibilityType.SLIDER
|
||||
div is DivImage && (div.accessibility != null || div.isClickable(resolver))-> AccessibilityType.IMAGE
|
||||
div is DivImage -> AccessibilityType.IMAGE
|
||||
div is DivGifImage -> AccessibilityType.IMAGE
|
||||
div is DivGallery && div.accessibility?.description != null -> AccessibilityType.PAGER
|
||||
div is RadioButton -> AccessibilityType.RADIO_BUTTON
|
||||
div is CheckBox -> AccessibilityType.CHECK_BOX
|
||||
div is DivContainer -> AccessibilityType.CONTAINER
|
||||
else -> AccessibilityType.NONE
|
||||
}
|
||||
DivAccessibility.Type.NONE -> AccessibilityType.NONE
|
||||
@@ -216,6 +130,161 @@ internal class DivAccessibilityBinder @Inject constructor(
|
||||
DivAccessibility.Type.RADIO -> AccessibilityType.RADIO_BUTTON
|
||||
DivAccessibility.Type.CHECKBOX -> AccessibilityType.CHECK_BOX
|
||||
}
|
||||
}
|
||||
|
||||
private val AccessibilityType.toClassName: String get() {
|
||||
return when (this) {
|
||||
AccessibilityType.NONE -> ""
|
||||
AccessibilityType.BUTTON -> "android.widget.Button"
|
||||
AccessibilityType.EDIT_TEXT -> "android.widget.EditText"
|
||||
AccessibilityType.HEADER -> ""
|
||||
AccessibilityType.IMAGE -> "android.widget.ImageView"
|
||||
AccessibilityType.LIST -> ""
|
||||
AccessibilityType.PAGER -> "androidx.viewpager.widget.ViewPager"
|
||||
AccessibilityType.SLIDER -> ""
|
||||
AccessibilityType.SELECT -> "android.widget.Spinner"
|
||||
AccessibilityType.TAB_WIDGET -> "android.widget.TabWidget"
|
||||
AccessibilityType.TEXT -> "android.widget.TextView"
|
||||
AccessibilityType.CHECK_BOX -> "android.widget.CheckBox"
|
||||
AccessibilityType.RADIO_BUTTON -> "android.widget.RadioButton"
|
||||
AccessibilityType.CONTAINER -> "android.view.ViewGroup"
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Description and Hint
|
||||
|
||||
private fun View.bindDescriptionAndHint(
|
||||
newDiv: DivBase,
|
||||
oldDiv: DivBase?,
|
||||
resolver: ExpressionResolver,
|
||||
subscriber: ExpressionSubscriber
|
||||
) {
|
||||
val newDescription = newDiv.accessibility?.description
|
||||
val newHint = newDiv.accessibility?.hint
|
||||
if (newDescription.equalsToConstant(oldDiv?.accessibility?.description) &&
|
||||
newHint.equalsToConstant(oldDiv?.accessibility?.hint)) {
|
||||
return
|
||||
}
|
||||
|
||||
applyDescriptionAndHint(newDescription?.evaluate(resolver), newHint?.evaluate(resolver))
|
||||
|
||||
if (newDescription.isConstantOrNull() && newHint.isConstantOrNull()) return
|
||||
|
||||
val callback = { _: Any ->
|
||||
applyDescriptionAndHint(newDescription?.evaluate(resolver), newHint?.evaluate(resolver))
|
||||
}
|
||||
subscriber.addSubscription(newDescription?.observe(resolver, callback))
|
||||
subscriber.addSubscription(newHint?.observe(resolver, callback))
|
||||
}
|
||||
|
||||
private fun View.applyDescriptionAndHint(description: String?, hint: String?) {
|
||||
contentDescription = when {
|
||||
description == null -> hint
|
||||
hint == null -> description
|
||||
else -> "$description\n$hint"
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Mode
|
||||
|
||||
private fun View.bindMode(
|
||||
newDiv: DivBase,
|
||||
oldDiv: DivBase?,
|
||||
resolver: ExpressionResolver,
|
||||
subscriber: ExpressionSubscriber
|
||||
) {
|
||||
if (!enabled) return
|
||||
|
||||
val newMode = newDiv.accessibility?.mode
|
||||
if (newMode.equalsToConstant(oldDiv?.accessibility?.mode)) return
|
||||
|
||||
applyMode(newMode?.evaluate(resolver))
|
||||
|
||||
if (newMode.isConstantOrNull()) return
|
||||
|
||||
subscriber.addSubscription(newMode?.observe(resolver) { applyMode(it) })
|
||||
}
|
||||
|
||||
private fun View.applyMode(mode: DivAccessibility.Mode? = null) {
|
||||
if (this !is ViewGroup) {
|
||||
importantForAccessibility = when {
|
||||
mode == DivAccessibility.Mode.EXCLUDE -> View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
|
||||
!contentDescription.isNullOrBlank() -> View.IMPORTANT_FOR_ACCESSIBILITY_YES
|
||||
else -> View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (this !is DivCollectionHolder) return
|
||||
|
||||
if (mode == DivAccessibility.Mode.MERGE) {
|
||||
updateContainerMode()
|
||||
if (accessibilityObserver != null) return
|
||||
|
||||
val observer = createContentObserver()
|
||||
expressionSubscriber.addSubscription(observer)
|
||||
accessibilityObserver = observer
|
||||
return
|
||||
}
|
||||
|
||||
accessibilityObserver?.close()
|
||||
accessibilityObserver = null
|
||||
|
||||
ViewCompat.setScreenReaderFocusable(this, false)
|
||||
importantForAccessibility = if (mode == DivAccessibility.Mode.EXCLUDE) {
|
||||
View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
|
||||
} else {
|
||||
View.IMPORTANT_FOR_ACCESSIBILITY_NO
|
||||
}
|
||||
}
|
||||
|
||||
private fun ViewGroup.createContentObserver() = object : OnLayoutChangeListener, Disposable {
|
||||
|
||||
init {
|
||||
addOnLayoutChangeListener(this)
|
||||
}
|
||||
|
||||
override fun onLayoutChange(
|
||||
v: View?, left: Int, top: Int, right: Int, bottom: Int,
|
||||
oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int
|
||||
) = updateContainerMode()
|
||||
|
||||
override fun close() = removeOnLayoutChangeListener(this)
|
||||
}
|
||||
|
||||
private fun ViewGroup.updateContainerMode() {
|
||||
val hasContent = children.any { it.isVisible }
|
||||
ViewCompat.setScreenReaderFocusable(this, hasContent)
|
||||
importantForAccessibility =
|
||||
if (hasContent) View.IMPORTANT_FOR_ACCESSIBILITY_YES else View.IMPORTANT_FOR_ACCESSIBILITY_NO
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region State description
|
||||
|
||||
private fun View.bindStateDescription(
|
||||
newDiv: DivBase,
|
||||
oldDiv: DivBase?,
|
||||
resolver: ExpressionResolver,
|
||||
subscriber: ExpressionSubscriber
|
||||
) {
|
||||
val newStateDescription = newDiv.accessibility?.stateDescription
|
||||
if (newStateDescription.equalsToConstant(oldDiv?.accessibility?.stateDescription)) return
|
||||
|
||||
applyStateDescription(newStateDescription?.evaluate(resolver))
|
||||
|
||||
if (newStateDescription.isConstantOrNull()) return
|
||||
|
||||
subscriber.addSubscription(newStateDescription?.observe(resolver) { applyStateDescription(it) })
|
||||
}
|
||||
|
||||
private fun View.applyStateDescription(stateDescription: String?) =
|
||||
ViewCompat.setStateDescription(this, stateDescription)
|
||||
|
||||
private enum class AccessibilityType {
|
||||
NONE,
|
||||
@@ -231,5 +300,6 @@ internal class DivAccessibilityBinder @Inject constructor(
|
||||
TEXT,
|
||||
RADIO_BUTTON,
|
||||
CHECK_BOX,
|
||||
CONTAINER,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.yandex.div.core.view2.backbutton
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import com.yandex.div.core.view2.backbutton.BackKeyPressedHelper.OnBackClickListener
|
||||
import com.yandex.div.core.view2.divs.gainAccessibilityFocus
|
||||
|
||||
/**
|
||||
* This class helps to handle BACK key inside some View.
|
||||
@@ -30,7 +31,7 @@ internal class BackKeyPressedHelper(private val mOwnerView: View) {
|
||||
*/
|
||||
fun setOnBackClickListener(onBackClickListener: OnBackClickListener?) {
|
||||
mOnBackClickListener = onBackClickListener
|
||||
setupFocus()
|
||||
setupAccessibilityFocus()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,7 +62,7 @@ internal class BackKeyPressedHelper(private val mOwnerView: View) {
|
||||
*/
|
||||
fun onWindowFocusChanged(hasWindowFocus: Boolean) {
|
||||
if (hasWindowFocus) {
|
||||
setupFocus()
|
||||
setupAccessibilityFocus()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,18 +70,16 @@ internal class BackKeyPressedHelper(private val mOwnerView: View) {
|
||||
* Call this from [View.onVisibilityChanged].
|
||||
*/
|
||||
fun onVisibilityChanged() {
|
||||
setupFocus()
|
||||
setupAccessibilityFocus()
|
||||
}
|
||||
|
||||
private fun setupFocus() {
|
||||
private fun setupAccessibilityFocus() {
|
||||
if (mOnBackClickListener == null || !mOwnerView.hasWindowFocus()) return
|
||||
|
||||
mOwnerView.apply {
|
||||
isFocusable = true
|
||||
isFocusableInTouchMode = true
|
||||
when {
|
||||
isShown -> requestFocus()
|
||||
hasFocus() -> rootView?.requestFocus(View.FOCUS_UP)
|
||||
isShown -> gainAccessibilityFocus()
|
||||
isAccessibilityFocused -> rootView?.gainAccessibilityFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import androidx.core.view.children
|
||||
import androidx.core.view.doOnNextLayout
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import com.yandex.div.core.expression.suppressExpressionErrors
|
||||
import com.yandex.div.core.util.AccessibilityStateProvider
|
||||
import com.yandex.div.core.util.doOnActualLayout
|
||||
import com.yandex.div.core.util.isLayoutRtl
|
||||
import com.yandex.div.core.util.toIntSafely
|
||||
@@ -56,7 +55,6 @@ import com.yandex.div.json.expressions.equalsToConstant
|
||||
import com.yandex.div.json.expressions.isConstant
|
||||
import com.yandex.div.json.expressions.isConstantOrNull
|
||||
import com.yandex.div2.Div
|
||||
import com.yandex.div2.DivAccessibility
|
||||
import com.yandex.div2.DivAction
|
||||
import com.yandex.div2.DivAlignmentHorizontal
|
||||
import com.yandex.div2.DivAlignmentVertical
|
||||
@@ -536,7 +534,6 @@ internal fun View.applyDivActions(
|
||||
pressStartActions: List<DivAction>?,
|
||||
pressEndActions: List<DivAction>?,
|
||||
actionAnimation: DivAnimation,
|
||||
accessibility: DivAccessibility?,
|
||||
captureFocusOnAction: Expression<Boolean>,
|
||||
) {
|
||||
val actionBinder = context.divView.div2Component.actionBinder
|
||||
@@ -556,7 +553,6 @@ internal fun View.applyDivActions(
|
||||
pressStartActions,
|
||||
pressEndActions,
|
||||
actionAnimation,
|
||||
accessibility,
|
||||
captureFocusOnAction,
|
||||
)
|
||||
}
|
||||
@@ -908,24 +904,6 @@ internal fun View.gainAccessibilityFocus() {
|
||||
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED)
|
||||
}
|
||||
|
||||
internal fun sendAccessibilityEventUnchecked(
|
||||
event: Int,
|
||||
view: View?,
|
||||
accessibilityStateProvider: AccessibilityStateProvider
|
||||
) {
|
||||
view ?: return
|
||||
if (accessibilityStateProvider.isAccessibilityEnabled(view.context)) {
|
||||
view.sendAccessibilityEventUnchecked(
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
AccessibilityEvent(event)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
AccessibilityEvent.obtain(event)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun ViewGroup.bindClipChildren(
|
||||
newClipToBounds: Expression<Boolean>,
|
||||
oldClipToBounds: Expression<Boolean>?,
|
||||
|
||||
@@ -5,12 +5,10 @@ import android.view.Gravity
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.StringDef
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
|
||||
import androidx.core.view.size
|
||||
import com.yandex.div.R
|
||||
import com.yandex.div.core.Div2Logger
|
||||
import com.yandex.div.core.DivActionHandler
|
||||
@@ -19,10 +17,8 @@ import com.yandex.div.core.DivViewFacade
|
||||
import com.yandex.div.core.annotations.Mockable
|
||||
import com.yandex.div.core.dagger.DivScope
|
||||
import com.yandex.div.core.dagger.ExperimentFlag
|
||||
import com.yandex.div.core.experiments.Experiment.ACCESSIBILITY_ENABLED
|
||||
import com.yandex.div.core.experiments.Experiment.IGNORE_ACTION_MENU_ITEMS_ENABLED
|
||||
import com.yandex.div.core.experiments.Experiment.LONGTAP_ACTIONS_PASS_TO_CHILD_ENABLED
|
||||
import com.yandex.div.core.view2.AccessibilityDelegateWrapper
|
||||
import com.yandex.div.core.view2.BindingContext
|
||||
import com.yandex.div.core.view2.Div2View
|
||||
import com.yandex.div.core.view2.DivGestureListener
|
||||
@@ -44,7 +40,6 @@ import com.yandex.div.internal.util.allIsNullOrEmpty
|
||||
import com.yandex.div.internal.widget.menu.OverflowMenuWrapper
|
||||
import com.yandex.div.json.expressions.Expression
|
||||
import com.yandex.div.json.expressions.ExpressionResolver
|
||||
import com.yandex.div2.DivAccessibility
|
||||
import com.yandex.div2.DivAction
|
||||
import com.yandex.div2.DivAnimation
|
||||
import java.util.UUID
|
||||
@@ -58,7 +53,6 @@ internal class DivActionBinder @Inject constructor(
|
||||
private val divActionBeaconSender: DivActionBeaconSender,
|
||||
@ExperimentFlag(LONGTAP_ACTIONS_PASS_TO_CHILD_ENABLED) private val longtapActionsPassToChild: Boolean,
|
||||
@ExperimentFlag(IGNORE_ACTION_MENU_ITEMS_ENABLED) private val shouldIgnoreActionMenuItems: Boolean,
|
||||
@ExperimentFlag(ACCESSIBILITY_ENABLED) private val accessibilityEnabled: Boolean
|
||||
) {
|
||||
private val passToParentLongClickListener: (View) -> Boolean = { view ->
|
||||
var isLongClickHandled = false
|
||||
@@ -83,7 +77,6 @@ internal class DivActionBinder @Inject constructor(
|
||||
pressStartActions: List<DivAction>?,
|
||||
pressEndActions: List<DivAction>?,
|
||||
actionAnimation: DivAnimation,
|
||||
accessibility: DivAccessibility?,
|
||||
captureFocusOnAction: Expression<Boolean>,
|
||||
) {
|
||||
val resolver = context.expressionResolver
|
||||
@@ -99,7 +92,6 @@ internal class DivActionBinder @Inject constructor(
|
||||
pressStartActions = pressStartActions.onlyEnabled(resolver),
|
||||
pressEndActions = pressEndActions.onlyEnabled(resolver),
|
||||
actionAnimation = actionAnimation,
|
||||
accessibility = accessibility,
|
||||
captureFocusOnAction = captureFocusOnAction,
|
||||
)
|
||||
}
|
||||
@@ -123,12 +115,8 @@ internal class DivActionBinder @Inject constructor(
|
||||
pressStartActions: List<DivAction>,
|
||||
pressEndActions: List<DivAction>,
|
||||
actionAnimation: DivAnimation,
|
||||
accessibility: DivAccessibility?,
|
||||
captureFocusOnAction: Expression<Boolean>,
|
||||
) {
|
||||
val clickableState = target.isClickable
|
||||
val longClickableState = target.isLongClickable
|
||||
|
||||
val divGestureListener = DivGestureListener(
|
||||
awaitLongClick = longTapActions.isNotEmpty() || target.parentIsLongClickable()
|
||||
)
|
||||
@@ -155,54 +143,6 @@ internal class DivActionBinder @Inject constructor(
|
||||
bindHoverActions(context, target, hoverStartActions, hoverEndActions)
|
||||
|
||||
target.attachTouchListeners(animatedTouchListener, pressTouchListener)
|
||||
|
||||
if (accessibilityEnabled) {
|
||||
if (DivAccessibility.Mode.MERGE == context.divView.getPropagatedAccessibilityMode(target) &&
|
||||
context.divView.isDescendantAccessibilityMode(target)) {
|
||||
target.isClickable = clickableState
|
||||
target.isLongClickable = longClickableState
|
||||
}
|
||||
|
||||
bindAccessibilityDelegate(target, actions, longTapActions, accessibility)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindAccessibilityDelegate(
|
||||
target: View,
|
||||
actions: List<DivAction>,
|
||||
longTapActions: List<DivAction>,
|
||||
accessibility: DivAccessibility?,
|
||||
) {
|
||||
val originalDelegate = ViewCompat.getAccessibilityDelegate(target)
|
||||
|
||||
val action = { _: View?, info: AccessibilityNodeInfoCompat? ->
|
||||
if (actions.isNotEmpty()) {
|
||||
info?.addAction(AccessibilityNodeInfoCompat
|
||||
.AccessibilityActionCompat.ACTION_CLICK)
|
||||
}
|
||||
if (longTapActions.isNotEmpty()) {
|
||||
info?.addAction(AccessibilityNodeInfoCompat
|
||||
.AccessibilityActionCompat.ACTION_LONG_CLICK)
|
||||
}
|
||||
if (target is ImageView && (accessibility?.type == DivAccessibility.Type.AUTO || accessibility == null)) {
|
||||
if (longTapActions.isNotEmpty() || actions.isNotEmpty() || accessibility?.description != null) {
|
||||
info?.className = "android.widget.ImageView"
|
||||
} else {
|
||||
info?.className = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val accessibilityWrapper = if (originalDelegate is AccessibilityDelegateWrapper) {
|
||||
originalDelegate.actionsAccessibilityNodeInfo = action
|
||||
originalDelegate
|
||||
} else {
|
||||
AccessibilityDelegateWrapper(
|
||||
originalDelegate,
|
||||
actionsAccessibilityNodeInfo = action)
|
||||
}
|
||||
|
||||
ViewCompat.setAccessibilityDelegate(target, accessibilityWrapper)
|
||||
}
|
||||
|
||||
private fun bindTapActions(
|
||||
@@ -602,7 +542,7 @@ internal class DivActionBinder @Inject constructor(
|
||||
val expressionResolver = context.expressionResolver
|
||||
val menu = popupMenu.menu
|
||||
for (itemData in items) {
|
||||
val itemPosition = menu.size()
|
||||
val itemPosition = menu.size
|
||||
val menuItem = menu.add(itemData.text.evaluate(expressionResolver))
|
||||
menuItem.setOnMenuItemClickListener {
|
||||
var actionsHandled = false
|
||||
|
||||
@@ -7,7 +7,6 @@ import android.view.View
|
||||
import android.view.ViewGroup.LayoutParams
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.transition.Transition
|
||||
import androidx.transition.TransitionManager
|
||||
import androidx.transition.Visibility
|
||||
@@ -37,10 +36,11 @@ import com.yandex.div.json.expressions.equalsToConstant
|
||||
import com.yandex.div.json.expressions.isConstant
|
||||
import com.yandex.div.json.expressions.isConstantOrNull
|
||||
import com.yandex.div2.Div
|
||||
import com.yandex.div2.DivAccessibility
|
||||
import com.yandex.div2.DivAction
|
||||
import com.yandex.div2.DivBase
|
||||
import com.yandex.div2.DivInput
|
||||
import com.yandex.div2.DivSize
|
||||
import com.yandex.div2.DivSwitch
|
||||
import com.yandex.div2.DivVisibility
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -73,7 +73,7 @@ internal class DivBaseBinder @Inject constructor(
|
||||
bindId(divView, div, oldDiv)
|
||||
bindLayoutParams(div, oldDiv, resolver, subscriber)
|
||||
bindLayoutProvider(bindingContext, div, oldDiv)
|
||||
bindAccessibility(divView, div, oldDiv, resolver, subscriber)
|
||||
bindAccessibility(div, oldDiv, resolver, subscriber)
|
||||
bindAlpha(div, oldDiv, resolver, subscriber)
|
||||
|
||||
bindBackground(bindingContext, div, oldDiv, subscriber)
|
||||
@@ -87,10 +87,7 @@ internal class DivBaseBinder @Inject constructor(
|
||||
|
||||
div.tooltips?.let { tooltipController.mapTooltip(this, it) }
|
||||
|
||||
// DivAccessibilityBinder is responsible for focus setup, so changing isFocusable only if binder is disabled
|
||||
if (!divAccessibilityBinder.enabled) {
|
||||
applyFocusableState(div)
|
||||
}
|
||||
applyFocusableState(div)
|
||||
}
|
||||
|
||||
//region Id
|
||||
@@ -379,135 +376,11 @@ internal class DivBaseBinder @Inject constructor(
|
||||
//region Accessibility
|
||||
|
||||
private fun View.bindAccessibility(
|
||||
divView: Div2View,
|
||||
newDiv: DivBase,
|
||||
oldDiv: DivBase?,
|
||||
resolver: ExpressionResolver,
|
||||
subscriber: ExpressionSubscriber
|
||||
) {
|
||||
if (newDiv.accessibility == null && oldDiv?.accessibility == null) {
|
||||
// Shortcut for empty accessibility binding
|
||||
applyAccessibilityMode(divView, newDiv, mode = null)
|
||||
divAccessibilityBinder.bindType(this, newDiv, DivAccessibility.Type.AUTO, resolver)
|
||||
return
|
||||
}
|
||||
|
||||
bindAccessibilityType(newDiv, oldDiv, resolver)
|
||||
bindAccessibilityDescriptionAndHint(newDiv, oldDiv, resolver, subscriber)
|
||||
bindAccessibilityMode(divView, newDiv, resolver, subscriber)
|
||||
bindAccessibilityStateDescription(newDiv, oldDiv, resolver, subscriber)
|
||||
//TODO: bind 'muteAfterAction' property
|
||||
}
|
||||
|
||||
private fun View.bindAccessibilityType(
|
||||
newDiv: DivBase,
|
||||
oldDiv: DivBase?,
|
||||
resolver: ExpressionResolver,
|
||||
) {
|
||||
if (oldDiv != null && newDiv.accessibility?.type == oldDiv.accessibility?.type) {
|
||||
return
|
||||
}
|
||||
|
||||
divAccessibilityBinder.bindType(this, newDiv, newDiv.accessibility?.type
|
||||
?: DivAccessibility.Type.AUTO, resolver)
|
||||
}
|
||||
|
||||
private fun View.bindAccessibilityDescriptionAndHint(
|
||||
newDiv: DivBase,
|
||||
oldDiv: DivBase?,
|
||||
resolver: ExpressionResolver,
|
||||
subscriber: ExpressionSubscriber
|
||||
) {
|
||||
if (newDiv.accessibility?.description.equalsToConstant(oldDiv?.accessibility?.description)
|
||||
&& newDiv.accessibility?.hint.equalsToConstant(oldDiv?.accessibility?.hint)) {
|
||||
return
|
||||
}
|
||||
|
||||
applyAccessibilityDescriptionAndHint(
|
||||
newDiv.accessibility?.description?.evaluate(resolver),
|
||||
newDiv.accessibility?.hint?.evaluate(resolver)
|
||||
)
|
||||
|
||||
if (newDiv.accessibility?.description.isConstantOrNull()
|
||||
&& newDiv.accessibility?.hint.isConstantOrNull()) {
|
||||
return
|
||||
}
|
||||
|
||||
val callback = { _: Any ->
|
||||
applyAccessibilityDescriptionAndHint(
|
||||
newDiv.accessibility?.description?.evaluate(resolver),
|
||||
newDiv.accessibility?.hint?.evaluate(resolver)
|
||||
)
|
||||
}
|
||||
subscriber.addSubscription(newDiv.accessibility?.description?.observe(resolver, callback))
|
||||
subscriber.addSubscription(newDiv.accessibility?.hint?.observe(resolver, callback))
|
||||
}
|
||||
|
||||
private fun View.applyAccessibilityDescriptionAndHint(contentDescription: String?, hint: String?) {
|
||||
this.contentDescription = when {
|
||||
contentDescription == null -> hint
|
||||
hint == null -> contentDescription
|
||||
else -> "$contentDescription\n$hint"
|
||||
}
|
||||
}
|
||||
|
||||
private fun View.bindAccessibilityMode(
|
||||
divView: Div2View,
|
||||
newDiv: DivBase,
|
||||
resolver: ExpressionResolver,
|
||||
subscriber: ExpressionSubscriber
|
||||
) {
|
||||
// We can't compare accessibility mode with previous one due to actual value depends on parent mode
|
||||
// and should be recalculated.
|
||||
|
||||
applyAccessibilityMode(divView, newDiv, newDiv.accessibility?.mode?.evaluate(resolver))
|
||||
|
||||
if (newDiv.accessibility?.mode.isConstantOrNull()) {
|
||||
return
|
||||
}
|
||||
|
||||
subscriber.addSubscription(
|
||||
newDiv.accessibility?.mode?.observe(resolver) { mode ->
|
||||
applyAccessibilityMode(divView, newDiv, mode)
|
||||
|
||||
val type = newDiv.accessibility?.type ?: DivAccessibility.Type.AUTO
|
||||
if (type == DivAccessibility.Type.AUTO) {
|
||||
divAccessibilityBinder.bindType(this, newDiv, type, resolver)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun View.applyAccessibilityMode(divView: Div2View, base: DivBase, mode: DivAccessibility.Mode?) {
|
||||
divAccessibilityBinder.bindAccessibilityMode(this, divView, mode, base)
|
||||
}
|
||||
|
||||
private fun View.bindAccessibilityStateDescription(
|
||||
newDiv: DivBase,
|
||||
oldDiv: DivBase?,
|
||||
resolver: ExpressionResolver,
|
||||
subscriber: ExpressionSubscriber
|
||||
) {
|
||||
if (newDiv.accessibility?.stateDescription.equalsToConstant(oldDiv?.accessibility?.stateDescription)) {
|
||||
return
|
||||
}
|
||||
|
||||
applyAccessibilityStateDescription(newDiv.accessibility?.stateDescription?.evaluate(resolver))
|
||||
|
||||
if (newDiv.accessibility?.stateDescription.isConstantOrNull()) {
|
||||
return
|
||||
}
|
||||
|
||||
subscriber.addSubscription(
|
||||
newDiv.accessibility?.stateDescription?.observe(resolver) { stateDescription ->
|
||||
applyAccessibilityStateDescription(stateDescription)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun View.applyAccessibilityStateDescription(stateDescription: String?) {
|
||||
ViewCompat.setStateDescription(this, stateDescription)
|
||||
}
|
||||
) = divAccessibilityBinder.bind(this, newDiv, oldDiv, resolver, subscriber)
|
||||
|
||||
//endregion
|
||||
|
||||
@@ -765,6 +638,7 @@ internal class DivBaseBinder @Inject constructor(
|
||||
//endregion
|
||||
|
||||
private fun View.applyFocusableState(div: DivBase) {
|
||||
if (div is DivInput || div is DivSwitch) return
|
||||
isFocusable = div.focus != null
|
||||
}
|
||||
|
||||
|
||||
@@ -110,7 +110,6 @@ internal class DivContainerBinder @Inject constructor(
|
||||
div.pressStartActions,
|
||||
div.pressEndActions,
|
||||
div.actionAnimation,
|
||||
div.accessibility,
|
||||
div.captureFocusOnAction,
|
||||
)
|
||||
|
||||
|
||||
@@ -59,7 +59,6 @@ internal class DivGifImageBinder @Inject constructor(
|
||||
div.pressStartActions,
|
||||
div.pressEndActions,
|
||||
div.actionAnimation,
|
||||
div.accessibility,
|
||||
div.captureFocusOnAction,
|
||||
)
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@ internal class DivGridBinder @Inject constructor(
|
||||
div.pressStartActions,
|
||||
div.pressEndActions,
|
||||
div.actionAnimation,
|
||||
div.accessibility,
|
||||
div.captureFocusOnAction,
|
||||
)
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ internal class DivImageBinder @Inject constructor(
|
||||
div.pressStartActions,
|
||||
div.pressEndActions,
|
||||
div.actionAnimation,
|
||||
div.accessibility,
|
||||
div.captureFocusOnAction,
|
||||
)
|
||||
|
||||
|
||||
@@ -74,8 +74,6 @@ internal class DivInputBinder @Inject constructor(
|
||||
path: DivStatePath
|
||||
) {
|
||||
val expressionResolver = bindingContext.expressionResolver
|
||||
isFocusable = true
|
||||
isFocusableInTouchMode = true
|
||||
textAlignment = TextView.TEXT_ALIGNMENT_VIEW_START
|
||||
accessibilityEnabled = accessibilityStateProvider.isAccessibilityEnabled(context)
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ internal class DivSeparatorBinder @Inject constructor(
|
||||
div.pressStartActions,
|
||||
div.pressEndActions,
|
||||
div.actionAnimation,
|
||||
div.accessibility,
|
||||
div.captureFocusOnAction,
|
||||
)
|
||||
|
||||
|
||||
@@ -80,7 +80,6 @@ internal class DivTextBinder @Inject constructor(
|
||||
div.pressStartActions,
|
||||
div.pressEndActions,
|
||||
div.actionAnimation,
|
||||
div.accessibility,
|
||||
div.captureFocusOnAction,
|
||||
)
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ internal class DivPagerAdapter(
|
||||
private val pageTranslations: SparseArray<Float>,
|
||||
private val viewCreator: DivViewCreator,
|
||||
path: DivStatePath,
|
||||
private val accessibilityEnabled: Boolean,
|
||||
private val pagerView: DivPagerView,
|
||||
) : DivCollectionAdapter<DivPagerViewHolder>(bindingContext, path, items) {
|
||||
|
||||
@@ -65,7 +64,6 @@ internal class DivPagerAdapter(
|
||||
view,
|
||||
divBinder,
|
||||
viewCreator,
|
||||
accessibilityEnabled,
|
||||
{ isHorizontal },
|
||||
{ crossAxisAlignment },
|
||||
)
|
||||
|
||||
@@ -91,7 +91,6 @@ internal class DivPagerBinder @Inject constructor(
|
||||
pageTranslations,
|
||||
viewCreator,
|
||||
path,
|
||||
a11yEnabled,
|
||||
this
|
||||
)
|
||||
viewPager.adapter = adapter
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.yandex.div.core.view2.divs.pager
|
||||
|
||||
import android.view.Gravity
|
||||
import com.yandex.div.R
|
||||
import com.yandex.div.core.state.DivStatePath
|
||||
import com.yandex.div.core.util.doOnEveryDetach
|
||||
import com.yandex.div.core.view2.BindingContext
|
||||
@@ -22,7 +21,6 @@ internal class DivPagerViewHolder(
|
||||
private val pageLayout: DivPagerPageLayout,
|
||||
divBinder: DivBinder,
|
||||
viewCreator: DivViewCreator,
|
||||
private val accessibilityEnabled: Boolean,
|
||||
private val isHorizontal: () -> Boolean,
|
||||
private val crossAxisAlignment: () -> ItemAlignment,
|
||||
) : DivCollectionViewHolder(pageLayout, parentContext, divBinder, viewCreator) {
|
||||
@@ -40,10 +38,6 @@ internal class DivPagerViewHolder(
|
||||
|
||||
(pageLayout.child?.layoutParams as? DivLayoutParams)
|
||||
?.setCrossAxisAlignment(div.value(), bindingContext.expressionResolver)
|
||||
|
||||
if (accessibilityEnabled) {
|
||||
pageLayout.setTag(R.id.div_pager_item_clip_id, position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun DivLayoutParams.setCrossAxisAlignment(div: DivBase, resolver: ExpressionResolver) {
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package com.yandex.div.core.view2.divs.widgets
|
||||
|
||||
import com.yandex.div.core.Disposable
|
||||
import com.yandex.div.internal.core.DivItemBuilderResult
|
||||
|
||||
internal interface DivCollectionHolder {
|
||||
var items: List<DivItemBuilderResult>?
|
||||
var accessibilityObserver: Disposable?
|
||||
}
|
||||
|
||||
internal class DivCollectionHolderMixin : DivCollectionHolder {
|
||||
override var items: List<DivItemBuilderResult>? = null
|
||||
override var accessibilityObserver: Disposable? = null
|
||||
}
|
||||
|
||||
@@ -50,14 +50,13 @@ internal class DivInputView @JvmOverloads constructor(
|
||||
|
||||
private var _hint: String? = null
|
||||
|
||||
private var _isFocusable = true
|
||||
|
||||
private var editorActionListener: OnEditorActionListener? = null
|
||||
|
||||
var enabled = true
|
||||
internal set(value) {
|
||||
field = value
|
||||
isFocusable = _isFocusable
|
||||
isFocusable = value
|
||||
isFocusableInTouchMode = value
|
||||
}
|
||||
|
||||
internal var accessibilityEnabled: Boolean = false
|
||||
@@ -96,13 +95,6 @@ internal class DivInputView @JvmOverloads constructor(
|
||||
drawBorderClipped(canvas, scrollX, scrollY) { super.draw(it) }
|
||||
}
|
||||
|
||||
override fun setFocusable(focusable: Boolean) {
|
||||
_isFocusable = focusable
|
||||
val isFocusable = _isFocusable && enabled
|
||||
super.setFocusable(isFocusable)
|
||||
isFocusableInTouchMode = isFocusable
|
||||
}
|
||||
|
||||
override fun setOnEditorActionListener(l: OnEditorActionListener?) {
|
||||
super.setOnEditorActionListener(l)
|
||||
editorActionListener = l
|
||||
|
||||
@@ -9,12 +9,13 @@ import android.view.ViewGroup
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import android.view.accessibility.AccessibilityNodeInfo
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.yandex.div.R
|
||||
import com.yandex.div.core.annotations.Mockable
|
||||
import com.yandex.div.core.view2.divs.drawShadow
|
||||
import com.yandex.div.core.view2.divs.pager.PagerSelectedActionsDispatcher
|
||||
import com.yandex.div.core.widget.DivViewWrapper
|
||||
import com.yandex.div.core.widget.ViewPager2Wrapper
|
||||
import com.yandex.div.internal.widget.OnInterceptTouchEventListener
|
||||
import com.yandex.div.internal.widget.OnInterceptTouchEventListenerHost
|
||||
@@ -82,25 +83,29 @@ internal class DivPagerView @JvmOverloads constructor(
|
||||
|
||||
private val accessibilityDelegate by lazy(LazyThreadSafetyMode.NONE) {
|
||||
val recycler = getRecyclerView() ?: return@lazy null
|
||||
recycler.descendantFocusability = ViewGroup.FOCUS_AFTER_DESCENDANTS
|
||||
|
||||
object : RecyclerViewAccessibilityDelegate(recycler) {
|
||||
override fun onRequestSendAccessibilityEvent(
|
||||
host: ViewGroup, child: View, event: AccessibilityEvent
|
||||
host: ViewGroup,
|
||||
child: View,
|
||||
event: AccessibilityEvent
|
||||
): Boolean {
|
||||
if (event.eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
|
||||
getFocusedChildPos(child)?.let { pos ->
|
||||
if (currentItem != pos) {
|
||||
recycler.performAccessibilityAction(
|
||||
if (pos > currentItem) AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
|
||||
else AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD,
|
||||
null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
performActionIfNeeded(child, event)
|
||||
return super.onRequestSendAccessibilityEvent(host, child, event)
|
||||
}
|
||||
|
||||
private fun performActionIfNeeded(child: View, event: AccessibilityEvent) {
|
||||
if (event.eventType != AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) return
|
||||
val pos = getWrapperFor(child)?.let { recycler.getChildAdapterPosition(it) } ?: return
|
||||
if (currentItem == pos || pos == RecyclerView.NO_POSITION) return
|
||||
|
||||
val action = if (pos > currentItem) {
|
||||
AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
|
||||
} else {
|
||||
AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD
|
||||
}
|
||||
recycler.performAccessibilityAction(action, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,11 +161,11 @@ internal class DivPagerView @JvmOverloads constructor(
|
||||
return wrappedChild.getChildAt(0)
|
||||
}
|
||||
|
||||
private fun getFocusedChildPos(child: View): Int? {
|
||||
var child = child
|
||||
while (child != this) {
|
||||
(child.getTag(R.id.div_pager_item_clip_id) as? Int)?.let { return it }
|
||||
child = child.parent as? View ?: return null
|
||||
private fun getWrapperFor(child: View): View? {
|
||||
var parent = child
|
||||
while (parent != getRecyclerView()) {
|
||||
if (parent is DivViewWrapper) return parent
|
||||
parent = parent.parent as? View ?: return null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package com.yandex.div.core.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.LayoutParams
|
||||
import com.yandex.div.core.util.makeFocusable
|
||||
import androidx.core.view.isEmpty
|
||||
import com.yandex.div.core.view2.BindingContext
|
||||
import com.yandex.div.core.view2.divs.widgets.DivBorderDrawer
|
||||
import com.yandex.div.core.view2.divs.widgets.DivBorderSupports
|
||||
@@ -26,15 +27,17 @@ internal open class DivViewWrapper @JvmOverloads constructor(
|
||||
): FrameContainerLayout(context, attrs, defStyleAttr), DivBorderSupports, TransientView by TransientViewMixin() {
|
||||
|
||||
val child: View?
|
||||
get() = if (childCount == 0) null else getChildAt(0)
|
||||
get() = if (isEmpty()) null else getChildAt(0)
|
||||
|
||||
init {
|
||||
makeFocusable()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
defaultFocusHighlightEnabled = false
|
||||
}
|
||||
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
|
||||
}
|
||||
|
||||
override fun addView(child: View?, index: Int, params: LayoutParams?) {
|
||||
require(childCount == 0) { "ViewWrapper can host only one child view" }
|
||||
require(isEmpty()) { "ViewWrapper can host only one child view" }
|
||||
super.addView(child, 0, params)
|
||||
}
|
||||
|
||||
|
||||
@@ -111,4 +111,6 @@ internal open class ViewPager2Wrapper @JvmOverloads constructor(
|
||||
}
|
||||
return maxValue
|
||||
}
|
||||
|
||||
override fun getAccessibilityClassName() = "androidx.viewpager.widget.ViewPager"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.yandex.div.internal.widget
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.text.TextUtils
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
@@ -14,14 +13,15 @@ import android.widget.AbsListView.CHOICE_MODE_SINGLE
|
||||
import android.widget.BaseAdapter
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.widget.ListPopupWindow
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import com.yandex.div.core.annotations.Mockable
|
||||
import com.yandex.div.core.view2.divs.dpToPx
|
||||
import com.yandex.div.core.view2.divs.clearFocusOnClick
|
||||
import com.yandex.div.core.view2.divs.dpToPx
|
||||
import com.yandex.div.core.view2.reuse.InputFocusTracker
|
||||
|
||||
private const val POPUP_ITEM_HEIGHT = 48
|
||||
|
||||
internal open class SelectView constructor(context: Context) : EllipsizedTextView(context) {
|
||||
internal open class SelectView(context: Context) : EllipsizedTextView(context) {
|
||||
init {
|
||||
this.setOnClickListener {
|
||||
focusTracker?.let { tracker -> clearFocusOnClick(tracker) }
|
||||
@@ -45,7 +45,7 @@ internal open class SelectView constructor(context: Context) : EllipsizedTextVie
|
||||
}
|
||||
|
||||
setOverlapAnchor(true)
|
||||
setBackgroundDrawable(ColorDrawable(Color.WHITE))
|
||||
setBackgroundDrawable(Color.WHITE.toDrawable())
|
||||
setAdapter(adapter)
|
||||
}
|
||||
|
||||
@@ -87,6 +87,8 @@ internal open class SelectView constructor(context: Context) : EllipsizedTextVie
|
||||
info.text = text
|
||||
}
|
||||
|
||||
override fun getAccessibilityClassName() = "android.widget.Spinner"
|
||||
|
||||
@Mockable
|
||||
private class PopupWindow @JvmOverloads constructor(
|
||||
private val context: Context,
|
||||
|
||||
@@ -28,7 +28,7 @@ internal open class TextViewWithAccessibleSpans(
|
||||
|
||||
init {
|
||||
AccessibilityStateProvider.evaluateTouchModeEnabled(context)
|
||||
if (AccessibilityStateProvider.touchModeEnabled == true) {
|
||||
if (AccessibilityStateProvider.touchExplorationEnabled == true) {
|
||||
spanHelper = SpanHelper()
|
||||
ViewCompat.setAccessibilityDelegate(this, spanHelper)
|
||||
accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_POLITE
|
||||
@@ -38,7 +38,7 @@ internal open class TextViewWithAccessibleSpans(
|
||||
}
|
||||
|
||||
internal fun addImageSpan(span: ImageSpan) {
|
||||
if (AccessibilityStateProvider.touchModeEnabled == true) {
|
||||
if (AccessibilityStateProvider.touchExplorationEnabled == true) {
|
||||
imageSpans.add(span)
|
||||
if (span.accessibility?.contentDescription != null || span.accessibility?.onClickAction != null) {
|
||||
accessibleImageSpans.add(span)
|
||||
@@ -56,7 +56,7 @@ internal open class TextViewWithAccessibleSpans(
|
||||
}
|
||||
|
||||
private fun evaluateAndSetContentDescription() {
|
||||
if (AccessibilityStateProvider.touchModeEnabled != true) {
|
||||
if (AccessibilityStateProvider.touchExplorationEnabled != true) {
|
||||
super.setContentDescription(_contentDescription)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.yandex.div.internal.widget.slider
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import android.view.accessibility.AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT
|
||||
import android.widget.SeekBar
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
|
||||
import androidx.customview.widget.ExploreByTouchHelper
|
||||
import com.yandex.div.R
|
||||
import com.yandex.div.internal.widget.slider.SliderView.Companion.boundsHeight
|
||||
import com.yandex.div.internal.widget.slider.SliderView.Companion.boundsWidth
|
||||
import com.yandex.div.internal.widget.slider.SliderView.Thumb
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
private const val THUMB_VIRTUAL_VIEW_ID = 0
|
||||
private const val SECONDARY_THUMB_VIRTUAL_VIEW_ID = 1
|
||||
|
||||
/**
|
||||
* Provides info about virtual view hierarchy for accessibility services.
|
||||
*/
|
||||
internal class SliderAccessibilityHelper(private val slider: SliderView) : ExploreByTouchHelper(slider) {
|
||||
|
||||
private val bounds = Rect()
|
||||
private val step get() = max(((slider.maxValue - slider.minValue) * 0.05).roundToInt(), 1)
|
||||
|
||||
init {
|
||||
ViewCompat.setAccessibilityDelegate(slider, this)
|
||||
slider.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_POLITE
|
||||
}
|
||||
|
||||
override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) = Unit
|
||||
|
||||
override fun getVirtualViewAt(x: Float, y: Float): Int {
|
||||
if (x < slider.paddingLeft) return THUMB_VIRTUAL_VIEW_ID
|
||||
|
||||
return when (slider.getClosestThumb(x.toInt())) {
|
||||
Thumb.THUMB -> THUMB_VIRTUAL_VIEW_ID
|
||||
Thumb.THUMB_SECONDARY -> SECONDARY_THUMB_VIRTUAL_VIEW_ID
|
||||
}
|
||||
}
|
||||
|
||||
override fun getVisibleVirtualViews(virtualViewIds: MutableList<Int>) {
|
||||
virtualViewIds.add(THUMB_VIRTUAL_VIEW_ID)
|
||||
slider.thumbSecondaryValue?.let {
|
||||
virtualViewIds.add(SECONDARY_THUMB_VIRTUAL_VIEW_ID)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPopulateNodeForVirtualView(
|
||||
virtualViewId: Int,
|
||||
node: AccessibilityNodeInfoCompat
|
||||
) {
|
||||
node.apply {
|
||||
className = SeekBar::class.java.name
|
||||
rangeInfo = AccessibilityNodeInfoCompat.RangeInfoCompat.obtain(
|
||||
RANGE_TYPE_INT,
|
||||
slider.minValue,
|
||||
slider.maxValue,
|
||||
virtualViewId.toThumbValue()
|
||||
)
|
||||
|
||||
val description = StringBuilder()
|
||||
slider.contentDescription?.let { description.append(it).append(",") }
|
||||
description.append(startOrEndDescription(virtualViewId))
|
||||
contentDescription = description.toString()
|
||||
|
||||
addAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_FORWARD)
|
||||
addAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_BACKWARD)
|
||||
|
||||
updateBounds(virtualViewId)
|
||||
setBoundsInParent(bounds)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPerformActionForVirtualView(virtualViewId: Int, action: Int, arguments: Bundle?): Boolean {
|
||||
val value = when (action) {
|
||||
android.R.id.accessibilityActionSetProgress -> {
|
||||
if (arguments?.containsKey(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_PROGRESS_VALUE) != true) {
|
||||
return false
|
||||
}
|
||||
arguments.getFloat(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_PROGRESS_VALUE)
|
||||
}
|
||||
|
||||
AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD -> virtualViewId.toThumbValue() + step
|
||||
AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD -> virtualViewId.toThumbValue() - step
|
||||
else -> return false
|
||||
}
|
||||
setThumbValue(virtualViewId, value)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun startOrEndDescription(virtualViewId: Int): String {
|
||||
return when {
|
||||
slider.thumbSecondaryValue == null -> ""
|
||||
virtualViewId == THUMB_VIRTUAL_VIEW_ID -> slider.context.getString(R.string.div_slider_range_start)
|
||||
virtualViewId == SECONDARY_THUMB_VIRTUAL_VIEW_ID -> slider.context.getString(R.string.div_slider_range_end)
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateBounds(index: Int) {
|
||||
val width: Int
|
||||
val height: Int
|
||||
when (index) {
|
||||
SECONDARY_THUMB_VIRTUAL_VIEW_ID -> {
|
||||
width = slider.thumbSecondaryDrawable.boundsWidth
|
||||
height = slider.thumbSecondaryDrawable.boundsHeight
|
||||
}
|
||||
else -> {
|
||||
width = slider.thumbDrawable.boundsWidth
|
||||
height = slider.thumbDrawable.boundsHeight
|
||||
}
|
||||
}
|
||||
|
||||
val position = slider.getPositionInView(index.toThumbValue())
|
||||
bounds.apply {
|
||||
left = position
|
||||
right = position + width
|
||||
top = (slider.height + slider.paddingTop - slider.paddingBottom - height) / 2
|
||||
bottom = (slider.height + slider.paddingTop - slider.paddingBottom + height) / 2
|
||||
}
|
||||
}
|
||||
|
||||
private fun setThumbValue(virtualViewId: Int, value: Float) {
|
||||
slider.setValueToAccessibilityThumb(virtualViewId.toThumb(), value)
|
||||
sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_SELECTED)
|
||||
invalidateVirtualView(virtualViewId)
|
||||
}
|
||||
|
||||
private fun Int.toThumb(): Thumb {
|
||||
return when {
|
||||
this == THUMB_VIRTUAL_VIEW_ID -> Thumb.THUMB
|
||||
slider.thumbSecondaryValue != null -> Thumb.THUMB_SECONDARY
|
||||
else -> Thumb.THUMB
|
||||
}
|
||||
}
|
||||
|
||||
private fun Int.toThumbValue(): Float {
|
||||
return if (this == THUMB_VIRTUAL_VIEW_ID) {
|
||||
slider.thumbValue
|
||||
} else {
|
||||
slider.thumbSecondaryValue ?: slider.thumbValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.yandex.div.internal.widget.slider
|
||||
|
||||
import android.animation.Animator
|
||||
|
||||
internal class SliderThumbAnimatorListener(
|
||||
private val onAnimationEnd: (Boolean) -> Unit
|
||||
) : Animator.AnimatorListener {
|
||||
|
||||
private var hasCanceled = false
|
||||
|
||||
override fun onAnimationEnd(animation: Animator) = onAnimationEnd(hasCanceled)
|
||||
|
||||
override fun onAnimationCancel(animation: Animator) {
|
||||
hasCanceled = true
|
||||
}
|
||||
|
||||
override fun onAnimationStart(animation: Animator) {
|
||||
hasCanceled = false
|
||||
}
|
||||
|
||||
override fun onAnimationRepeat(animation: Animator) = Unit
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.yandex.div.internal.widget.slider
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.ValueAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
@@ -8,21 +7,13 @@ import android.graphics.Canvas
|
||||
import android.graphics.Rect
|
||||
import android.graphics.Region
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.util.AttributeSet
|
||||
import android.view.KeyEvent
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewConfiguration
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import android.view.accessibility.AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import android.widget.SeekBar
|
||||
import androidx.annotation.Px
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
|
||||
import androidx.customview.widget.ExploreByTouchHelper
|
||||
import com.yandex.div.R
|
||||
import com.yandex.div.core.ObserverList
|
||||
import com.yandex.div.core.util.isLayoutRtl
|
||||
import com.yandex.div.internal.widget.slider.shapes.TextDrawable
|
||||
@@ -40,8 +31,6 @@ private const val UNSET_VALUE = -1
|
||||
private const val DEFAULT_ANIMATION_DURATION = 300L
|
||||
private const val DEFAULT_ANIMATION_ENABLED = true
|
||||
private const val DEFAULT_INTERCEPTION_ANGLE = 45f
|
||||
private const val THUMB_VIRTUAL_VIEW_ID = 0
|
||||
private const val SECONDARY_THUMB_VIRTUAL_VIEW_ID = 1
|
||||
|
||||
open class SliderView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
@@ -70,56 +59,25 @@ open class SliderView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
private var sliderAnimator: ValueAnimator? = null
|
||||
private var prevThumbValue: Float = DEFAULT_MIN_VALUE
|
||||
private var sliderSecondaryAnimator: ValueAnimator? = null
|
||||
private var prevThumbSecondaryValue: Float? = null
|
||||
|
||||
private val animatorListener = object : Animator.AnimatorListener {
|
||||
|
||||
var prevThumbValue: Float = DEFAULT_MIN_VALUE
|
||||
|
||||
private var hasCanceled = false
|
||||
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
sliderAnimator = null
|
||||
if (!hasCanceled) {
|
||||
notifyThumbChangedListeners(prevThumbValue, thumbValue)
|
||||
}
|
||||
private val animatorListener = SliderThumbAnimatorListener { hasCanceled ->
|
||||
sliderAnimator = null
|
||||
if (!hasCanceled) {
|
||||
notifyThumbChangedListeners(prevThumbValue, thumbValue)
|
||||
}
|
||||
|
||||
override fun onAnimationCancel(animation: Animator) {
|
||||
hasCanceled = true
|
||||
}
|
||||
|
||||
override fun onAnimationStart(animation: Animator) {
|
||||
hasCanceled = false
|
||||
}
|
||||
|
||||
override fun onAnimationRepeat(animation: Animator) = Unit
|
||||
}
|
||||
private val animatorSecondaryListener = object : Animator.AnimatorListener {
|
||||
|
||||
var prevThumbSecondaryValue: Float? = null
|
||||
|
||||
private var hasCanceled = false
|
||||
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
sliderSecondaryAnimator = null
|
||||
if (!hasCanceled) {
|
||||
notifyThumbSecondaryChangedListeners(
|
||||
prevThumbSecondaryValue,
|
||||
thumbSecondaryValue
|
||||
)
|
||||
}
|
||||
private val animatorSecondaryListener = SliderThumbAnimatorListener { hasCanceled ->
|
||||
sliderSecondaryAnimator = null
|
||||
if (!hasCanceled) {
|
||||
notifyThumbSecondaryChangedListeners(
|
||||
prevThumbSecondaryValue,
|
||||
thumbSecondaryValue
|
||||
)
|
||||
}
|
||||
|
||||
override fun onAnimationCancel(animation: Animator) {
|
||||
hasCanceled = true
|
||||
}
|
||||
|
||||
override fun onAnimationStart(animation: Animator) {
|
||||
hasCanceled = false
|
||||
}
|
||||
|
||||
override fun onAnimationRepeat(animation: Animator) = Unit
|
||||
}
|
||||
|
||||
val ranges = mutableListOf<Range>()
|
||||
@@ -138,7 +96,7 @@ open class SliderView @JvmOverloads constructor(
|
||||
* This allows the basic animation effects (alpha, scale, translate, rotate) to be accelerated,
|
||||
* decelerated, repeated, etc.
|
||||
*/
|
||||
var animationInterpolator = AccelerateDecelerateInterpolator()
|
||||
private var animationInterpolator = AccelerateDecelerateInterpolator()
|
||||
|
||||
/**
|
||||
* Animation enabled flag.
|
||||
@@ -240,7 +198,7 @@ open class SliderView @JvmOverloads constructor(
|
||||
if (thumbValue == newValue) return
|
||||
if (animated && animationEnabled) {
|
||||
if (sliderAnimator == null) {
|
||||
animatorListener.prevThumbValue = thumbValue
|
||||
prevThumbValue = thumbValue
|
||||
}
|
||||
sliderAnimator?.cancel()
|
||||
sliderAnimator = ValueAnimator.ofFloat(thumbValue, newValue).apply {
|
||||
@@ -257,9 +215,9 @@ open class SliderView @JvmOverloads constructor(
|
||||
sliderAnimator?.cancel()
|
||||
}
|
||||
if (forced || sliderAnimator == null) {
|
||||
animatorListener.prevThumbValue = thumbValue
|
||||
prevThumbValue = thumbValue
|
||||
thumbValue = newValue
|
||||
notifyThumbChangedListeners(animatorListener.prevThumbValue, thumbValue)
|
||||
notifyThumbChangedListeners(prevThumbValue, thumbValue)
|
||||
}
|
||||
}
|
||||
invalidate()
|
||||
@@ -277,7 +235,7 @@ open class SliderView @JvmOverloads constructor(
|
||||
|
||||
/**
|
||||
* The appearance of thumb text.
|
||||
* Returns [null] if doesn't exist.
|
||||
* Returns null if doesn't exist.
|
||||
*/
|
||||
var thumbTextDrawable: TextDrawable? = null
|
||||
set(drawable) {
|
||||
@@ -287,21 +245,16 @@ open class SliderView @JvmOverloads constructor(
|
||||
|
||||
/**
|
||||
* The value of thumb secondary. Should be in range from [minValue] to [maxValue].
|
||||
* Returns [null] if doesn't exist.
|
||||
* Returns null if doesn't exist.
|
||||
*/
|
||||
var thumbSecondaryValue: Float? = null
|
||||
private set
|
||||
|
||||
private val a11yHelper: A11yHelper = A11yHelper(this)
|
||||
|
||||
init {
|
||||
ViewCompat.setAccessibilityDelegate(this, a11yHelper)
|
||||
accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_POLITE
|
||||
}
|
||||
private val a11yHelper = SliderAccessibilityHelper(this)
|
||||
|
||||
/**
|
||||
* Set the value of thumb secondary.
|
||||
* @param value should be in range from [minValue] to [maxValue] or be [null]
|
||||
* @param value should be in range from [minValue] to [maxValue] or be null
|
||||
* @param animated change value with animation
|
||||
*/
|
||||
fun setThumbSecondaryValue(value: Float?, animated: Boolean = animationEnabled) {
|
||||
@@ -310,7 +263,7 @@ open class SliderView @JvmOverloads constructor(
|
||||
|
||||
/**
|
||||
* Tries to set the value of thumb secondary.
|
||||
* @param value should be in range from [minValue] to [maxValue] or be [null]
|
||||
* @param value should be in range from [minValue] to [maxValue] or be null
|
||||
* @param animated change value with animation
|
||||
* @param forced if [animated] is false and [forced] is true,
|
||||
* then value will be set regardless of the running animation
|
||||
@@ -324,7 +277,7 @@ open class SliderView @JvmOverloads constructor(
|
||||
if (thumbSecondaryValue == newValue) return
|
||||
if (animated && animationEnabled && thumbSecondaryValue != null && newValue != null) {
|
||||
if (sliderSecondaryAnimator == null) {
|
||||
animatorSecondaryListener.prevThumbSecondaryValue = thumbSecondaryValue
|
||||
prevThumbSecondaryValue = thumbSecondaryValue
|
||||
}
|
||||
sliderSecondaryAnimator?.cancel()
|
||||
sliderSecondaryAnimator = ValueAnimator.ofFloat(thumbSecondaryValue!!, newValue).apply {
|
||||
@@ -341,10 +294,10 @@ open class SliderView @JvmOverloads constructor(
|
||||
sliderSecondaryAnimator?.cancel()
|
||||
}
|
||||
if (forced || sliderSecondaryAnimator == null) {
|
||||
animatorSecondaryListener.prevThumbSecondaryValue = thumbSecondaryValue
|
||||
prevThumbSecondaryValue = thumbSecondaryValue
|
||||
thumbSecondaryValue = newValue
|
||||
notifyThumbSecondaryChangedListeners(
|
||||
animatorSecondaryListener.prevThumbSecondaryValue,
|
||||
prevThumbSecondaryValue,
|
||||
thumbSecondaryValue
|
||||
)
|
||||
}
|
||||
@@ -354,7 +307,7 @@ open class SliderView @JvmOverloads constructor(
|
||||
|
||||
/**
|
||||
* The appearance of thumb secondary.
|
||||
* Returns [null] if doesn't exist.
|
||||
* Returns null if doesn't exist.
|
||||
*/
|
||||
var thumbSecondaryDrawable: Drawable? = null
|
||||
set(value) {
|
||||
@@ -365,7 +318,7 @@ open class SliderView @JvmOverloads constructor(
|
||||
|
||||
/**
|
||||
* The appearance of thumb secondary text.
|
||||
* Returns [null] if doesn't exist.
|
||||
* Returns null if doesn't exist.
|
||||
*/
|
||||
var thumbSecondTextDrawable: TextDrawable? = null
|
||||
set(drawable) {
|
||||
@@ -393,10 +346,6 @@ open class SliderView @JvmOverloads constructor(
|
||||
listeners.addObserver(listener)
|
||||
}
|
||||
|
||||
fun removeOnChangedListener(listener: ChangedListener) {
|
||||
listeners.removeObserver(listener)
|
||||
}
|
||||
|
||||
fun clearOnThumbChangedListener() = listeners.clear()
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
@@ -441,10 +390,6 @@ open class SliderView @JvmOverloads constructor(
|
||||
return maxOf(thumbHeight, trackHeight, rangesHeight)
|
||||
}
|
||||
|
||||
private val Drawable?.boundsWidth get() = this?.bounds?.width() ?: 0
|
||||
|
||||
private val Drawable?.boundsHeight get() = this?.bounds?.height() ?: 0
|
||||
|
||||
override fun getSuggestedMinimumWidth(): Int {
|
||||
val tickCount = (maxValue - minValue + 1).toInt()
|
||||
|
||||
@@ -592,7 +537,7 @@ open class SliderView @JvmOverloads constructor(
|
||||
return false
|
||||
}
|
||||
|
||||
private fun getClosestThumb(position: Int): Thumb {
|
||||
internal fun getClosestThumb(position: Int): Thumb {
|
||||
if (!isThumbSecondaryEnabled()) {
|
||||
return Thumb.THUMB
|
||||
}
|
||||
@@ -618,6 +563,9 @@ open class SliderView @JvmOverloads constructor(
|
||||
return a11yHelper.dispatchKeyEvent(event) || super.dispatchKeyEvent(event)
|
||||
}
|
||||
|
||||
internal fun setValueToAccessibilityThumb(thumb: Thumb, value: Float) =
|
||||
setValueToThumb(thumb, value.inBoarders(), animated = false, forced = true)
|
||||
|
||||
private fun setValueToThumb(thumb: Thumb, value: Float, animated: Boolean, forced: Boolean = false) =
|
||||
when (thumb) {
|
||||
Thumb.THUMB -> trySetThumbValue(value, animated, forced)
|
||||
@@ -683,6 +631,8 @@ open class SliderView @JvmOverloads constructor(
|
||||
interpolator = animationInterpolator
|
||||
}
|
||||
|
||||
internal fun getPositionInView(thumbValue: Float) = thumbValue.toPosition() + paddingLeft
|
||||
|
||||
/**
|
||||
* Calculates slider active range presented by two values, where start <= end.
|
||||
* If there is only one thumb then its value will be considered as the end of the range.
|
||||
@@ -714,7 +664,7 @@ open class SliderView @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private enum class Thumb {
|
||||
internal enum class Thumb {
|
||||
THUMB, THUMB_SECONDARY
|
||||
}
|
||||
|
||||
@@ -732,114 +682,9 @@ open class SliderView @JvmOverloads constructor(
|
||||
@Px var endPosition = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides info about virtual view hierarchy for accessibility services.
|
||||
*/
|
||||
private inner class A11yHelper(private val slider: SliderView) : ExploreByTouchHelper(slider) {
|
||||
private val bounds = Rect()
|
||||
private val step get() = max(((maxValue - minValue) * 0.05).roundToInt(), 1)
|
||||
internal companion object {
|
||||
val Drawable?.boundsWidth get() = this?.bounds?.width() ?: 0
|
||||
|
||||
override fun getVirtualViewAt(x: Float, y: Float): Int {
|
||||
if (x < leftPaddingOffset) {
|
||||
return THUMB_VIRTUAL_VIEW_ID
|
||||
}
|
||||
val position = when (getClosestThumb(x.toInt())) {
|
||||
Thumb.THUMB -> THUMB_VIRTUAL_VIEW_ID
|
||||
Thumb.THUMB_SECONDARY -> SECONDARY_THUMB_VIRTUAL_VIEW_ID
|
||||
}
|
||||
return position
|
||||
}
|
||||
|
||||
override fun getVisibleVirtualViews(virtualViewIds: MutableList<Int>) {
|
||||
virtualViewIds.add(THUMB_VIRTUAL_VIEW_ID)
|
||||
if (thumbSecondaryValue != null) virtualViewIds.add(SECONDARY_THUMB_VIRTUAL_VIEW_ID)
|
||||
}
|
||||
|
||||
override fun onPopulateNodeForVirtualView(
|
||||
virtualViewId: Int,
|
||||
node: AccessibilityNodeInfoCompat
|
||||
) {
|
||||
node.className = SeekBar::class.java.name
|
||||
node.rangeInfo = AccessibilityNodeInfoCompat.RangeInfoCompat.obtain(
|
||||
RANGE_TYPE_INT, minValue, maxValue, virtualViewId.toThumbValue()
|
||||
)
|
||||
val contentDescription = StringBuilder()
|
||||
slider.contentDescription?.let {
|
||||
contentDescription.append(it).append(",")
|
||||
}
|
||||
contentDescription.append(startOrEndDescription(virtualViewId))
|
||||
node.contentDescription = contentDescription.toString()
|
||||
node.addAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_FORWARD)
|
||||
node.addAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_BACKWARD)
|
||||
|
||||
updateBounds(virtualViewId)
|
||||
node.setBoundsInParent(bounds)
|
||||
}
|
||||
|
||||
override fun onPerformActionForVirtualView(virtualViewId: Int, action: Int, arguments: Bundle?): Boolean {
|
||||
when (action) {
|
||||
android.R.id.accessibilityActionSetProgress -> {
|
||||
if (arguments == null || !arguments.containsKey(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_PROGRESS_VALUE)) {
|
||||
return false
|
||||
}
|
||||
val value = arguments.getFloat(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_PROGRESS_VALUE)
|
||||
setThumbValue(virtualViewId, value)
|
||||
}
|
||||
|
||||
AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD ->
|
||||
setThumbValue(virtualViewId, virtualViewId.toThumbValue() + step)
|
||||
AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD ->
|
||||
setThumbValue(virtualViewId, virtualViewId.toThumbValue() - step)
|
||||
else -> return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun startOrEndDescription(virtualViewId: Int): String {
|
||||
return when {
|
||||
thumbSecondaryValue == null -> ""
|
||||
virtualViewId == THUMB_VIRTUAL_VIEW_ID -> context.getString(R.string.div_slider_range_start)
|
||||
virtualViewId == SECONDARY_THUMB_VIRTUAL_VIEW_ID -> context.getString(R.string.div_slider_range_end)
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateBounds(index: Int) {
|
||||
val width: Int
|
||||
val height: Int
|
||||
when (index) {
|
||||
SECONDARY_THUMB_VIRTUAL_VIEW_ID -> {
|
||||
width = thumbSecondaryDrawable.boundsWidth
|
||||
height = thumbSecondaryDrawable.boundsHeight
|
||||
}
|
||||
else -> {
|
||||
width = thumbDrawable.boundsWidth
|
||||
height = thumbDrawable.boundsHeight
|
||||
}
|
||||
}
|
||||
|
||||
val position = index.toThumbValue().toPosition() + slider.paddingLeft
|
||||
bounds.left = position
|
||||
bounds.right = position + width
|
||||
bounds.top = slider.height / 2 - height / 2
|
||||
bounds.bottom = slider.height / 2 + height / 2
|
||||
}
|
||||
|
||||
private fun setThumbValue(virtualViewId: Int, value: Float) {
|
||||
setValueToThumb(virtualViewId.toThumb(), value.inBoarders(), animated = false, forced = true)
|
||||
sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_SELECTED)
|
||||
invalidateVirtualView(virtualViewId)
|
||||
}
|
||||
|
||||
private fun Int.toThumb(): Thumb = when {
|
||||
this == THUMB_VIRTUAL_VIEW_ID -> Thumb.THUMB
|
||||
thumbSecondaryValue != null -> Thumb.THUMB_SECONDARY
|
||||
else -> Thumb.THUMB
|
||||
}
|
||||
|
||||
private fun Int.toThumbValue(): Float = when {
|
||||
this == THUMB_VIRTUAL_VIEW_ID -> thumbValue
|
||||
else -> thumbSecondaryValue ?: thumbValue
|
||||
}
|
||||
val Drawable?.boundsHeight get() = this?.bounds?.height() ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,6 @@ import android.text.TextUtils;
|
||||
import android.text.method.TransformationMethod;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -146,10 +143,6 @@ public final class TabView extends SuperLineHeightTextView {
|
||||
if (mBoldTextOnSelection && changed) {
|
||||
setupTypeface();
|
||||
}
|
||||
|
||||
if (changed && selected) {
|
||||
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupTypeface() {
|
||||
@@ -178,19 +171,9 @@ public final class TabView extends SuperLineHeightTextView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
|
||||
super.onInitializeAccessibilityEvent(event);
|
||||
// This view masquerades as an action bar tab.
|
||||
public CharSequence getAccessibilityClassName() {
|
||||
//noinspection deprecation
|
||||
event.setClassName(ActionBar.Tab.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
||||
super.onInitializeAccessibilityNodeInfo(info);
|
||||
// This view masquerades as an action bar tab.
|
||||
//noinspection deprecation
|
||||
info.setClassName(ActionBar.Tab.class.getName());
|
||||
return ActionBar.Tab.class.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -84,4 +84,6 @@ internal open class TabsLayout @JvmOverloads constructor(
|
||||
bottomMargin = resources.getDimensionPixelSize(R.dimen.title_tab_title_margin_vertical)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAccessibilityClassName() = "android.widget.TabWidget"
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
<item name="div_transition_position" type="id"/>
|
||||
|
||||
<item name="div_releasable_list" type="id"/>
|
||||
<item name="div_pager_item_clip_id" type="id"/>
|
||||
|
||||
<item name="div_layout_provider_listener_id" type="id"/>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.yandex.div.core.view2
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.View
|
||||
import com.yandex.div.DivDataTag
|
||||
import com.yandex.div.core.Div2Context
|
||||
import com.yandex.div.core.DivConfiguration
|
||||
import com.yandex.div.core.images.DivImageDownloadCallback
|
||||
import com.yandex.div.core.images.DivImageLoader
|
||||
import com.yandex.div.core.images.LoadReference
|
||||
import com.yandex.div.core.util.AccessibilityStateProvider
|
||||
import com.yandex.div.core.view2.divs.UnitTestData
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.robolectric.Robolectric
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class TypeAutoAccessibilityTest {
|
||||
|
||||
private val activity = Robolectric.buildActivity(Activity::class.java).get()
|
||||
private val imageLoader = mock<DivImageLoader> {
|
||||
on { loadImage(any(), any<DivImageDownloadCallback>()) } doReturn LoadReference { }
|
||||
}
|
||||
private val divContext = Div2Context(activity, DivConfiguration.Builder(imageLoader).build())
|
||||
private val data = UnitTestData("accessibility", "auto_types.json", "regression_test_data").dataWithTemplates
|
||||
private val divView: Div2View
|
||||
|
||||
init {
|
||||
AccessibilityStateProvider.touchExplorationEnabled = true
|
||||
divView = Div2View(divContext)
|
||||
divView.setData(data, DivDataTag("tag"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `text is important for accessibility`() {
|
||||
checkViewIsImportantForAccessibility("text")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `input is important for accessibility`() {
|
||||
checkViewIsImportantForAccessibility("input")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `select is important for accessibility`() {
|
||||
checkViewIsImportantForAccessibility("select")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `image with description is important for accessibility`() {
|
||||
checkViewIsImportantForAccessibility("image with description")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `image without action or description is not important for accessibility`() {
|
||||
checkViewIsImportantForAccessibility("image", isImportant = false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `slider is important for accessibility`() {
|
||||
checkViewIsImportantForAccessibility("slider")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `separator is not important for accessibility`() {
|
||||
checkViewIsImportantForAccessibility("separator", isImportant = false)
|
||||
}
|
||||
|
||||
private fun checkViewIsImportantForAccessibility(tag: String, isImportant: Boolean = true) {
|
||||
Assert.assertEquals(isImportant, divView.findViewWithTag<View>(tag)?.isImportantForAccessibility)
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ internal fun assertActionApplied(context: BindingContext, target: View, actionUr
|
||||
pressStartActions = anyOrNull(),
|
||||
pressEndActions = anyOrNull(),
|
||||
actionAnimation = any(),
|
||||
accessibility = anyOrNull(),
|
||||
captureFocusOnAction = any(),
|
||||
)
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@ import org.robolectric.RuntimeEnvironment
|
||||
open class DivBinderTest {
|
||||
|
||||
internal val actionBinder = mock<DivActionBinder> {
|
||||
on { bindDivActions(any(), any(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(), any(), anyOrNull(), any()) }.thenCallRealMethod()
|
||||
on { bindDivActions(any(), any(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(),
|
||||
anyOrNull(), any(), any()) }.thenCallRealMethod()
|
||||
}
|
||||
private val mockComponent = mock<Div2Component>(defaultAnswer = Mockito.RETURNS_DEEP_STUBS) {
|
||||
on { actionBinder } doReturn actionBinder
|
||||
@@ -83,6 +84,7 @@ open class DivBinderTest {
|
||||
|
||||
private val viewIdProvider = DivViewIdProvider()
|
||||
|
||||
@Suppress("unused")
|
||||
private val viewComponent = mock<Div2ViewComponent>(defaultAnswer = Mockito.RETURNS_DEEP_STUBS) {
|
||||
on { viewIdProvider } doReturn viewIdProvider
|
||||
on { releaseViewVisitor } doReturn visitor
|
||||
|
||||
@@ -38,7 +38,8 @@ internal fun divView(
|
||||
)
|
||||
|
||||
val actionBinder = mock<DivActionBinder> {
|
||||
on { bindDivActions(any(), any(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(), any(), anyOrNull(), any()) }.thenCallRealMethod()
|
||||
on { bindDivActions(any(), any(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(),
|
||||
anyOrNull(), any(), any()) }.thenCallRealMethod()
|
||||
}
|
||||
|
||||
val component = mock<Div2Component>(defaultAnswer = Mockito.RETURNS_DEEP_STUBS) {
|
||||
|
||||
@@ -12,6 +12,7 @@ import java.io.File
|
||||
class UnitTestData(
|
||||
private val dir: String,
|
||||
private val fileName: String,
|
||||
private val testDataDir: String = "unit_test_data"
|
||||
) {
|
||||
|
||||
val data: DivData
|
||||
@@ -21,7 +22,7 @@ class UnitTestData(
|
||||
val div: Div
|
||||
get() {
|
||||
if (_div == null) {
|
||||
val path = "${BuildConfig.DIV2_JSON_PATH}/unit_test_data/$dir/$fileName"
|
||||
val path = "${BuildConfig.DIV2_JSON_PATH}/$testDataDir/$dir/$fileName"
|
||||
val jsonString = File(path).readText(Charsets.UTF_8)
|
||||
val json = JSONObject(jsonString)
|
||||
val environment = DivParsingEnvironment(ParsingErrorLogger.LOG)
|
||||
@@ -32,7 +33,7 @@ class UnitTestData(
|
||||
|
||||
val dataWithTemplates: DivData
|
||||
get() {
|
||||
val path = "${BuildConfig.DIV2_JSON_PATH}/unit_test_data/$dir/$fileName"
|
||||
val path = "${BuildConfig.DIV2_JSON_PATH}/$testDataDir/$dir/$fileName"
|
||||
val jsonString = File(path).readText(Charsets.UTF_8)
|
||||
val json = JSONObject(jsonString)
|
||||
val templates = json.getJSONObject("templates")
|
||||
@@ -46,7 +47,7 @@ class UnitTestData(
|
||||
|
||||
val patchWithTemplates: DivPatch
|
||||
get() {
|
||||
val path = "${BuildConfig.DIV2_JSON_PATH}/unit_test_data/$dir/$fileName"
|
||||
val path = "${BuildConfig.DIV2_JSON_PATH}/$testDataDir/$dir/$fileName"
|
||||
val jsonString = File(path).readText(Charsets.UTF_8)
|
||||
val json = JSONObject(jsonString)
|
||||
val templates = json.getJSONObject("templates")
|
||||
|
||||
@@ -10,7 +10,7 @@ import org.junit.Test
|
||||
|
||||
class DivFocusableInputTest {
|
||||
|
||||
private val activityTestRule = ActivityTestRule(DummyActivity::class.java)
|
||||
private val activityTestRule = ActivityTestRule(DummyActivity::class.java, true)
|
||||
|
||||
@get:Rule
|
||||
val rule = uiTestRule { activityTestRule }
|
||||
|
||||
@@ -7,13 +7,15 @@ import com.yandex.divkit.demo.DummyActivity
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
private const val TEXT_BEFORE_BREAK = "https://Text_with different+symbols(123)@site.ru"
|
||||
internal const val TEXT_WITH_DIFFERENT_SYMBOLS = "$TEXT_BEFORE_BREAK\nsecond_line"
|
||||
internal const val SEARCH_KEY_TAPPED = "Search key tapped!"
|
||||
internal const val TEXT_WITH_DIFFERENT_SYMBOLS =
|
||||
"https://Text_with different\nsymbols+(123)@site.ru"
|
||||
|
||||
private const val TEXT_WITHOUT_BREAK = "https://Text_with differentsymbols+(123)@site.ru"
|
||||
private const val SEARCH_KEY_TAPPED = "Search key tapped!"
|
||||
|
||||
class DivInputKeyboardTypeTest {
|
||||
|
||||
private val activityRule = ActivityTestRule(DummyActivity::class.java)
|
||||
private val activityRule = ActivityTestRule(DummyActivity::class.java, true)
|
||||
|
||||
@get:Rule
|
||||
val rule = uiTestRule { activityRule }
|
||||
@@ -30,7 +32,7 @@ class DivInputKeyboardTypeTest {
|
||||
fun checkSingleLineText() {
|
||||
checkType(
|
||||
type = "single_line_text",
|
||||
expectedText = TEXT_BEFORE_BREAK
|
||||
expectedText = TEXT_WITHOUT_BREAK
|
||||
)
|
||||
}
|
||||
|
||||
@@ -72,7 +74,7 @@ class DivInputKeyboardTypeTest {
|
||||
fun checkEmail() {
|
||||
checkType(
|
||||
type = "email",
|
||||
expectedText = TEXT_BEFORE_BREAK
|
||||
expectedText = TEXT_WITHOUT_BREAK
|
||||
)
|
||||
}
|
||||
|
||||
@@ -80,7 +82,7 @@ class DivInputKeyboardTypeTest {
|
||||
fun checkUri() {
|
||||
checkType(
|
||||
type = "uri",
|
||||
expectedText = TEXT_BEFORE_BREAK
|
||||
expectedText = TEXT_WITHOUT_BREAK
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
package com.yandex.div
|
||||
|
||||
import androidx.test.rule.ActivityTestRule
|
||||
import com.yandex.div.rule.uiTestRule
|
||||
import com.yandex.div.steps.accessibility
|
||||
import com.yandex.divkit.demo.DummyActivity
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class TypeAutoAccessibilityTest {
|
||||
private val activityTestRule = ActivityTestRule(DummyActivity::class.java)
|
||||
|
||||
@get:Rule
|
||||
val rule = uiTestRule { activityTestRule }
|
||||
|
||||
@Before
|
||||
fun buildContainer() {
|
||||
accessibility {
|
||||
testAsset = "regression_test_data/accessibility/auto_types.json"
|
||||
activityTestRule.buildContainer()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun checkTextHasCorrectClassnameAndFocusable() {
|
||||
accessibility {
|
||||
assert {
|
||||
checkTextIsFocusable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun checkInputHasCorrectClassnameAndFocusable() {
|
||||
accessibility {
|
||||
assert {
|
||||
checkInputIsFocusable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun checkSelectHasCorrectClassnameAndFocusable() {
|
||||
accessibility {
|
||||
assert {
|
||||
checkSelectIsFocusable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun checkImageWithDescriptionHasCorrectClassnameAndFocusable() {
|
||||
accessibility {
|
||||
assert {
|
||||
checkImageWithDescriptionIsFocusable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun checkImageWithoutActionOrDescriptionHasNoClassnameAndNotFocusable() {
|
||||
accessibility {
|
||||
assert {
|
||||
checkImageWithoutDescriptionIsNotFocusable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun checkSliderHasCorrectClassnameAndNotFocusable() {
|
||||
accessibility {
|
||||
assert {
|
||||
// slider itself should be to focusable, only it's thumbs are focusable
|
||||
checkSliderIsNotFocusable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun checkSeparatorHasNoClassnameAndNotFocusable() {
|
||||
accessibility {
|
||||
assert {
|
||||
checkSeparatorIsNotFocusable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ open class ActivityParamsTestRule<A : Activity>(
|
||||
launchActivity: Boolean = true,
|
||||
private val action: String? = null,
|
||||
private val params: Bundle? = null
|
||||
) : IntentsTestRule<A>(activityClass, false, launchActivity) {
|
||||
) : IntentsTestRule<A>(activityClass, true, launchActivity) {
|
||||
|
||||
constructor(activityClass: Class<A>, vararg params: Pair<String, Any?>) : this(
|
||||
activityClass = activityClass,
|
||||
|
||||
@@ -1,26 +1,39 @@
|
||||
package com.yandex.div.steps
|
||||
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.ViewInteraction
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withTagValue
|
||||
import com.yandex.div.internal.widget.slider.SliderView
|
||||
import org.hamcrest.Matchers.allOf
|
||||
import org.hamcrest.Matchers.equalTo
|
||||
|
||||
object SliderViews {
|
||||
val commonSlider get() = onView(isAssignableFrom(SliderView::class.java))
|
||||
|
||||
val defaultSlider get() = onView(allOf(withContentDescription("Default slider"),
|
||||
isAssignableFrom(SliderView::class.java)))
|
||||
val commonSlider: ViewInteraction get() = onView(isAssignableFrom(SliderView::class.java))
|
||||
|
||||
val max10Slider get() = onView(allOf(withContentDescription("Min - 0, max - 10"),
|
||||
isAssignableFrom(SliderView::class.java)))
|
||||
val defaultSlider: ViewInteraction get() = onView(allOf(
|
||||
withTagValue(equalTo("default_slider")),
|
||||
isAssignableFrom(SliderView::class.java))
|
||||
)
|
||||
|
||||
val max3Slider get() = onView(allOf(withContentDescription("Min - 0, max - 3"),
|
||||
isAssignableFrom(SliderView::class.java)))
|
||||
val max10Slider: ViewInteraction get() = onView(allOf(
|
||||
withTagValue(equalTo("min_0_max_10")),
|
||||
isAssignableFrom(SliderView::class.java))
|
||||
)
|
||||
|
||||
val doubleDefaultSlider get() = onView(allOf(withContentDescription("Double without ticks"),
|
||||
isAssignableFrom(SliderView::class.java)))
|
||||
val max3Slider: ViewInteraction get() = onView(allOf(
|
||||
withTagValue(equalTo("min_0_max_3")),
|
||||
isAssignableFrom(SliderView::class.java))
|
||||
)
|
||||
|
||||
val doubleWithDivisionsSlider get() = onView(allOf(withContentDescription("Double with ticks"),
|
||||
isAssignableFrom(SliderView::class.java)))
|
||||
val doubleDefaultSlider: ViewInteraction get() = onView(allOf(
|
||||
withTagValue(equalTo("double_without_ticks")),
|
||||
isAssignableFrom(SliderView::class.java))
|
||||
)
|
||||
|
||||
val doubleWithDivisionsSlider: ViewInteraction get() = onView(allOf(
|
||||
withTagValue(equalTo("double_with_ticks")),
|
||||
isAssignableFrom(SliderView::class.java))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
package com.yandex.div.steps
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.ViewInteraction
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isFocusable
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isNotFocusable
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withTagValue
|
||||
import androidx.test.rule.ActivityTestRule
|
||||
import com.yandex.test.util.Report.step
|
||||
import com.yandex.test.util.StepsDsl
|
||||
import org.hamcrest.Matchers.`is`
|
||||
|
||||
private const val TEXT_ID = "text"
|
||||
private const val INPUT_ID = "input"
|
||||
private const val SELECT_ID = "select"
|
||||
private const val IMAGE_ID = "image"
|
||||
private const val IMAGE_WITH_DESCRIPTION_ID = "image with description"
|
||||
private const val SLIDER_ID = "slider"
|
||||
private const val SEPARATOR_ID = "separator"
|
||||
|
||||
private val textView get() = getViewWithId(TEXT_ID)
|
||||
private val inputView get() = getViewWithId(INPUT_ID)
|
||||
private val selectView get() = getViewWithId(SELECT_ID)
|
||||
private val imageView get() = getViewWithId(IMAGE_ID)
|
||||
private val imageWithDescriptionView get() = getViewWithId(IMAGE_WITH_DESCRIPTION_ID)
|
||||
private val sliderView get() = getViewWithId(SLIDER_ID)
|
||||
private val separatorView get() = getViewWithId(SEPARATOR_ID)
|
||||
|
||||
internal fun accessibility(f: TypeAutoAccessibilitySteps.() -> Unit) = f(TypeAutoAccessibilitySteps())
|
||||
|
||||
@StepsDsl
|
||||
internal class TypeAutoAccessibilitySteps: DivTestAssetSteps() {
|
||||
|
||||
fun ActivityTestRule<*>.buildContainer(): Unit = step("Build container") {
|
||||
buildContainer(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
}
|
||||
|
||||
internal fun assert(f: TypeAutoAccessibilityAssertions.() -> Unit) = f(TypeAutoAccessibilityAssertions())
|
||||
}
|
||||
|
||||
@StepsDsl
|
||||
internal class TypeAutoAccessibilityAssertions {
|
||||
|
||||
fun checkTextIsFocusable(): Unit = step("Check text view is focusable ") {
|
||||
textView.checkFocusable()
|
||||
}
|
||||
fun checkInputIsFocusable(): Unit = step("Check input view is focusable ") {
|
||||
inputView.checkFocusable()
|
||||
}
|
||||
fun checkSelectIsFocusable(): Unit = step("Check select view is focusable ") {
|
||||
selectView.checkFocusable()
|
||||
}
|
||||
fun checkImageWithDescriptionIsFocusable(): Unit =
|
||||
step("Check image with description is focusable ") {
|
||||
imageWithDescriptionView.checkFocusable()
|
||||
}
|
||||
fun checkSeparatorIsNotFocusable(): Unit = step("Check separator is not focusable") {
|
||||
separatorView.checkNotFocusable()
|
||||
}
|
||||
fun checkImageWithoutDescriptionIsNotFocusable(): Unit =
|
||||
step("Check image without description is not focusable") {
|
||||
imageView.checkNotFocusable()
|
||||
}
|
||||
fun checkSliderIsNotFocusable(): Unit = step("Check slider is not focusable") {
|
||||
sliderView.checkNotFocusable() // only it's virtual view should be focusable
|
||||
}
|
||||
|
||||
private fun ViewInteraction.checkFocusable(): Unit {
|
||||
check(matches(isFocusable()))
|
||||
}
|
||||
|
||||
private fun ViewInteraction.checkNotFocusable(): Unit {
|
||||
check(matches(isNotFocusable()))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getViewWithId(id: String) = onView(withTagValue(`is`(id)))
|
||||
@@ -4,17 +4,15 @@ import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.FOCUS_BEFORE_DESCENDANTS
|
||||
import android.widget.EditText
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.descendants
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.yandex.div.core.Div2Context
|
||||
import com.yandex.div.core.view2.Div2View
|
||||
import com.yandex.divkit.demo.R
|
||||
@@ -76,8 +74,8 @@ class DivScreenshotActivity : AppCompatActivity() {
|
||||
ViewGroup.LayoutParams.MATCH_PARENT else ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, divViewHeight)
|
||||
id = R.id.morda_screenshot_div
|
||||
removeAutofocusForOldApis()
|
||||
hideCursor()
|
||||
focusRecyclersBeforeDescendants()
|
||||
}
|
||||
applyConfiguration(divView)
|
||||
setContentView(divView)
|
||||
@@ -85,6 +83,12 @@ class DivScreenshotActivity : AppCompatActivity() {
|
||||
|
||||
fun getTestCaseJson() = assetReader.read(cardAssetName)
|
||||
|
||||
private fun View.removeAutofocusForOldApis() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
isFocusableInTouchMode = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun ViewGroup.hideCursor() {
|
||||
for (child in children) {
|
||||
if (child is EditText) {
|
||||
@@ -96,14 +100,6 @@ class DivScreenshotActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun ViewGroup.focusRecyclersBeforeDescendants() {
|
||||
descendants.forEach {
|
||||
if (it is RecyclerView) {
|
||||
it.descendantFocusability = FOCUS_BEFORE_DESCENDANTS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyConfiguration(view: View) {
|
||||
val divJson = getTestCaseJson()
|
||||
val configuration = if (divJson.has("configuration")) {
|
||||
|
||||
@@ -31,11 +31,11 @@ class Div2FocusScreenshotTest(case: String, escapedCase: String) {
|
||||
|
||||
@Test
|
||||
@Screenshot(viewId = R.id.morda_screenshot_div, relativePath = "not_focused")
|
||||
fun divScreenshotTopFocused() = Unit
|
||||
fun divScreenshotNotFocused() = Unit
|
||||
|
||||
@Test
|
||||
@Screenshot(viewId = R.id.morda_screenshot_div, relativePath = "focused")
|
||||
fun divScreenshotBottomFocused() {
|
||||
fun divScreenshotFocused() {
|
||||
divFocus { clickOnTopInput() }
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
@@ -11,7 +11,7 @@ open class ActivityParamsTestRule<A : Activity>(
|
||||
launchActivity: Boolean = true,
|
||||
private val action: String? = null,
|
||||
private val params: Bundle? = null
|
||||
) : IntentsTestRule<A>(activityClass, false, launchActivity) {
|
||||
) : IntentsTestRule<A>(activityClass, true, launchActivity) {
|
||||
|
||||
constructor(activityClass: Class<A>, vararg params: Pair<String, Any?>) : this(
|
||||
activityClass = activityClass,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"description": "Text with focused text color and focus background",
|
||||
"platforms": [
|
||||
"android",
|
||||
"ios"
|
||||
],
|
||||
"div_data": {
|
||||
|
||||
@@ -160,6 +160,7 @@
|
||||
},
|
||||
{
|
||||
"type": "slider",
|
||||
"id": "double_with_ticks",
|
||||
"width": {
|
||||
"type": "match_parent"
|
||||
},
|
||||
|
||||
@@ -158,6 +158,7 @@
|
||||
"width": {
|
||||
"type": "match_parent"
|
||||
},
|
||||
"id": "min_0_max_10",
|
||||
"accessibility": {
|
||||
"description": "Min - 0, max - 10"
|
||||
},
|
||||
|
||||
@@ -453,7 +453,7 @@
|
||||
},
|
||||
{
|
||||
"type": "slider_preset",
|
||||
"id": "slider_default",
|
||||
"id": "default_slider",
|
||||
"thumb_value_variable": "first_thumb_value",
|
||||
"accessibility": {
|
||||
"description": "Default slider"
|
||||
@@ -465,7 +465,7 @@
|
||||
},
|
||||
{
|
||||
"type": "slider_preset",
|
||||
"id": "slider_default",
|
||||
"id": "min_0_max_10",
|
||||
"min_value": 0,
|
||||
"max_value": 10,
|
||||
"thumb_value_variable": "second_thumb_value",
|
||||
@@ -525,7 +525,7 @@
|
||||
},
|
||||
{
|
||||
"type": "slider_preset",
|
||||
"id": "slider_default",
|
||||
"id": "min_0_max_3",
|
||||
"min_value": 0,
|
||||
"max_value": 3,
|
||||
"thumb_value_variable": "third_thumb_value",
|
||||
@@ -585,7 +585,7 @@
|
||||
},
|
||||
{
|
||||
"type": "slider_preset",
|
||||
"id": "slider_default",
|
||||
"id": "double_with_ticks",
|
||||
"min_value": 0,
|
||||
"max_value": 10,
|
||||
"thumb_value_variable": "fourth_thumb_value",
|
||||
@@ -665,7 +665,7 @@
|
||||
},
|
||||
{
|
||||
"type": "slider_preset",
|
||||
"id": "slider_default",
|
||||
"id": "double_without_ticks",
|
||||
"thumb_value_variable": "fifth_thumb_value",
|
||||
"thumb_secondary_value_variable": "fifth_thumb_secondary_value",
|
||||
"accessibility": {
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
"items": [
|
||||
{
|
||||
"type": "slider",
|
||||
"id": "min_0_max_10",
|
||||
"accessibility": {
|
||||
"description": "Min - 0, max - 10"
|
||||
},
|
||||
|
||||