From c113c765fffea8dabc59872e2feddad32f324571 Mon Sep 17 00:00:00 2001 From: pkurchatov Date: Wed, 25 Mar 2026 10:13:58 +0300 Subject: [PATCH] Updated Compose BOM to 2026.03.00 commit_hash:2c382f7fc098471c025ac6e4bdd4b55a2da075d3 --- .mapping.json | 8 +- client/android/compose-interop/build.gradle | 3 - client/android/compose/build.gradle.kts | 8 +- .../com/yandex/div/compose/DivViewPreview.kt | 62 ++++ .../com/yandex/div/compose/DivViewPreview.kt | 107 ------- .../com/yandex/div/core/DivPreloaderTest.kt | 2 +- .../yandex/div/core/util/DivWalkTreeTest.kt | 46 ++- .../view2/DivVisibilityActionTrackerTest.kt | 10 +- .../view2/animations/DivComparatorTest.kt | 40 +-- client/android/divkit-demo-app/build.gradle | 2 + .../div/utils/UiTestDivActionHandler.kt | 35 --- .../java/com/yandex/div/view/TextView.kt | 56 ---- .../java/com/yandex/div/view/ViewActions.kt | 120 -------- .../com/yandex/div/view/ViewAssertions.kt | 28 -- .../com/yandex/div/view/ViewInteraction.kt | 181 ------------ .../java/com/yandex/div/view/getThemeColor.kt | 43 --- .../java/com/yandex/div/view/viewHelpers.kt | 271 ------------------ .../java/com/yandex/divkit/demo/Container.kt | 9 +- .../yandex/divkit/demo/div/Div2Activity.kt | 10 +- .../com/yandex/divkit/demo/div/DivActivity.kt | 9 +- .../divkit/demo/screenshot/DivAssetReader.kt | 16 -- .../yandex/divkit/demo/uri/CustomTabHelper.kt | 7 +- .../demo/utils/DivkitDemoPermissionHelper.kt | 56 +--- .../divkit/demo/utils/JsonAssetReader.kt | 33 --- .../div-container/alpha-and-background.png | Bin 13183 -> 13124 bytes .../horizontal-gallery-items-not-fit.png | Bin 20807 -> 21822 bytes .../vertical-gallery-items-not-fit.png | Bin 17555 -> 18160 bytes .../div-image/custom-alpha.png | Bin 2026 -> 2031 bytes client/android/gradle/libs.versions.toml | 2 +- .../div/test/data/DivBackgroundUtils.kt | 13 + .../yandex/div/test/data/DivContainerUtils.kt | 8 + .../com/yandex/div/test/data/DivTextUtils.kt | 25 +- 32 files changed, 181 insertions(+), 1029 deletions(-) create mode 100644 client/android/compose/src/androidTest/kotlin/com/yandex/div/compose/DivViewPreview.kt delete mode 100644 client/android/compose/src/main/kotlin/com/yandex/div/compose/DivViewPreview.kt delete mode 100644 client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/UiTestDivActionHandler.kt delete mode 100644 client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/TextView.kt delete mode 100644 client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/getThemeColor.kt delete mode 100644 client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/viewHelpers.kt delete mode 100644 client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/JsonAssetReader.kt create mode 100644 client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivBackgroundUtils.kt diff --git a/.mapping.json b/.mapping.json index 85c300821..b7352a0a0 100644 --- a/.mapping.json +++ b/.mapping.json @@ -602,12 +602,12 @@ "client/android/compose-interop/src/main/java/com/yandex/div/compose/interop/DivViewInterop.kt":"divkit/public/client/android/compose-interop/src/main/java/com/yandex/div/compose/interop/DivViewInterop.kt", "client/android/compose/build.gradle.kts":"divkit/public/client/android/compose/build.gradle.kts", "client/android/compose/proguard-rules.pro":"divkit/public/client/android/compose/proguard-rules.pro", + "client/android/compose/src/androidTest/kotlin/com/yandex/div/compose/DivViewPreview.kt":"divkit/public/client/android/compose/src/androidTest/kotlin/com/yandex/div/compose/DivViewPreview.kt", "client/android/compose/src/main/kotlin/com/yandex/div/compose/DivComposeConfiguration.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/DivComposeConfiguration.kt", "client/android/compose/src/main/kotlin/com/yandex/div/compose/DivContext.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/DivContext.kt", "client/android/compose/src/main/kotlin/com/yandex/div/compose/DivException.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/DivException.kt", "client/android/compose/src/main/kotlin/com/yandex/div/compose/DivReporter.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/DivReporter.kt", "client/android/compose/src/main/kotlin/com/yandex/div/compose/DivView.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/DivView.kt", - "client/android/compose/src/main/kotlin/com/yandex/div/compose/DivViewPreview.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/DivViewPreview.kt", "client/android/compose/src/main/kotlin/com/yandex/div/compose/actions/DivActionData.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/actions/DivActionData.kt", "client/android/compose/src/main/kotlin/com/yandex/div/compose/actions/DivActionHandler.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/actions/DivActionHandler.kt", "client/android/compose/src/main/kotlin/com/yandex/div/compose/actions/DivActionHandlingContext.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/actions/DivActionHandlingContext.kt", @@ -1839,20 +1839,16 @@ "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", - "client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/UiTestDivActionHandler.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/UiTestDivActionHandler.kt", "client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/Utils.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/Utils.kt", "client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/ViewCoordinatesHelper.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/ViewCoordinatesHelper.kt", "client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/ViewInteractionExt.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/ViewInteractionExt.kt", "client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/ViewPagerActions.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/ViewPagerActions.kt", - "client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/TextView.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/TextView.kt", "client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/Utils.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/Utils.kt", "client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewActions.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewActions.kt", "client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewAssertions.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewAssertions.kt", "client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewInteraction.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewInteraction.kt", "client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewMatchers.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewMatchers.kt", "client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/actions/ExtendedScrollTo.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/actions/ExtendedScrollTo.kt", - "client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/getThemeColor.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/getThemeColor.kt", - "client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/viewHelpers.kt":"divkit/public/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/viewHelpers.kt", "client/android/divkit-demo-app/src/debug/AndroidManifest.xml":"divkit/public/client/android/divkit-demo-app/src/debug/AndroidManifest.xml", "client/android/divkit-demo-app/src/main/AndroidManifest.xml":"divkit/public/client/android/divkit-demo-app/src/main/AndroidManifest.xml", "client/android/divkit-demo-app/src/main/assets/application/demo.json":"divkit/public/client/android/divkit-demo-app/src/main/assets/application/demo.json", @@ -2007,7 +2003,6 @@ "client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/DivkitDemoPermissionHelper.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/DivkitDemoPermissionHelper.kt", "client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/DivkitDemoUriHandler.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/DivkitDemoUriHandler.kt", "client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/HttpUtil.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/HttpUtil.kt", - "client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/JsonAssetReader.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/JsonAssetReader.kt", "client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/MetricaUtils.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/MetricaUtils.kt", "client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/QRCaptureActivity.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/QRCaptureActivity.kt", "client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/Toasts.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/Toasts.kt", @@ -17187,6 +17182,7 @@ "client/android/test-utils/src/main/kotlin/com/yandex/div/test/crossplatform/ParsingResult.kt":"divkit/public/client/android/test-utils/src/main/kotlin/com/yandex/div/test/crossplatform/ParsingResult.kt", "client/android/test-utils/src/main/kotlin/com/yandex/div/test/crossplatform/ParsingUtils.kt":"divkit/public/client/android/test-utils/src/main/kotlin/com/yandex/div/test/crossplatform/ParsingUtils.kt", "client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivActionUtils.kt":"divkit/public/client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivActionUtils.kt", + "client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivBackgroundUtils.kt":"divkit/public/client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivBackgroundUtils.kt", "client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivContainerUtils.kt":"divkit/public/client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivContainerUtils.kt", "client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivDataUtils.kt":"divkit/public/client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivDataUtils.kt", "client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivTextUtils.kt":"divkit/public/client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivTextUtils.kt", diff --git a/client/android/compose-interop/build.gradle b/client/android/compose-interop/build.gradle index e407bb62d..cfae54abe 100644 --- a/client/android/compose-interop/build.gradle +++ b/client/android/compose-interop/build.gradle @@ -26,10 +26,7 @@ dependencies { implementation platform(libs.androidx.compose.bom) implementation libs.androidx.compose.foundation - implementation libs.androidx.compose.ui.tooling.preview implementation libs.androidx.core - - debugImplementation libs.androidx.compose.ui.tooling } tasks.withType(KotlinCompile).configureEach { diff --git a/client/android/compose/build.gradle.kts b/client/android/compose/build.gradle.kts index 1b287bc3f..2625cb28a 100644 --- a/client/android/compose/build.gradle.kts +++ b/client/android/compose/build.gradle.kts @@ -9,6 +9,10 @@ apply(from = "../div-tests.gradle") android { namespace = "com.yandex.div.compose" + + defaultConfig { + minSdk = 23 + } } dependencies { @@ -19,7 +23,6 @@ dependencies { implementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.compose.material3) - implementation(libs.androidx.compose.ui.tooling.preview) implementation(libs.androidx.coreKtx) implementation(libs.coil.compose) implementation(libs.coil.network) @@ -29,6 +32,9 @@ dependencies { debugImplementation(libs.androidx.compose.ui.tooling) + androidTestImplementation(project(":test-utils")) + androidTestImplementation(libs.androidx.compose.ui.tooling.preview) + testImplementation(project(":test-utils")) testImplementation(libs.androidx.compose.ui.test.junit4) testImplementation(libs.androidx.compose.ui.test.manifest) diff --git a/client/android/compose/src/androidTest/kotlin/com/yandex/div/compose/DivViewPreview.kt b/client/android/compose/src/androidTest/kotlin/com/yandex/div/compose/DivViewPreview.kt new file mode 100644 index 000000000..db23b11ef --- /dev/null +++ b/client/android/compose/src/androidTest/kotlin/com/yandex/div/compose/DivViewPreview.kt @@ -0,0 +1,62 @@ +package com.yandex.div.compose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.tooling.preview.Preview +import com.yandex.div.test.data.action +import com.yandex.div.test.data.color +import com.yandex.div.test.data.constant +import com.yandex.div.test.data.container +import com.yandex.div.test.data.data +import com.yandex.div.test.data.expression +import com.yandex.div.test.data.setVariableAction +import com.yandex.div.test.data.solidBackground +import com.yandex.div.test.data.text +import com.yandex.div.test.data.typedValue +import com.yandex.div.test.data.variable +import com.yandex.div2.DivEdgeInsets + +@Preview +@Composable +fun DivViewPreview() { + val divContext = DivComposeConfiguration() + .createContext(baseContext = LocalContext.current) + CompositionLocalProvider(LocalContext provides divContext) { + DivView(data = testData) + } +} + +private val testData = data( + content = container( + backgrounds = listOf( + solidBackground(color = color(0xFF90C090)) + ), + items = listOf( + text( + action = action( + typed = setVariableAction( + name = "title", + value = typedValue("New Title") + ) + ), + fontSize = 36, + text = expression("@{title}"), + textColor = color(0xFFBF0000) + ) + ), + margins = DivEdgeInsets( + top = constant(10), + bottom = constant(10) + ), + paddings = DivEdgeInsets( + start = constant(10), + end = constant(10), + top = constant(20), + bottom = constant(20) + ) + ), + variables = listOf( + variable(name = "title", value = "Hello!") + ) +) diff --git a/client/android/compose/src/main/kotlin/com/yandex/div/compose/DivViewPreview.kt b/client/android/compose/src/main/kotlin/com/yandex/div/compose/DivViewPreview.kt deleted file mode 100644 index 879258da3..000000000 --- a/client/android/compose/src/main/kotlin/com/yandex/div/compose/DivViewPreview.kt +++ /dev/null @@ -1,107 +0,0 @@ -package com.yandex.div.compose - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.tooling.preview.Preview -import com.yandex.div.internal.parser.TYPE_HELPER_STRING -import com.yandex.div.json.ParsingErrorLogger -import com.yandex.div.json.expressions.Expression -import com.yandex.div2.Div -import com.yandex.div2.DivAction -import com.yandex.div2.DivActionSetVariable -import com.yandex.div2.DivActionTyped -import com.yandex.div2.DivBackground -import com.yandex.div2.DivContainer -import com.yandex.div2.DivData -import com.yandex.div2.DivEdgeInsets -import com.yandex.div2.DivSolidBackground -import com.yandex.div2.DivText -import com.yandex.div2.DivTypedValue -import com.yandex.div2.DivVariable -import com.yandex.div2.StrValue -import com.yandex.div2.StrVariable - -@Preview -@Composable -private fun DivViewPreview() { - val divContext = DivComposeConfiguration() - .createContext(baseContext = LocalContext.current) - CompositionLocalProvider(LocalContext provides divContext) { - DivView(data = testData) - } -} - -private val testData = DivData( - logId = "preview", - variables = listOf( - DivVariable.Str( - StrVariable( - name = "title", - value = constant("Hello!") - ) - ) - ), - states = listOf( - DivData.State( - stateId = 0, - div = Div.Container( - value = DivContainer( - orientation = constant(DivContainer.Orientation.VERTICAL), - paddings = DivEdgeInsets( - start = constant(10), - end = constant(10), - top = constant(20), - bottom = constant(20) - ), - margins = DivEdgeInsets( - top = constant(10), - bottom = constant(10) - ), - background = listOf( - DivBackground.Solid( - value = DivSolidBackground( - color = constant(0xFF909090.toInt()), - ) - ) - ), - items = listOf( - Div.Text( - value = DivText( - action = DivAction( - logId = constant("test"), - typed = DivActionTyped.SetVariable( - DivActionSetVariable( - variableName = constant("title"), - value = DivTypedValue.Str( - StrValue(value = constant("New Title")) - ) - ) - ) - ), - fontSize = constant(36), - text = expression("@{title}"), - textColor = constant(0xFFBF0000.toInt()), - ) - ) - ) - ) - ) - ) - ) -) - -private fun constant(value: T): Expression { - return Expression.ConstantExpression(value) -} - -private fun expression(expression: String): Expression { - return Expression.MutableExpression( - expressionKey = "test", - rawExpression = expression, - converter = null, - validator = { true }, - logger = ParsingErrorLogger.ASSERT, - typeHelper = TYPE_HELPER_STRING - ) -} diff --git a/client/android/div/src/test/java/com/yandex/div/core/DivPreloaderTest.kt b/client/android/div/src/test/java/com/yandex/div/core/DivPreloaderTest.kt index 2528b6b05..7618f1369 100644 --- a/client/android/div/src/test/java/com/yandex/div/core/DivPreloaderTest.kt +++ b/client/android/div/src/test/java/com/yandex/div/core/DivPreloaderTest.kt @@ -44,7 +44,7 @@ class DivPreloaderTest { private val extensionHandlers = listOf(mock(), mock()) private val extensionHandlersController = DivExtensionController(extensionHandlers) - private val divText = text(text = constant("test")) + private val divText = text(text = "test") private val custom = DivCustom(customType = "test") private val divCustom = Div.Custom(custom) diff --git a/client/android/div/src/test/java/com/yandex/div/core/util/DivWalkTreeTest.kt b/client/android/div/src/test/java/com/yandex/div/core/util/DivWalkTreeTest.kt index e9d587ad9..ab2d2b8de 100644 --- a/client/android/div/src/test/java/com/yandex/div/core/util/DivWalkTreeTest.kt +++ b/client/android/div/src/test/java/com/yandex/div/core/util/DivWalkTreeTest.kt @@ -4,10 +4,10 @@ import android.net.Uri import com.yandex.div.core.asExpression import com.yandex.div.core.mockExpressionResolver import com.yandex.div.test.data.container +import com.yandex.div.test.data.text import com.yandex.div2.Div import com.yandex.div2.DivGallery import com.yandex.div2.DivImage -import com.yandex.div2.DivText import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @@ -20,7 +20,7 @@ class DivWalkTreeTest { @Test fun `walking single node hierarchy`() { - val rootDiv = divText("lorem ipsum") + val rootDiv = text(text = "lorem ipsum") val divWalk = rootDiv.walk(resolver) .map { item -> item.div.type } @@ -31,8 +31,8 @@ class DivWalkTreeTest { @Test fun `walking multiple node hierarchy`() { val rootDiv = container( - listOf( - divText("lorem ipsum"), + items = listOf( + text(text = "lorem ipsum"), divImage("https://none") ) ) @@ -46,14 +46,14 @@ class DivWalkTreeTest { @Test fun `walking uses depth-first order`() { val rootDiv = container( - listOf( + items = listOf( container( - listOf( - divText("lorem ipsum"), + items = listOf( + text(text = "lorem ipsum"), divImage("https://none") ) ), - divText("lorem ipsum") + text(text = "lorem ipsum") ) ) @@ -66,14 +66,14 @@ class DivWalkTreeTest { @Test fun `onEnter excludes subtree from walking`() { val rootDiv = container( - listOf( + items = listOf( divGallery( listOf( - divText("lorem ipsum"), + text(text = "lorem ipsum"), divImage("https://none") ) ), - divText("lorem ipsum") + text(text = "lorem ipsum") ) ) @@ -87,11 +87,11 @@ class DivWalkTreeTest { @Test fun `maxDepth limits walk depth`() { val rootDiv = container( - listOf( - divText("lorem ipsum"), + items = listOf( + text(text = "lorem ipsum"), container( - listOf( - divText("lorem ipsum"), + items = listOf( + text(text = "lorem ipsum"), divImage("https://none") ) ) @@ -108,11 +108,11 @@ class DivWalkTreeTest { @Test fun `onEnter() called only for branch nodes`() { val rootDiv = container( - listOf( - divText("lorem ipsum"), + items = listOf( + text(text = "lorem ipsum"), divGallery( listOf( - divText("lorem ipsum"), + text(text = "lorem ipsum"), divImage("https://none") ) ) @@ -133,11 +133,11 @@ class DivWalkTreeTest { @Test fun `onLeave() called only for branch nodes`() { val rootDiv = container( - listOf( - divText("lorem ipsum"), + items = listOf( + text(text = "lorem ipsum"), divGallery( listOf( - divText("lorem ipsum"), + text(text = "lorem ipsum"), divImage("https://none") ) ) @@ -152,10 +152,6 @@ class DivWalkTreeTest { assertEquals(listOf("gallery", "container"), leftDivs) } - private fun divText(text: String): Div { - return Div.Text(DivText(text = text.asExpression())) - } - private fun divImage(imageUrl: String): Div { return Div.Image(DivImage(imageUrl = Uri.parse(imageUrl).asExpression())) } diff --git a/client/android/div/src/test/java/com/yandex/div/core/view2/DivVisibilityActionTrackerTest.kt b/client/android/div/src/test/java/com/yandex/div/core/view2/DivVisibilityActionTrackerTest.kt index d33a1e42d..e57e34baf 100644 --- a/client/android/div/src/test/java/com/yandex/div/core/view2/DivVisibilityActionTrackerTest.kt +++ b/client/android/div/src/test/java/com/yandex/div/core/view2/DivVisibilityActionTrackerTest.kt @@ -66,11 +66,11 @@ class DivVisibilityActionTrackerTest { ) }.toList() - private val div1 = text(text = constant("test1"), visibilityActions = listOf(action1)) - private val div2 = text(text = constant("test2"), visibilityActions = listOf(action2)) - private val div3 = text(text = constant("test3"), visibilityActions = lottaActions) - private val div4 = text(text = constant("test4"), visibilityActions = actionsWithThreeDifferentDelays) - private val div5 = text(text = constant("test5"), visibilityActions = listOf(action1), disappearActions = listOf(disappearAction1)) + private val div1 = text(text = "test1", visibilityActions = listOf(action1)) + private val div2 = text(text = "test2", visibilityActions = listOf(action2)) + private val div3 = text(text = "test3", visibilityActions = lottaActions) + private val div4 = text(text = "test4", visibilityActions = actionsWithThreeDifferentDelays) + private val div5 = text(text = "test5", visibilityActions = listOf(action1), disappearActions = listOf(disappearAction1)) private val visibilityActionTracker = DivVisibilityActionTracker( viewVisibilityCalculator, diff --git a/client/android/div/src/test/java/com/yandex/div/core/view2/animations/DivComparatorTest.kt b/client/android/div/src/test/java/com/yandex/div/core/view2/animations/DivComparatorTest.kt index fb30620c0..fdd50d240 100644 --- a/client/android/div/src/test/java/com/yandex/div/core/view2/animations/DivComparatorTest.kt +++ b/client/android/div/src/test/java/com/yandex/div/core/view2/animations/DivComparatorTest.kt @@ -3,9 +3,9 @@ package com.yandex.div.core.view2.animations import android.net.Uri import com.yandex.div.core.asExpression import com.yandex.div.core.mockExpressionResolver +import com.yandex.div.test.data.container import com.yandex.div2.Div import com.yandex.div2.DivAppearanceTransition -import com.yandex.div2.DivContainer import com.yandex.div2.DivCustom import com.yandex.div2.DivGallery import com.yandex.div2.DivGrid @@ -83,48 +83,48 @@ class DivComparatorTest { @Test fun `containers with similar hierarchy are replaceable`() { - val div1 = divContainer(items = listOf(divText(text = "Text 01"))) - val div2 = divContainer(items = listOf(divText(text = "Text 02"))) + val div1 = container(items = listOf(divText(text = "Text 01"))) + val div2 = container(items = listOf(divText(text = "Text 02"))) assertTrue(DivComparator.areDivsReplaceable(div1, div2, resolver, resolver)) } @Test fun `containers with different child count are not replaceable`() { - val div1 = divContainer(items = listOf(divText(text = "Text 01"), divImage(imageUrl = "https://image"))) - val div2 = divContainer(items = listOf(divText(text = "Text 02"))) + val div1 = container(items = listOf(divText(text = "Text 01"), divImage(imageUrl = "https://image"))) + val div2 = container(items = listOf(divText(text = "Text 02"))) assertFalse(DivComparator.areDivsReplaceable(div1, div2, resolver, resolver)) } @Test fun `containers with different child types are not replaceable`() { - val div1 = divContainer(items = listOf(divImage(imageUrl = "https://image"))) - val div2 = divContainer(items = listOf(divText(text = "Text"))) + val div1 = container(items = listOf(divImage(imageUrl = "https://image"))) + val div2 = container(items = listOf(divText(text = "Text"))) assertFalse(DivComparator.areDivsReplaceable(div1, div2, resolver, resolver)) } @Test fun `containers with different child order are not replaceable`() { - val div1 = divContainer(items = listOf(divImage(imageUrl = "https://image"), divText(text = "Text 01"))) - val div2 = divContainer(items = listOf(divText(text = "Text 02"), divImage(imageUrl = "https://image"))) + val div1 = container(items = listOf(divImage(imageUrl = "https://image"), divText(text = "Text 01"))) + val div2 = container(items = listOf(divText(text = "Text 02"), divImage(imageUrl = "https://image"))) assertFalse(DivComparator.areDivsReplaceable(div1, div2, resolver, resolver)) } @Test fun `containers with different child ids are replaceable`() { - val div1 = divContainer(items = listOf(divText(id = "01", text = "Text 01"))) - val div2 = divContainer(items = listOf(divText(id = "02", text = "Text 02"))) + val div1 = container(items = listOf(divText(id = "01", text = "Text 01"))) + val div2 = container(items = listOf(divText(id = "02", text = "Text 02"))) assertTrue(DivComparator.areDivsReplaceable(div1, div2, resolver, resolver)) } @Test fun `containers which children has transitions and different ids are not replaceable`() { - val div1 = divContainer(items = listOf(divText(id = "01", text = "Text 01", transitionIn = transitionMock))) - val div2 = divContainer(items = listOf(divText(id = "02", text = "Text 02", transitionIn = transitionMock))) + val div1 = container(items = listOf(divText(id = "01", text = "Text 01", transitionIn = transitionMock))) + val div2 = container(items = listOf(divText(id = "02", text = "Text 02", transitionIn = transitionMock))) assertFalse(DivComparator.areDivsReplaceable(div1, div2, resolver, resolver)) } @@ -235,20 +235,6 @@ class DivComparatorTest { return Div.Custom(DivCustom(customType = type, transitionIn = transitionIn)) } - private fun divContainer( - orientation: DivContainer.Orientation = DivContainer.Orientation.VERTICAL, - items: List
, - transitionIn: DivAppearanceTransition? = null - ): Div { - return Div.Container( - DivContainer( - orientation = orientation.asExpression(), - items = items, - transitionIn = transitionIn - ) - ) - } - private fun divGrid(items: List
, transitionIn: DivAppearanceTransition? = null): Div { return Div.Grid( DivGrid( diff --git a/client/android/divkit-demo-app/build.gradle b/client/android/divkit-demo-app/build.gradle index 8894a983e..ee7026a48 100644 --- a/client/android/divkit-demo-app/build.gradle +++ b/client/android/divkit-demo-app/build.gradle @@ -25,6 +25,8 @@ android { } defaultConfig { + minSdk = 23 + versionCode = divkitVersion.versionCode + divkitVersion.buildNumber buildConfigField "int", "BUILD_NUMBER", "${divkitVersion.buildNumber}" buildConfigField "String", "METRICA_API_KEY", JsonOutput.toJson(providers.gradleProperty("metricaApiKey").getOrElse("")) diff --git a/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/UiTestDivActionHandler.kt b/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/UiTestDivActionHandler.kt deleted file mode 100644 index 5dfd865c5..000000000 --- a/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/utils/UiTestDivActionHandler.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.yandex.div.utils - -import android.net.Uri -import com.yandex.div.core.DivActionHandler -import com.yandex.div.core.DivViewFacade -import com.yandex.div.json.expressions.ExpressionResolver -import com.yandex.div2.DivAction - -private const val DIV_UI_TEST_SCHEME = "ui-test" -private const val CLICK_ACTION_HOST = "click" -private const val CLICK_TYPE_PARAM = "type" -private const val CLICK_DESCRIPTION_PARAM = "description" - -class UiTestDivActionHandler( - private val onClick: ((clickType: String, description: String) -> Unit)? = null -) : DivActionHandler() { - override fun handleAction(action: DivAction, view: DivViewFacade, resolver: ExpressionResolver): Boolean { - val url = action.url?.evaluate(resolver) ?: return false - if (url.scheme == DIV_UI_TEST_SCHEME) { - handleDemoActionUrl(url, view) - return true - } - return super.handleAction(action, view, resolver) - } - - private fun handleDemoActionUrl(url: Uri, view: DivViewFacade) { - if (url.host == CLICK_ACTION_HOST) { - val clickType = url.getQueryParameter(CLICK_TYPE_PARAM) - val description = url.getQueryParameter(CLICK_DESCRIPTION_PARAM) - if (clickType != null && description != null) { - onClick?.invoke(clickType, description) - } - } - } -} diff --git a/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/TextView.kt b/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/TextView.kt deleted file mode 100644 index 3fd4cac4e..000000000 --- a/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/TextView.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.yandex.div.view - -import android.text.Spanned -import android.text.style.ClickableSpan -import android.view.View -import android.widget.EditText -import android.widget.TextView -import androidx.core.text.getSpans -import androidx.test.espresso.UiController -import androidx.test.espresso.ViewAction -import androidx.test.espresso.matcher.ViewMatchers.assertThat -import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed -import org.hamcrest.Matcher -import org.hamcrest.Matchers.allOf -import org.hamcrest.Matchers.notNullValue - -object TextViewActions { - - /** - * Clear text in TextView - */ - fun clearText(): ViewAction { - return object : ViewAction { - - override fun getConstraints(): Matcher = allOf(isDisplayed(), isAssignableFrom(EditText::class.java)) - - override fun getDescription(): String = "Delete all text in EditText" - - override fun perform(uiController: UiController, view: View) { - uiController.loopMainThreadUntilIdle() - (view as EditText).text.clear() - } - } - } - - /** - * Taps a first found link if it is recognized. - */ - fun tapLink(): ViewAction { - return object : ViewAction { - - override fun getConstraints(): Matcher = allOf(isDisplayed(), isAssignableFrom(TextView::class.java)) - - override fun getDescription(): String = "Tap on link inside text" - - override fun perform(uiController: UiController, view: View) { - val spannable = (view as TextView).text as? Spanned - assertThat("Unknown text format found in $spannable", spannable, notNullValue()) - val span = spannable?.getSpans()?.firstOrNull() - assertThat("No link found in $spannable", spannable, notNullValue()) - span?.onClick(view) - } - } - } -} diff --git a/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewActions.kt b/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewActions.kt index e8480c252..8379244ec 100644 --- a/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewActions.kt +++ b/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewActions.kt @@ -2,52 +2,22 @@ package com.yandex.div.view -import android.view.InputDevice -import android.view.MotionEvent import android.view.View -import androidx.annotation.IdRes import androidx.test.espresso.Espresso.onView import androidx.test.espresso.NoMatchingViewException -import androidx.test.espresso.PerformException import androidx.test.espresso.UiController import androidx.test.espresso.ViewAction import androidx.test.espresso.ViewInteraction -import androidx.test.espresso.action.GeneralClickAction -import androidx.test.espresso.action.GeneralLocation -import androidx.test.espresso.action.Press -import androidx.test.espresso.action.Tap -import androidx.test.espresso.action.ViewActions -import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom import androidx.test.espresso.matcher.ViewMatchers.isClickable -import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.isRoot -import androidx.test.espresso.util.HumanReadables import androidx.test.espresso.util.TreeIterables import com.yandex.div.view.ViewMatchers.isDisplayedForClicking import org.hamcrest.Matcher import org.hamcrest.Matchers.allOf import java.lang.Thread.sleep -import java.util.concurrent.TimeoutException object ViewActions { - /** - * Performs click on view with very light constraints. Use only if there is no other way - */ - fun callOnClickAction(): ViewAction { - return object : ViewAction { - - override fun getConstraints(): Matcher = allOf(isClickable(), isDisplayed()) - - override fun getDescription() = "Call view onClick directly" - - override fun perform(uiController: UiController, view: View) { - view.callOnClick() - } - } - } - /** * Performs click ignoring 90% visible area constraint */ @@ -64,98 +34,8 @@ object ViewActions { } } - /** - * Performs an action on a child view. - */ - fun actionOnChild(@IdRes id: Int, action: ViewAction): ViewAction { - return object : ViewAction { - - override fun getConstraints(): Matcher = allOf(isDisplayedForClicking(), isClickable()) - - override fun getDescription() = "Performs '$action' on a child view with id='$id'" - - override fun perform(uiController: UiController, view: View) { - val child = view.findViewById(id) - ?: throw PerformException.Builder() - .withActionDescription(this.description) - .withViewDescription(HumanReadables.describe(view)) - .withCause(RuntimeException("View with id='$id' not found in '$view'")) - .build() - action.perform(uiController, child) - } - } - } - - fun clickAtRight(): ViewAction = ViewActions.actionWithAssertions( - GeneralClickAction( - Tap.SINGLE, - GeneralLocation.CENTER_RIGHT, - Press.FINGER, - InputDevice.SOURCE_UNKNOWN, - MotionEvent.BUTTON_PRIMARY - ) - ) - const val WAITING_TIMEOUT = 15_000L const val WAITING_TIMEOUT_PER_TRY = 100L - const val MAIN_THREAD_LOOP_TIME_MS = 50L - - /** - * Wait for view is completely displayed and perform action - */ - fun waitForCompletelyDisplayedAndPerformAction( - targetAction: ViewAction? = null, - timeoutInMillis: Long = WAITING_TIMEOUT, - ) = waitForViewConditionAndPerformAction( - description = "Waits view to become completely displayed.", - timeoutInMillis = timeoutInMillis, - condition = { view -> isCompletelyDisplayed().matches(view) }, - targetAction = targetAction, - ) - - inline fun waitForViewConditionAndPerformAction( - description: String, - targetAction: ViewAction? = null, - timeoutInMillis: Long = WAITING_TIMEOUT, - condition: Matcher, - ) = waitForViewConditionAndPerformAction(description, targetAction, timeoutInMillis, - { view: ViewType -> condition.matches(view) } - ) - - inline fun waitForViewConditionAndPerformAction( - description: String, - targetAction: ViewAction? = null, - timeoutInMillis: Long = WAITING_TIMEOUT, - crossinline condition: (view: ViewType) -> Boolean, - ): ViewAction { - return object : ViewAction { - - override fun getConstraints(): Matcher = isAssignableFrom(ViewType::class.java) - - override fun getDescription() = description - - override fun perform(uiController: UiController, view: View) { - uiController.loopMainThreadUntilIdle() - val startTime = System.currentTimeMillis() - val endTime = startTime + timeoutInMillis - - do { - if (condition(view as ViewType)) { - targetAction?.perform(uiController, view) - return - } - - uiController.loopMainThreadForAtLeast(MAIN_THREAD_LOOP_TIME_MS) - } while (System.currentTimeMillis() < endTime) - - throw PerformException.Builder() - .withActionDescription(this.description) - .withViewDescription(HumanReadables.describe(view)) - .withCause(TimeoutException()) - .build() - } - } - } fun waitForView( viewMatcher: Matcher, diff --git a/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewAssertions.kt b/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewAssertions.kt index b50572511..067fb2144 100644 --- a/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewAssertions.kt +++ b/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewAssertions.kt @@ -1,41 +1,15 @@ package com.yandex.div.view -import android.view.View -import androidx.test.espresso.ViewAssertion import androidx.test.espresso.ViewInteraction import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.util.HumanReadables import com.yandex.div.view.ViewMatchers.positionMatch import com.yandex.test.util.Report.step -import org.hamcrest.MatcherAssert.assertThat /** * Provides assertions for all types of views. * */ object ViewAssertions { - fun doesNotExistOrGone(): ViewAssertion { - return ViewAssertion { view, _ -> - view?.let { - assertThat( - "View is visible or exists: ${HumanReadables.describe(view)}", - view.visibility == View.GONE || view.visibility == View.INVISIBLE - ) - } - } - } - - fun doesExistAndVisible(): ViewAssertion { - return ViewAssertion { view, ex -> - view?.let { - assertThat( - "View is invisible: ${HumanReadables.describe(view)}", - view.visibility == View.VISIBLE - ) - } ?: throw ViewDoesNotExistsException("View does not exist: ${ex.viewMatcherDescription}") - } - } - fun hasChangedPosition(view: ViewInteraction, lastPosition: FloatArray): Unit = step("Assert view has changed position") { checkPosition(shouldMatch = false, view, lastPosition) @@ -52,5 +26,3 @@ object ViewAssertions { lastPosition: FloatArray ) = view.check(matches(positionMatch(shouldMatch, lastPosition))) } - -class ViewDoesNotExistsException(message: String) : RuntimeException(message) diff --git a/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewInteraction.kt b/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewInteraction.kt index 8e866b800..4a09020d0 100644 --- a/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewInteraction.kt +++ b/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/ViewInteraction.kt @@ -1,37 +1,17 @@ package com.yandex.div.view -import android.view.KeyEvent -import android.view.View import androidx.recyclerview.widget.RecyclerView.ViewHolder import androidx.test.espresso.NoMatchingViewException -import androidx.test.espresso.UiController -import androidx.test.espresso.ViewAction import androidx.test.espresso.ViewInteraction -import androidx.test.espresso.action.GeneralLocation -import androidx.test.espresso.action.GeneralSwipeAction -import androidx.test.espresso.action.Press -import androidx.test.espresso.action.Swipe import androidx.test.espresso.action.ViewActions -import androidx.test.espresso.action.ViewActions.typeText -import androidx.test.espresso.assertion.PositionAssertions import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition import androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition import androidx.test.espresso.matcher.ViewMatchers -import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed -import com.yandex.div.view.ViewActions.callOnClickAction import com.yandex.div.view.ViewActions.clickOnPartlyVisibleView -import com.yandex.div.view.ViewActions.waitForCompletelyDisplayedAndPerformAction -import com.yandex.div.view.ViewAssertions.doesExistAndVisible -import com.yandex.div.view.ViewAssertions.doesNotExistOrGone import com.yandex.div.view.actions.ExtendedScrollTo -import junit.framework.AssertionFailedError -import org.hamcrest.Matcher fun ViewInteraction.checkIsDisplayed() = check(matches(ViewMatchers.isDisplayed())) -fun ViewInteraction.checkIsChecked() = check(matches(ViewMatchers.isChecked())) -fun ViewInteraction.checkIsNotChecked() = check(matches(ViewMatchers.isNotChecked())) fun ViewInteraction.whenDisplayed(): ViewInteraction? { return try { @@ -45,42 +25,8 @@ fun ViewInteraction.isDisplayed(): Boolean { return whenDisplayed() != null } -fun ViewInteraction.whenChecked(): ViewInteraction? { - return try { - checkIsChecked() - } catch (e: AssertionFailedError) { - null - } -} - -fun ViewInteraction.whenMatches(matcher: Matcher): ViewInteraction? { - return try { - check(matches(matcher)) - } catch (e: AssertionFailedError) { - null - } -} - -fun ViewInteraction.isMatching(matcher: Matcher): Boolean { - return whenMatches(matcher) != null -} - -fun ViewInteraction.whenNotChecked(): ViewInteraction? { - return try { - checkIsNotChecked() - } catch (e: AssertionFailedError) { - null - } -} - fun ViewInteraction.checkNotExist() = check(doesNotExist()) -fun ViewInteraction.checkIsAbove(matcher: Matcher) = check( - PositionAssertions.isCompletelyAbove( - matcher - ) -) - fun ViewInteraction.click() = perform(clickOnPartlyVisibleView()) fun ViewInteraction.tap() = perform(ViewActions.click()) @@ -89,133 +35,6 @@ fun ViewInteraction.scrollTo() = perform(ExtendedScrollTo()) fun ViewInteraction.scrollTo(position: Int) = perform(scrollToPosition(position)) -fun ViewInteraction.callOnClick() = perform(callOnClickAction()) - -fun ViewInteraction.clearText() = perform(ViewActions.clearText()) - -fun ViewInteraction.deleteText() = perform(TextViewActions.clearText()) - -fun ViewInteraction.inputText(text: String) = perform(typeText(text)) - -fun ViewInteraction.replaceText(text: String) = perform(ViewActions.replaceText(text)) - -fun ViewInteraction.clickRecyclerViewItem(index: Int) = - perform(actionOnItemAtPosition(index, ViewActions.click())) - fun ViewInteraction.swipeRight() = perform(ViewActions.swipeRight()) fun ViewInteraction.swipeLeft() = perform(ViewActions.swipeLeft()) fun ViewInteraction.swipeUp() = perform(ViewActions.swipeUp()) -fun ViewInteraction.swipeDown() = perform(ViewActions.swipeDown()) - -fun ViewInteraction.waitForCompletelyDisplayedAndCallOnClick() = - perform(waitForCompletelyDisplayedAndPerformAction(callOnClickAction())) - -fun ViewInteraction.waitForCompletelyDisplayedAndClick() = - perform(waitForCompletelyDisplayedAndPerformAction(ViewActions.click())) - -fun ViewInteraction.waitForCompletelyDisplayedAndLongClick() = - perform(waitForCompletelyDisplayedAndPerformAction(ViewActions.longClick())) - -fun ViewInteraction.pressSpace() = perform(ViewActions.pressKey(KeyEvent.KEYCODE_SPACE)) - -fun ViewInteraction.pressEnter() = perform(ViewActions.pressKey(KeyEvent.KEYCODE_ENTER)) - -fun ViewInteraction.pressBackButton() = perform(ViewActions.pressBack()) - -fun ViewInteraction.swipeLeftCenterToRightCenter() = perform( - waitForCompletelyDisplayedAndPerformAction( - GeneralSwipeAction( - Swipe.FAST, GeneralLocation.CENTER_LEFT, - GeneralLocation.CENTER_RIGHT, Press.FINGER - ) - ) -) - -fun ViewInteraction.swipeRightCenterToLeftCenter() = perform( - waitForCompletelyDisplayedAndPerformAction( - GeneralSwipeAction( - Swipe.FAST, GeneralLocation.CENTER_RIGHT, - GeneralLocation.CENTER_LEFT, Press.FINGER - ) - ) -) - -fun ViewInteraction.swipeTopCenterToBottomCenter() = perform( - waitForCompletelyDisplayedAndPerformAction( - GeneralSwipeAction( - Swipe.FAST, GeneralLocation.TOP_CENTER, - GeneralLocation.BOTTOM_CENTER, Press.FINGER - ) - ) -) - -fun ViewInteraction.swipeFromCenterToBottomCenter(swipeSpeed: Swipe = Swipe.SLOW) = perform( - waitForCompletelyDisplayedAndPerformAction( - GeneralSwipeAction( - swipeSpeed, - GeneralLocation.CENTER, - GeneralLocation.BOTTOM_CENTER, Press.FINGER - ) - ) -) - -fun ViewInteraction.swipeFromCenterToTopCenter(swipeSpeed: Swipe = Swipe.SLOW) = perform( - waitForCompletelyDisplayedAndPerformAction( - GeneralSwipeAction( - swipeSpeed, - GeneralLocation.CENTER, - GeneralLocation.TOP_CENTER, Press.FINGER - ) - ) -) - -fun ViewInteraction.waitForCompletelyDisplayed( - timeoutInMillis: Long? = null -): ViewInteraction { - return if (timeoutInMillis == null) - perform(waitForCompletelyDisplayedAndPerformAction()) - else perform( - waitForCompletelyDisplayedAndPerformAction( - timeoutInMillis = timeoutInMillis - ) - ) -} - -fun ViewInteraction.waitMainThreadBecomeIdle(): ViewInteraction { - return perform(object : ViewAction { - - override fun getDescription(): String = - "Wait while main thread become idle on displayed view" - - override fun getConstraints(): Matcher = ViewMatchers.isDisplayed() - - override fun perform(uiController: UiController, view: View) = - uiController.loopMainThreadUntilIdle() - }) -} - -fun ViewInteraction.checkExistsAndVisible() = check(doesExistAndVisible()) - -fun ViewInteraction.checkExistsAndCompletelyVisible() = check(matches(isCompletelyDisplayed())) - -fun ViewInteraction.checkNotExistsOrGone() = check(doesNotExistOrGone()) - - -fun ViewInteraction.configure(block: (T) -> Unit): ViewInteraction { - return perform(ViewActions.actionWithAssertions(ConfigureViewAction(block))) -} - -private class ConfigureViewAction( - private val block: (T) -> Unit -) : ViewAction { - - override fun getDescription() = "ConfigureViewAction" - - override fun getConstraints(): Matcher = ViewMatchers.isDisplayed() - - @Suppress("UNCHECKED_CAST") - override fun perform(uiController: UiController, view: View) { - val receiver = view as? T ?: return - block(receiver) - } -} diff --git a/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/getThemeColor.kt b/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/getThemeColor.kt deleted file mode 100644 index 528534885..000000000 --- a/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/getThemeColor.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.yandex.div.view - -import android.content.Context -import android.util.TypedValue -import androidx.annotation.AttrRes -import androidx.annotation.ColorInt - -/** - * Returns the color int for the provided theme color attribute. - * - * @throws IllegalArgumentException if the attribute is not set in the current theme. - */ -@ColorInt -fun Context.getThemeColor(@AttrRes attribute: Int): Int { - val typedValue = resolveAttributeOrThrow(attribute) - return typedValue.data -} - -fun Context.getThemeBoolean(@AttrRes attribute: Int): Boolean { - return resolveAttributeOrThrow(attribute).data != 0 -} - -fun Context.getThemeResource(@AttrRes attribute: Int): Int { - return resolveAttributeOrThrow(attribute).resourceId -} - -internal fun Context.resolveAttributeOrThrow(@AttrRes attributeResId: Int): TypedValue { - val typedValue = TypedValue() - - val isAttrResolved = theme.resolveAttribute(attributeResId, typedValue, true) - if (isAttrResolved) { - return typedValue - } - - val errorMessage = ("%1\$s requires a value for the %2\$s attribute to be set in your theme.") - throw java.lang.IllegalArgumentException( - String.format( - errorMessage, - javaClass.canonicalName, - resources.getResourceName(attributeResId), - ) - ) -} diff --git a/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/viewHelpers.kt b/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/viewHelpers.kt deleted file mode 100644 index d10ab7622..000000000 --- a/client/android/divkit-demo-app/src/androidTest/java/com/yandex/div/view/viewHelpers.kt +++ /dev/null @@ -1,271 +0,0 @@ -package com.yandex.div.view - -import android.animation.AnimatorInflater -import android.app.Activity -import android.content.ContextWrapper -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter -import android.os.Build -import android.view.View -import android.view.ViewGroup -import android.view.ViewGroup.MarginLayoutParams -import android.widget.CompoundButton -import android.widget.TextView -import androidx.annotation.AnimatorRes -import androidx.annotation.AttrRes -import androidx.annotation.ColorInt -import androidx.annotation.ColorRes -import androidx.annotation.DimenRes -import androidx.annotation.DrawableRes -import androidx.annotation.Px -import androidx.constraintlayout.widget.ConstraintLayout -import androidx.core.content.res.ResourcesCompat -import androidx.core.view.marginBottom -import androidx.core.view.marginLeft -import androidx.core.view.marginRight -import androidx.core.view.marginTop -import com.yandex.divkit.demo.utils.coroutineScope -import com.yandex.divkit.demo.utils.noGetter -import kotlinx.coroutines.launch - -const val matchParent: Int = ViewGroup.LayoutParams.MATCH_PARENT -const val wrapContent: Int = ViewGroup.LayoutParams.WRAP_CONTENT -const val matchConstraint: Int = ConstraintLayout.LayoutParams.MATCH_CONSTRAINT - -var TextView.textColor: Int - @ColorInt get() = textColors.defaultColor - set(@ColorInt value) = setTextColor(value) - -var TextView.textColorResource: Int - get() = noGetter() - set(colorId) = setTextColor(context.resources.run { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) getColor(colorId, null) else getColor(colorId) - }) - -var TextView.textColorAttr: Int - get() = noGetter() - set(@AttrRes value) = setTextColor(context.getThemeColor(value)) - -var TextView.textColorHint: Int - get() = hintTextColors.defaultColor - set(value) = setHintTextColor(value) - -var TextView.textColorHintResource: Int - get() = noGetter() - set(colorId) = setHintTextColor(context.resources.run { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) getColor(colorId, null) else getColor(colorId) - }) - -var TextView.fontResource: Int - get() = noGetter() - set(fontId) { - typeface = ResourcesCompat.getFont(context, fontId) - } - -var TextView.lineSpacingAdd: Float - get() = lineSpacingExtra - set(value) = setLineSpacing(value, lineSpacingMultiplier) - -var TextView.lineSpacingMult: Float - get() = lineSpacingMultiplier - set(value) = setLineSpacing(lineSpacingExtra, value) - -var TextView.textResource: Int - get() = noGetter() - set(v) = setText(v) - -var TextView.hintResource: Int - get() = noGetter() - set(value) = setHint(value) - -var View.backgroundColor: Int - get() = noGetter() - set(@ColorInt value) = setBackgroundColor(value) - -var View.backgroundColorResource: Int - get() = noGetter() - set(@ColorRes value) = setBackgroundColor(ResourcesCompat.getColor(resources, value, null)) - -var View.backgroundColorAttr: Int - get() = noGetter() - set(@AttrRes value) = setBackgroundColor(context.getThemeColor(value)) - -var View.backgroundResource: Int - get() = noGetter() - set(@DrawableRes value) = setBackgroundResource(value) - - -var View.backgroundTintColor: Int - get() = noGetter() - set(@ColorInt value) { - background?.colorFilter = PorterDuffColorFilter(value, PorterDuff.Mode.SRC_ATOP) - } - -var View.backgroundTintColorResource: Int - get() = noGetter() - set(@ColorRes value) { - backgroundTintColor = ResourcesCompat.getColor(resources, value, null) - } - -fun View.onClick(value: suspend () -> Unit) { - setOnClickListener { - activity.coroutineScope.launch { value() } - } -} - -fun View.clearClickListener() = setOnClickListener(null) - -fun View.onLongClick(value: suspend () -> Unit) { - setOnLongClickListener { - activity.coroutineScope.launch { value() } - true - } -} - -var View.leftPadding: Int - get() = paddingLeft - set(value) = setPadding(value, paddingTop, paddingRight, paddingBottom) - -var View.rightPadding: Int - get() = paddingRight - set(value) = setPadding(paddingLeft, paddingTop, value, paddingBottom) - -var View.topPadding: Int - get() = paddingTop - set(value) = setPadding(paddingLeft, value, paddingRight, paddingBottom) - -var View.bottomPadding: Int - get() = paddingBottom - set(value) = setPadding(paddingLeft, paddingTop, paddingRight, value) - -var View.padding: Int - get() = noGetter() - set(value) = setPadding(value, value, value, value) - -var View.verticalPadding: Int - get() = noGetter() - set(value) = setPadding(paddingLeft, value, paddingRight, value) - -var View.horizontalPadding: Int - get() = noGetter() - set(value) = setPadding(value, paddingTop, value, paddingBottom) - -var View.stateListAnimatorResource: Int - get() = noGetter() - set(@AnimatorRes value) { - stateListAnimator = AnimatorInflater.loadStateListAnimator(context, value) - } - -var View.topMargin: Int - get() = noGetter() - set(value) { - withMarginLayoutParams { - topMargin = value - } - } - -var View.leftMargin: Int - get() = noGetter() - set(value) { - withMarginLayoutParams { - leftMargin = value - } - } - -var View.bottomMargin: Int - get() = noGetter() - set(value) { - withMarginLayoutParams { - bottomMargin = value - } - } - -var View.rightMargin: Int - get() = noGetter() - set(value) { - withMarginLayoutParams { - rightMargin = value - } - } - -var View.topMarginRes: Int - get() = noGetter() - set(@DimenRes value) { - withMarginLayoutParams { - topMargin = context.resources.getDimensionPixelSize(value) - } - } - -var View.leftMarginRes: Int - get() = noGetter() - set(@DimenRes value) { - withMarginLayoutParams { - leftMargin = context.resources.getDimensionPixelSize(value) - } - } - -var View.bottomMarginRes: Int - get() = noGetter() - set(@DimenRes value) { - withMarginLayoutParams { - bottomMargin = context.resources.getDimensionPixelSize(value) - } - } - -var View.rightMarginRes: Int - get() = noGetter() - set(@DimenRes value) { - withMarginLayoutParams { - rightMargin = context.resources.getDimensionPixelSize(value) - } - } - -fun View.updateMargins( - @Px left: Int = marginLeft, - @Px top: Int = marginTop, - @Px right: Int = marginRight, - @Px bottom: Int = marginBottom -) { - if (marginLeft == left && marginTop == top && marginBottom == bottom && marginRight == right) { - return - } - withMarginLayoutParams { - topMargin = top - bottomMargin = bottom - leftMargin = left - rightMargin = right - } -} - -inline fun View.withMarginLayoutParams(block: MarginLayoutParams.() -> Unit) = withLayoutParams(block) - -inline fun View.withLayoutParams(block: T.() -> Unit) { - (layoutParams as? T)?.let { lp -> - lp.block() - layoutParams = lp - } -} - -inline fun View.onLayoutChange(crossinline onLayoutChange: () -> Unit) = - addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> onLayoutChange() } - -inline fun CompoundButton.onCheckedChange(crossinline onCheckedChange: (Boolean) -> Unit) = - setOnCheckedChangeListener { _, isChecked -> onCheckedChange(isChecked) } - -val View.activity: Activity - get() { - when (val context = context) { - is Activity -> return context - is ContextWrapper -> { - var contextWrapper = context - while (contextWrapper is ContextWrapper) { - if (contextWrapper is Activity) { - return contextWrapper - } - contextWrapper = contextWrapper.baseContext - } - error("Unknown view context $contextWrapper") - } - else -> error("Unknown view context $context") - } - } diff --git a/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/Container.kt b/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/Container.kt index cc1205052..1e535af58 100644 --- a/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/Container.kt +++ b/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/Container.kt @@ -2,7 +2,6 @@ package com.yandex.divkit.demo import android.annotation.SuppressLint import android.content.Context -import android.os.Build import com.facebook.stetho.okhttp3.StethoInterceptor import com.neovisionaries.ws.client.WebSocketFactory import com.yandex.div.coil.CoilDivImageLoader @@ -74,11 +73,9 @@ internal object Container { .setSSLContext(NaiveSSLContext.getInstance("TLS")) .setConnectionTimeout(TimeUnit.SECONDS.toMillis(15).toInt()) .apply { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - context.connectivityManager.defaultProxy?.let { proxyInfo -> - proxySettings.host = proxyInfo.host - proxySettings.port = proxyInfo.port - } + context.connectivityManager.defaultProxy?.let { proxyInfo -> + proxySettings.host = proxyInfo.host + proxySettings.port = proxyInfo.port } } } diff --git a/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/Div2Activity.kt b/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/Div2Activity.kt index 62ddea9ea..ddf96f8fe 100644 --- a/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/Div2Activity.kt +++ b/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/Div2Activity.kt @@ -9,7 +9,6 @@ import android.content.pm.PackageManager import android.content.res.Configuration import android.graphics.Path import android.net.Uri -import android.os.Build import android.os.Bundle import android.view.View import android.view.ViewGroup @@ -26,7 +25,6 @@ import com.yandex.div.core.DivDataChangeListener import com.yandex.div.core.DivViewFacade import com.yandex.div.core.state.DivStateChangeListener import com.yandex.div.core.state.DivStateTransition -import com.yandex.div.core.util.SafeAlertDialogBuilder import com.yandex.div.core.view2.Div2View import com.yandex.div.data.Variable import com.yandex.div.font.YandexSansDisplayDivTypefaceProvider @@ -280,13 +278,7 @@ class Div2Activity : AppCompatActivity() { private fun (() -> Unit).invokeWithPermissions(vararg permissions: String) = when { checkPermissions(permissions) -> invoke() - Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> - requestPermissions(permissions, OPEN_FILE_ACTIVITY_REQUEST_CODE) - else -> SafeAlertDialogBuilder(this@Div2Activity) - .setTitle("Unable to add new template from storage: no permissions") - .setPositiveButton("Ok") { dialog, id -> - dialog.cancel() - } + else -> requestPermissions(permissions, OPEN_FILE_ACTIVITY_REQUEST_CODE) } private fun checkPermissions(permissions: Array) = diff --git a/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/DivActivity.kt b/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/DivActivity.kt index 98e4aa3a5..b8bb1a3a9 100644 --- a/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/DivActivity.kt +++ b/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/DivActivity.kt @@ -5,7 +5,6 @@ import android.content.Intent import android.content.pm.PackageManager import android.content.res.Configuration import android.graphics.Color -import android.os.Build import android.os.Bundle import android.text.TextUtils import android.view.View @@ -175,12 +174,8 @@ open class DivActivity : AppCompatActivity() { private fun (()->Unit).invokeWithPermissions(vararg permissions: String) = when { - checkPermissions(permissions) -> - invoke() - Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> - requestPermissions(permissions, REQUEST_CODE) - else -> - noPermissions() + checkPermissions(permissions) -> invoke() + else -> requestPermissions(permissions, REQUEST_CODE) } private fun checkPermissions(permissions: Array) = diff --git a/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/screenshot/DivAssetReader.kt b/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/screenshot/DivAssetReader.kt index dbcc8e65a..679e7e006 100644 --- a/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/screenshot/DivAssetReader.kt +++ b/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/screenshot/DivAssetReader.kt @@ -1,7 +1,6 @@ package com.yandex.divkit.demo.screenshot import android.content.Context -import android.os.Build import com.yandex.div.internal.util.IOUtils import org.json.JSONObject @@ -9,21 +8,6 @@ class DivAssetReader(private val context: Context) { fun read(filename: String): JSONObject { val data = IOUtils.toString(context.assets.open(filename)) - .replaceUnsupportedUnicodeSymbols() return JSONObject(data) } - - private fun String.replaceUnsupportedUnicodeSymbols(): String { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) return this - var result = this - UNSUPPORTED_UNICODE_PRE_M.forEachIndexed { index, unsupportedString -> - result = result.replace(unsupportedString, UNSUPPORTED_UNICODE_REPLACEMENTS[index]) - } - return result - } - - companion object { - private val UNSUPPORTED_UNICODE_PRE_M = arrayOf("⌜", "⌝", "⌞", "⌄", "⌟") - private val UNSUPPORTED_UNICODE_REPLACEMENTS = arrayOf("tl", "tr", "bl", "b", "br") - } } diff --git a/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/uri/CustomTabHelper.kt b/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/uri/CustomTabHelper.kt index 1e27bee42..5da11f1b4 100644 --- a/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/uri/CustomTabHelper.kt +++ b/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/uri/CustomTabHelper.kt @@ -4,7 +4,6 @@ import android.content.ActivityNotFoundException import android.content.ComponentName import android.content.Context import android.net.Uri -import android.os.Build import android.os.Parcelable import androidx.annotation.ColorInt import androidx.browser.customtabs.CustomTabsClient @@ -32,11 +31,7 @@ class CustomTabHelper(private val context: Context) { } val intentBuilder = CustomTabsIntent.Builder(customTabSession) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - intentBuilder.setToolbarColor(context.resources.getColor(R.color.colorPrimary, null)) - } else { - intentBuilder.setToolbarColor(context.resources.getColor(R.color.colorPrimary)) - } + intentBuilder.setToolbarColor(context.resources.getColor(R.color.colorPrimary, null)) if (colors != null) { colors.toolbarColor?.let { intentBuilder.setToolbarColor(it) } diff --git a/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/DivkitDemoPermissionHelper.kt b/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/DivkitDemoPermissionHelper.kt index d069e91a5..edbc0534d 100644 --- a/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/DivkitDemoPermissionHelper.kt +++ b/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/DivkitDemoPermissionHelper.kt @@ -3,7 +3,6 @@ package com.yandex.divkit.demo.utils import android.Manifest import android.app.Activity import android.content.pm.PackageManager -import android.os.Build import android.widget.Toast import androidx.annotation.StringRes import androidx.core.app.ActivityCompat @@ -12,49 +11,24 @@ import androidx.core.content.ContextCompat /** * Encapsulates permissions requesting logic */ -class DivkitDemoPermissionHelper(private val activity: Activity) { +object DivkitDemoPermissionHelper { - companion object { - const val LOCATION_PERMISSION_REQUEST_CODE = 101 - private const val READ_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 102 + private const val READ_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 102 + private const val READ_EXTERNAL_STORAGE = Manifest.permission.READ_EXTERNAL_STORAGE - const val ACCESS_COARSE_LOCATION = Manifest.permission.ACCESS_COARSE_LOCATION - const val ACCESS_FINE_LOCATION = Manifest.permission.ACCESS_FINE_LOCATION - private const val READ_EXTERNAL_STORAGE = Manifest.permission.READ_EXTERNAL_STORAGE + fun requestReadExternalStoragePermission(activity: Activity, @StringRes rationale: Int) { + if (ContextCompat.checkSelfPermission(activity, READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + return + } - fun requestReadExternalStoragePermission(activity: Activity, @StringRes rationale: Int) { - if (ContextCompat.checkSelfPermission(activity, READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { - return - } - - if (ActivityCompat.shouldShowRequestPermissionRationale(activity, READ_EXTERNAL_STORAGE)) { - Toast.makeText(activity, rationale, Toast.LENGTH_LONG).show() - } else { - ActivityCompat.requestPermissions(activity, arrayOf(READ_EXTERNAL_STORAGE), READ_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE) - } + if (ActivityCompat.shouldShowRequestPermissionRationale(activity, READ_EXTERNAL_STORAGE)) { + Toast.makeText(activity, rationale, Toast.LENGTH_LONG).show() + } else { + ActivityCompat.requestPermissions( + activity, + arrayOf(READ_EXTERNAL_STORAGE), + READ_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE + ) } } - - private var locationPermissionGrantedCallback: Runnable? = null - - fun requestLocationPermission(callback: Runnable) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - activity.requestPermissions(arrayOf(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION), LOCATION_PERMISSION_REQUEST_CODE) - locationPermissionGrantedCallback = callback - } - } - - fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - when (requestCode) { - LOCATION_PERMISSION_REQUEST_CODE -> { - if (isGranted(ACCESS_COARSE_LOCATION, permissions, grantResults) || isGranted(ACCESS_FINE_LOCATION, permissions, grantResults)) { - locationPermissionGrantedCallback?.run() - locationPermissionGrantedCallback = null - } - } - } - } - - private fun isGranted(permission: String, permissions: Array, grantResults: IntArray) = - permissions.contains(permission) && grantResults[permissions.indexOf(permission)] == PackageManager.PERMISSION_GRANTED } diff --git a/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/JsonAssetReader.kt b/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/JsonAssetReader.kt deleted file mode 100644 index 6bfe9870d..000000000 --- a/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/utils/JsonAssetReader.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.yandex.divkit.demo.utils - -import android.content.Context -import androidx.annotation.WorkerThread -import org.json.JSONException -import org.json.JSONObject -import java.io.IOException - -internal class JsonAssetReader( - private val context: Context -) { - - @WorkerThread - @Throws(IOException::class) - fun readBytes(filename: String): ByteArray { - val stream = context.assets.open(filename) - stream.use { - return stream.readBytes() - } - } - - @WorkerThread - @Throws(IOException::class) - fun readText(filename: String): String { - return readBytes(filename).toString(charset = Charsets.UTF_8) - } - - @WorkerThread - @Throws(IOException::class, JSONException::class) - fun readJson(filename: String): JSONObject { - return JSONObject(readText(filename)) - } -} diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/alpha-and-background.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/alpha-and-background.png index 4bdb74d8844c307d606c70654b5acbb3cbb24c9c..f2406614a91e4ba5618ba4d55ff3f65de320b803 100644 GIT binary patch literal 13124 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%0Dcx#%MGo%zb2W|9naxt7C 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 diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-gallery/horizontal-gallery-items-not-fit.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-gallery/horizontal-gallery-items-not-fit.png index bb053234e20bcedffd2a0cdc4ca726e07e3fa450..104b4217b730117eabba841188d4b69a5fb3f60c 100644 GIT binary patch delta 4285 zcmYjUXE>Z&*Cskq5`-uTiQan`Opxd;dJw_UqC^iFGZ8Hqa}tRXWE?}32&30wq9#Wf zT@bzZ-Ush?uJ6Y;zn74F5gprxnP%S=Yf%Vbdh(BT^B(kqj+A$H&KAF4--aqMSMsiLLV9M90@>#MHC9PbCS@ zaX$9L2o0^PWSx6FC$==R`DZ3< zb$#7=yvl1iKuJnk`g^c?P;juBv9Wr1_ydp1&-3%nDI7*OMPS^KtQt&Oj%&l;pVsq?W3A)~ofQ*rT`#|lCz8Ik`)sI07Pd40WgWaKD3W=`h6+lffaRMzItLwBGsb=Pt9#SKW;)|P4Y`i z-zOwomy(iN-q^rS%}~w+UHw7H4U6=Z6dd3JcL<2v*U5GD^xh$nuA6^q(Q?u6wY9Iz z#x|9Djubmeu)4i?!7(J6AoJlF)s>T)RQ($u6s}CAz_=uSqnmf`^fX7&Y*bCv1;%5; zj%H_Oehe6?AyQIO0&R_q=(W-B8Tk!+dwb#4)$&Pv2G1`DC!`cGit&9jOUsV;_a9Nx z(((!lzI#hS?{~WI{?#y3Hy}X9+}u1eD(d~0FWxLIQ8X+90s`vahRQsKgq{d}GFuv( zm@v@Mp>lC?dD{5;wz#93a{cLP)``lzyE(v>bXLN_B9-f}}`T1ieC++(O2B>Ig%2)ETvm?sN zq#7C;tZMyu)d<@ySxyX9{}n=mnwt8aynG5APGo9oy8a;wR#+(H=kLE!RasrV*T&*t zY-L5M9ulH(aB%R%!s0`EdT0Gsqh3HjMd0P|umw1ort(UvBayTGqK1ruBI)nH)xEvN z!o$OJzJ48RGd4AafLEm2Ma{skq3(3~ll=iBNj60OkVkV%i@S$Mc41*0$Zwh7g6@~s zyE^F?qA*E*9v;w+pexl&M*Nq0ID0acWBT0Y_g4lQP8QQcLI{elOdTBX?Fe>YpzO;W zu>KQN>#>p163Cs(?$7&ZsUK2oh{2(uzKIE1oKMqvJE+w5&Q7V0H%DzFfk4>a+nY=G zY5d_VRqEJ6zINoe(%~$%bGlYCw*Xs^U1p6`{cm*shCm>(@$oHgGJcC)x4#;s|5c7H zD!LBO$>B*qENQy<+}sQS)oY;^X{6-j*#gwEva+uaM_peZO$3vIG$$wjb#&3+Zj) zE;MazZ6fv!slSr=Nfu4YHpZ(51_w2Db*b^HM;ye&#D8l2W3sdF1+D*{8C0x^N3hFZ z+de;5T(>UyV>;_RG22wD4H9h;D#nQJ;T zGot~4T%)-qLNpCNdKd~jKd=PD(FbhUW5-TMLqbD8Bqz7Wi#IM0DkkvtJaw?Qw}&ZXThD4}b0if>I|myZb!y&S(Za$)BC8tT z891sNq$@)yrmuB$O;}0kGeA-{c6LSIX`=ezEpjet=>czpEZ@W{IE*B@^LkqYln@|Q zR#nxolEHDOcoUI?=dein`P%9#T#d!=DEMU8C0&i;zKTi)3?$Xq&dy8PAY^x52Y0ff zAyHCF?4K>eP-)ZsQP4~QcA-K`ODkvg@o08sg({WB$yP>2hKS+DjhTQ3^n`J7Nv>Xs zsfC5Mp&=a+^1-Pu``MJ6*@t*x2>aRkK0UB@ci0|Em9ii5IO)wdMa*4~Cc zjG==~l;aZ<>0+KgHZaJotNRRa>$#JY`->Mj;3YPevK@JM%h&;0Sygo{Yh-P!4(T$6 z*>d|R?LARPmYT;L(K7o%D-UUYyh*j#+K9dukD%7tksL18xC|5uk5{|w z%F#&*Ii87uWv+HM`%yS+Xb@wu*r0K7yE;vGcR`QJP+SB$5%bYgg7vyU-1F)QsVITX zp3${VfT5lvVgX8=c$K|`%CAF~4p1#y+i?e$ZGn4K$zWc%g@od^w|(h2l{C%F7~S37 zNesJrd3kRzF*%F&kE*=~Ameg=dN8cK%5j689TsnxhtA0%#qrY5AdpCYz?^ zXkg%>g~csSPEM7BK{J4z(l2KIs+5(Nzo|FsJ~=(zZD;qvyxRLKXk#p@&&~OCe+7Ord;(e&uvuoE z>|{qrOs)UY2Na5)P%GvsP+=olf<0tQm-CthGndoQkWpZk*FQ9*1tbECxIGUaUpsFb z9~26Gb+V8$S{Epcf?dEAL2fiPHGBK}Atok$a2E>gq>HA!SFc{pFDy(1UfR_MU8x`9 zYV6Tu7)jOEpV}mVLSq#ZYYLZQ(S5qaqP|&1W-;V7hA2{+Z3_LC+o=qY1kIhD9cNcp zMi!P}JgNJ?9|Z|}s7s)Fub5yhA|TVSqd!;Ay76`G04%tlmyZv80v-@96&2OXLgWP) zh*y{AN25(JH7zYIR)jY0wX37HHhDaYc#!q3G=LJh7r$|v6ZL|^!e~H{z}#$Ge44_` zd$v_CZue(HxubQSHH~x&kv$;4KpV}m3;Dh-vWY!=wl#@8q>+!TiT@`NQ$HK(>Ep8m zJ3Ux)lVKyApK~^bVVc13JvfYJv?TC>AQuVsp-wp(a|D@rZ4&V0zk7|0k5d2 zC}5tlpbeY$M9#3Lhs?9ZOxvXEn-ZlwRtE5ea9hr86b`$#Ny8mkBQ7K)R0BPeGs`pN zm6rC`Z^|zPJ8O|_knI9-B3rS24 zzFnUzF+VFTHG&Zg1EtWXxS}EvC1vHEg99MgYe0`i0`;Hpm^y@5eBV*cE*9 z5Uae%J-y@j&JgJRhcax4aUM%Zf-jPU7;1l%KP`> zspU`9x2!KVVSRZ!PFY)TQ31Fnrad=MEO3H|iIVt&OrtgZ3lhy|zv{XS-C>l7Q#dQ3nHY%a86FqjO@Hn38_uSU5PY>j#F}Uj!?4#bXKWTtCAp|{S#3QA2|O!gp=a3 zeZ$W*PN7M|-I@<*>-cyMDJUWW77)srO{Q&&X`Sgycn1cbZdfp#`DRktm^YZ{$@b#i z%%!98c^U%Aam@2#R3KA6g^hXd@*hD;?$)0#H^yYIlan|)I--@xx&OSoX4AC>D%Lpcp^X^_7Nf*uiyED=>AbYrM zFPW<9^S(zJTYOw8=U|CE&_{P^*oUDp>D zrZroVW2e8;RTUN6a=crNz+WydE~^^i%*@PX<>iI7wOp8S69i)BSvWeMOO7*52Cv9A z6-+&Mw?NA@EKEa5Nr@CjZ$<}LN1n}$WW+h`=Y9G5Rp#>L>BC)$8?`gJxTJ(zM5KS6 z+NJLIiw%K5XdZ42f6G<(yQO9J-zyCh2zc+-!%h;`PexWYr{?Sz=YJ!uV&_GF_2iJT$D~v*x2x8Vq$7I+Fw3iLNV~l9i9nn z|E=+^xY(rkR6+evDaf2#P*9ME59hRayJncq!w<8T01vkbxI%nk3|putWPMbBYz2?^hGm~jsRk(i#7)0Y=CrS6H$dZKy~ z!uQ;txi^kA;~xG&L1xSR?*;!o$KsiU1k(Ta8iHNq2He+Ixi3?G^G4*+LC7a>%3VXl zGaNU!&hsX9M0h-UbOuKrXn)GQNTW$9E591YxM7@oxIWykK;PC_Sy`b?eolS%jFFX< zb*wq;`t1~9#3~8v?Cd;^QZqC*F088~$Uf)3boZX6rG9X*nme(o!&&(>f!N-zyR@|Q z=+UETLYalN9ItDCwmt%J5##oKr<|Us4ui$Mdd2m`-Cf;(;e3f%!LLpsZ|C-y({eVo zc&dia;>wB(9X-{oCd>|SHZ(PTS6UihTr3qx8`FL8KzNrNPnT3TC)z0lRXw*G(^O=ATt*r$?l!!Ok*@BQ)>FJ9L z3;%%PNK9mP2wj9JD=U+>|LpIVmXjDtPyDH#`p3Eh4o8axe6-LY4ipF__L zkw`E4T_vTZpMwJQp^Yig0yO{wGk?3~UzGZp+LNA=!hX$j0&ZrOic58j+=KnuSt+Wk z%V-YU^_}$mY}&`z=k+=VRDJjx7Ze*uw?Z;PD zDL90$>+jNMo|IHnB$eccvLCc<-Rra8)`EF$ZLMa?byZc! zy?ghj&5vf{#QtMp@wHGt$@;UG$j6T#+0LCax3}-ll(!pe3Q?(U+DNpjB}_~h+u92L z)wITtkdOdKQbu;NadC62hipj6%gZ~vx#5;MI5;fq?9S?Vd3otXsIomp1C@b$z0mRBtOIBeNcgj)=IqxVYG~zf@8D?j3@kDLFa0X=lQn zFgof>v=35p8xU?&H~ti|=^7dudilzg*`eEFFhDXubbXuXh;ix5jM_gh2Vi7ub1R7G z9IdpoX%4+walaD1Q9e)K3joDt;pmu3_A05WO8N5T79?Oi_)eU;;CpKnPO^f%@2dYG>?8$q1%WXEI$uwt>rY=(Rgl% z2f6XJ-~52jES^Q{aLZ!1o6v1;!1AI+my=WbTY+{#O^xD{Cr=vu7hmB>GpIod#mvZv zWoBk()z^xvE_nT+grsEigk1yb;43twhXTn4KAN;%9qg zDQRgYLfoR%;HIpk$&SB9dmO|OG`_T zI<|zR^>D`yvf4mpPu&MD*Cuurds`!5DWj_ZnFb{3>MAXdmX{YDeip|r!oW}<$~Z45 zgpxLH$?A7iR#8!pJWvCqh+1S=aq;ppsqrCk{(*rEFM+3_nuf{21JxH~?+*?RwgIg} z8xD6SWfT;SxQp@}PW=A2jJ9?`dHEGVA)%h$-r3oXq%n_cz;B5nfz8d$5ZX9qKGD;m z0fla_=1)vYdi?nD+qZ9{X*7>U91bil0`DXc{;zdHu&lb8BPuH^t8diDR47F_nH9gc zIS-w$D`a%loF@hl?Yqh?D}oQViO`1Qqd!-#szv|rcv;z`u`z4Fn>8*_9>AM>Gc0qi z?+v%$(%$#-<(PU{-5(LBz!chQ(s3T5)(IWVD0? z6a}+j<`BiZV$$;SSN-JE+>`GI1k`u!7o|OrymjO&?^N~S!%r}aAa-cuB;P)HX#L!5 z;45FHP$t=IPr9~7oPV@U}j&Atm4%#jM^O*qB)!55#_!jm+rX9>;;W}WH z;m|&)2{WtMoeE0@o$e?EY9@J-Kf}xX{JWqDhwaebmXySSDk-q=JAL{zzN-s&eNi~i z5VUrT57l5-B;crfU_e?zBBrtN20Dr*UiW+Z;z&R?#drP0vVwNz-s8o!W#_Pl3@Xj`L6v~MhUwHOn+i`Mv z5cOxQ)XB&)*O44b7GlU1u;kY;Y6l}qJmb?L?680%%l&<+(~@8d7n=dydr0VE%(1X# z@SlI*X^PG`#k`3i>t`VsR&RkxF|9&boUL2He$}`*)F4A2a;z+%n{YDn_#7vy^!P4D zqqVs{Oqjp!V_Vr!0AYP?n5?4$iW1$YcNzN_z15eoWX2D<84KX9@b~Xu2T$p$ImZOG z`BjdrntR*2wc&57fteI8Wyp_`Ei$M!sUdCezT}MchDwSZ@IVj9s1(^|w)? zmkoHrG`EJRUb^YKbxy)-*-#bqamJYwB;LQI_!U}3XQjwh~PPL0{MQNo39FgI}=3^VdEa9Dw@KPOz!I#MXUwae${ih2>;2XWQA z4%gZRhl1xWuoD?ZbeIR-Vpc2SWKWRsR1z1SH?&lJ$tcIf`j-`jST|itMeYyYTv%5J z-zD@UKO-A*m0;EiRs#K80DBf&E1`P9o@(9 zyyu+n#CK-Rd}sccnf0;OyLiRT-uH9Ib^Yr0?Gsta^XDkep-`yvQV;LT!}kUh>XgbE zT=+l6&ncVm?X=xpsb^>4%l(YL9|}c>lDdEAne(T`VYg>t4w#b@#>MkZclzmk#qVG# zwEVpE>@+b?j7BuiHYIsw*l&@rR5w$TR8FHGE7D!nX`c=(e@t;&SRTllOc}E#qd9^b zq<(s*f3aQnOYrmigu|ER<;Ct+AKoe{OBv!K)@!PGAm}05=TN@ARV><_=+g2*WD6YTE(>hiFf9nhzG1zRwXQdeB|euRGl$}uPsG>fj&*V`~x=~@y*k>Uz{rqe|4^t zF@S}Iim2-RnbW#2Ox`_k9IQ54k}>C_lh`FAGcu6BoJHPmfyXB#Z+vzC1M#O9#Z?Mb z7npMO(bdhp;rSHshByo=QF++AO@u#6Rg|vo2V~!%I>BPj;#9T5%N#y_9s^@j934Z8 zX5e6QeDp+~K0)5-nl~R-L~lI0`d(qX84KNk<-siHs#|He7d{6a*>kd0(b2yB4fH)- zC-hyhQ_)JairAI;J{Kv&6#PW>Ub%TYs9++v2l9t9Q92{0(~2r@5{aD^YOdn`@QOvB zM1(j-1JLbaCOX^Rj3JQ-~&bUdRSe#@Mh3k=rpBqJEZYEqf zsv=dR(T?n=9DA@UaH+G9W%V0%_l`v4t64M0CwPaIXwf)AF}$C-J+EAKoY+1-_B+<+ z3%7bffYWRkCiRAzd*{*nSF?ceWWa8_XrKF_j>+4^jp=sNk>pCdmuX%Uu9)Npz`DDsJj9V19 z$wgm6>hb<;y=O{F;|mMT$cO_1?k9HUsTL$2;3i~cg<#_mj?Tg!)GbzB;Uc|k^) z9Ov33tgWCx;#wX@FB$YiLn9zJmn%^+*swQEb$54HMRUfFnBFfi@CFM@ApF>NYhE$+ zAc?E~duIQz=)1T1pXCJg7JAbq%C*`LRw~s zA+g>mN&8e?-M_GqZ)$3an}-K$b+b#3%L*gx?=Ukxy*?v$64li7NLW~Sb!+QeQBmal zyy>UdSk-*f%X;-eIHQ4l800a2{gQ}|j_xq#HqKXr{h?CnS?&^RU|`_BT8*bs=1d$Y zQM)=;dy9!Fv7jLQjt}nI@5gM!G&GiRVnRYOP8b(om|!mZ*(bk$|F(8=s@GwEqO6Qp zm1okWlC6ugy&YRAPAlg52_`idkH*>i$B)}>u|mEP5my`>9HzI#c1L}L4;FJtug5`RCu?CevA5@DQc6RqRq$8rj^oSA%cIT^5PU5uiKgVU99>*&iLt6^{Qfj? zFpcZlwU;oe_tDWmhWMyOBw{;b8Rt42pV7u(z8V=BZFJlt$~Nt}Z7)b27#Mi{#*J*x zBUjJEHAz@$WEEIh3HkW=<`)(Q3ax0hJ+>~7xNn^2GHj{p>A8#yR`hsR?=};Y%kkdK z+rYpFiJjI9y)2H4{ngt`=z*-~*k*kh@WaT`=T6H*jnl0$s(Hr5!D7cn5B8#CVg`zA z=&!J8O^*4}GO)4+UA_0Fqr3aQtt}@k@IJ!rOjaFEz@?Ec7nAm{bRkC(nACaRX$-=d+RfvxgBB&6!i zc}iGpkyPbOOS}A*hKAXWin#`kc{AQ}JaSQY{={KVc&d_5YC)=0XgkhHp>eL~^wb-WQz z7RX{n=wLKbR8*8@JN4+=wQH+8JA7Wmp9JjQl)LSW&&<@@wTt+|S22~{bj$PcpKoFC zw(w1Bgs;3YN%?}dwsxw;nzuI!#UK;iZCDZ;OAQ~F2k!JC!CE-Gxa98*wPzYo<@6e? zoMU5S!zHpjpfP#&?2^|3dgSKKo1?!1DV<$i9nD4pD0rGoML4PUH0Gn zS7GkOn+x-K=p_Pl=6-*RRZC8>Oma(!o1U9{o0-X`oT>FKEsa2~J>HjOw$Vec#5E!! zBG>EWn71oaqgZ!sB`9mFBKvje{GGAJP)1}UD`#j_S69D)8a=j*S$AXWYV08I>0!~c zRzFBuZeK2*p~=uHf8=r0j@`bwS#gVv4Ns>%PNZqi5<}IGeK^8{RqVdUN$b9fg@S{v zvp(5Ks+_=warSxn@}+FLT}MKA<=O5qlJ9JaiunX%`addJwgcyGSC|iQzVz`y$7ZGu z4>B(e>ck4i zKJM%!x%f7yGY{|F!Aez1zsAf=;8R_dtygbsZvGREj)Ah2zgIv&KyX@T1yjz=$2T!L zD(>zsyuH26FC>%;vx@OL@#xLadWPdH+Xhs>^1lB)OQUduCr>UEOP> zz`$Qyuaxh|YirAyn%;(ubL0B;SH3j+d#`;nmMJeQD=UYyX}3l5nCZ*n z($cbQcidliZ!)xhz+qwOH7hxf=_Rkj3EGtJ-*wjtXN^ zjvlFthGAQkt>Je3-Yjz2V1Q98(W({nhIw=mLmzzh>{&vOY3H0h?_BwO14l_{>AWVh zI{Z0SbT=a-V|u4#aAJDeaG8Am)e>*;t z40>u+%7ksp=Q8&C%6`e2FO> zA6#+?VfwdLv>;iz$YDWAPcBoz4`aI(>%@Glo$=?rF}ubP&a<_(rBUfg9Vp@B>x<`u zdwxE!^<1X~@A-#tLePa)Mo@pq2yjtwI3k5zIOT-bPK%0)77V#3Q~wzjo+V*e{Y7cD zIbZPj(IaS|I%0s=NuO_vF2MO(V$#4J)}MjTkR zD_S12mA?uNCGpy8*6x_I_t5<6`5DS{S{gHg41Ja&vPv#bsj!>)=z#1?_KIRUYvo?ZO0bwC%;b zFFmF(mS1&sIOzpOgi=sNp{T#|_2u%|-=tABvVsXFMH<458?T{NY|M0ab}q3ZeGMs5 z_wHe5XJ-eHbG?BQLr5dsz&GN4@99%wwu;>^bzz^&4Y<(-PvzzBdU%LnVPO^EHvpJI zMRMr9dBBf?v6<%&lzs>fCIZM=i^0TI`{3;~2sSr0!SdXoqf3zN?Nf*HE$iYUfI?US z9V27%@Lr$Miz7S$1`O=4Qu-mt9 z!xR=Q)cwfJ#H$(}9#)DWdjciNjhV`cOHxvjlufH;FyCyiEB^SW$Ifu_r<3tI|4$-r zq_niO(=AbY95TsW**evyP@em9$%HJ4J*KVNql$`(B3?LBj~~+1+ zGBPr=TdqM=0(Wg}INI9U0%$~{RdWsV(PynEex2W58EK#EN>0hlGy;McYxxF2jIb7m zM@KHZW4h=+)An__J%by65Z{G3As*L`2Bi**w9= z$KR_LJH`@+W(sJNUCzwZRQF(e37B(^_1Ik$1qDTaxf>q}2n8wDoVm-EtH|ruucwc^ zwii`dwaWbb{U678BlI5u8+m!$L&L)yWCQ^AX@CR6qNrx;K8Ud%$w*3i6Ur!uvGD@X zM?yyCT)!tsLg>BFmx;Z^hnCqXsjI7lLIZVXxhUWkH|$*z+K>nSq}c!!D-M>03am!j zKHPZxv7CqjND3o8J$AX4oU5x~SCS0&s4uN&!q=~_;FuTe^$z72;NQA+>%OF9uKk=m z@MmLSTUW2XI8Vu&1?&-~#ey)bT1V$-f9qpd7%>S+Exa!r0QhuZF#Sa~dcF&DQ&SI& zjc)?D&?vEg`<`AhIyqnTxon)U3314mFV}!lMzKGyp%%JbB9DQlOynIaqe9t%)f!vQlbkanaAuPX~x2m9TRNyl{krPB1oTsySlBMjiOqf~|m}v~tsS zo~RMjcfmaAOkupacTW=~h(WFDO;_K8iQ)G;7Vh}HSajqG%$US@E+uOlbqjbHtg@C? zoB&#TIgQWHyDB@-J1JG3J5%)%2Oog0eti%%3c-7TGxMS{ zv$8H2yF}*w2cNR?ejLGywb}no6evC%9pJ&RLkTFj8{Fl6U%v(&r#ViC{uBqo~8fz=M^v5gh%Fgge5=yA$q!(^1SCMIG~jUs5vN5vsK{ zT;|esI08!{-tHg*ygf-ess?#in3>O?KO^OEZcYk#HLed9BO@axCugL99m9hM51uJ1 za(Epd5)KD!+q+nN{_>?}{DqdAL?A`a=Tt+ZD9i6LF)`_~R!7Bio|Cf8WPxHQx``t= zUsF;V$KQp7gmfe^p7XR+XH_o<7#$sT?CaZSXx;pvztCadceFDCBXffaRo~to91uW2 z<1$H{r^cd|9u8pt!-o&wfBYD4iQ<|z5Ue4cS~39+<-XOU?DWa1@xu+?vwkTq!x&M{ z;gH()nVQ&e6Y5W%S3-R7h+uZ!zI*ot`nAWwHi%JeAQ>`GZ>+6-PDntdE2R=VGy}Eb zld#JNAVOOzE;4-C0|Nuoe7FqKkvegrP;a5hH?+3i;N!dE4f@rNs*BrT$LD){6SOD& zpZCnG^V$K6q{;H-bKkvtw?#Qfh(*seySK41#p{UXD-?>%%3}(cLZlnmRUVxjwx2vv zR5akooFfN0^KPldR7r`T{|V9g^Eh-jZq$u)4q^_eKX=U`?4>@KmhtLUWC8r+;%z2= zeeN`VC?_YG*+&A;5pwmOV4kJ=$HHU*0Rax+sY1sfSnTxcA0!=cm1|jTz0*lofHjDW zE?{M4Jz}JTwg%_!Z495)I--I=LtQT4(E=7DZensvz0fj0QjC-HB4`dEr1s~VDaVl9 zq^I|Z(Dob{7l~V4wT9}3%mW8Ur|xlc1lz{2(}+XLVp1v^xTIe|K;EpjlY@i7e0R!q zMn(lq&EXW^_rtd`Q(O%9ajCyUcZ4c-3hBK=#a&-qoJB(652x6mvFiae1QZk_$NondWK<(jo-(_FjQ2b0^qM+4PIEg8DGqJS% z1*!#T079bVm6R+XJc%96$sxTO07y7Utnmn}q87YwU~mHpk5%pnTK(6-qAE02;Ndj` z1I1n%ccf*QjEkepJoH5L8!p7T&fiL~P+$U%>NMgJ0bt11&TasDhI)bd7|6iPDp}rQ zhhsz*gB1!%($9N-D4srjIwUHpac9J<9X6Y4md+ht8fVomLBC$D46fmi2YY)Q?z^k9 z>7}VtP!teN5=7VIql4DW9D@4*>dKC`(a??T4tH%3FbV=H!sXgU57PDy=NdSH)Duwh zwhUcgT&uK2Dkkvkp?fgCS9Q}RmQW2tm zPcrD(-aJ=mIc&N(=Wwo5J)>eHsP3fsGGamfb@O~tfAr{45uh#YE44pB^!SS{HRbS7 z{Cg<&jjlV}( z@mqTn(@R{Sq5`HO8Vmaebo&%xPmTj~N0bFD^ z?w|ucV)LK)LSj24P`p>?lH;M7cN%j;p_f-ze+*p^UC_$kA-QWKZ zEH#Qi+;k;>e}8;{t(AJJKw`l?A}VrTyG9ZCW~|m%0hZOmf(0~)@oy1Y*Mxc2Pn~Ht?mle?l#)0)nLWcV_|+C zcoo}2DJd*EcJ>f3++fCD&gc&rV37({fwl0mRVC z*9U;X{cx=wu=YB<7I9}K631ZYAPjy9U6}j478j&x_~G2z1W^{yZa5lFq5DMsO0FM6^teHsTPM>fu~t~BB0K6>lWbJQ?atX z@$vB%r7pumL(WrS%4Y}(f3>v{y1BUldWaNp@V^R?aH=l4uu32q4Z1JS%;4p5 zD@${7abZOYIRVsUwFp0pkFN*fh(?k13y@=KYHCUp3!pM%vzzrY1M41?4u1EpMu$Dp zr;7x6ZuVhq&?ufNDEOp;tDpwnptjc$kGOATs|+v;gM5O&dT9nwClSXH2M4FSx7X2G zT~pu~)TTVw3EIn-Uu$JD!v68|JA(p&4X4V7NYogkou2PRG!WO)BF%sehURpVcKoJp zlx#!qh^J9|9FCgf4<0B99UV_tHHyT6)fHmfg1L_Z$J5YZAN+Tf68n@oqk`Ls`A5Xy z1Qgj!@H@<3syrB8K^PI(@(ld^WPo#0`HDn2p%~$6A~aafeVHW zOqi>$=h7v0j@S^l?Zu~UPGFjDc*SHG{+Q?tv8g6<9pzVHZhAq@<-M3oDOUV8$1>9TTSV-xG=MqP#CN zDY3h(>-(p6Q(BdQi+Ly5yqQ8F)DVE~X*|3_A?*dusO{`|XZvpi$gubZ_FGF;l9DTb zJa&OSi~{v@7!#M`F^KVZlt_FJ2o(JAgR}q!V1A>cI@4{j%`09fZgze12p6HJCPn>W z>>&c{MQUn`Tm}s{=~$ekl7gLfh##~zN5(a4lscwMHtQG}`GRf>Z4c-V>_5CwXxXHU z-x>iBG&VGdCw8iMkoBof{0hWYqxqp48W-0F)oc%-!ok-7TCYP6w(`vnJ?l_=fq8vT zO-%$XSsl$$Y%_tYQ(kO~Sc0M5O49Z8SFe5^juYtW8Mw~PeaRaf^nhOlV*2GC$3fv^ zVAxJ?ZN+#4eIdDULEO_*6a=r_@75r~j(F@)|H%diC{5ZN&6=OYs}JVw{n3eudtm(o zZpe>Rf{hbIE!48Hb)Q^_iJw0bs8_DlNCmHq9|$Kqh-l$S13kSA~L3bqY80?Zj`2AM1F=Zyc+pr`miO@BO6h73R8+{IS^|9O?C^~A;&|U|gT&g{<@jS%l$4H6Lfrxbm}J5mEnI1urQrbM-oui^A&1-$ zBM(oH>6MMxrVC_bh%SB;x_3b}Z*ff!^oTsEdvm*|1G z0}Bg+UKCS$W@2JurKmhBiyh;-z*1m={sq4v07SFHHWUPGIce#DIwtzzTY5%OT1)-V zP!Y>=rY&~b1PMAoGk}DG3UgP_5h|f;&iQDoZ*M*|6PXkcRKcO&gw2V_tI)(pA>{+P z9>-G8UZmSBm#*aK)f0kmVeZ*BHaYndlqToJ?0RcEyId8+rjNJM)bihfZ3)y;%FvJj zcpAbyp)GA3jGR;`7+5*pP!%Iezt?qW5MxTfadf02tG1`KUtofg~>t(SP5Ugyc`@HXRATI!DP>MA@k31z(pUNPoz3=i@9zf8tv>LGR{)}z0W7E^U)95+b z68T>lNKRsdA^nllHg=wd%>HdMhS=dkV6saUOK3&-el)|N>396_zir9?U)@(niGHIs zEa7fth%aUi$Ke&f{CA9IV>j2=Ab0U>ZwkJi{SMiI(m0KHjXQtqU;hpc`}olsAY%pV z8%0EggA=hvs2nYAlEK|hKDH>85Ktuh@*CB=6jFwj@%goQsrXGXib zsil)b5rn8*hv^ji*5!c>Y5=l-=9A?)A|Sy5l=Uqy7XhJiIn3f+!F9T$$lP2z?JHv= zaWdHQ%F2?D9-U*9{geO!9FPHlVab@AGanxxD@B-?n_m|Zi3MPTwEVfm?U?nw3O-q}ER)9AOg&u%(271fF)bwsKvBBXsPG zV`F2a&Hu<4ZIAXH{tz~={FOB7zX&HW0UrxmZb)pb4DkHrw>X&>q6s9GK_#ey!% z*H@gnl@6Isf3^w`hyQc;t!dmDz;m|imMI8#En9!%zje+09ByF10$tM(W)lLM@1b)< z!iI;XxeGNI7CueAFi1{x?-lgo2KS5PffBs`D|`@b7=yUd|5M9}0EMJ>HrmA`6FXO- zjYa!F-mq;Z-YW>`CK#Sblylm$S0l_-@-6&Ex+c;eacSymQk)zpg_xHzedjIICt8 z$n7XdzW#j#v88!(YU=%`PXVQ+LJ%OT?dl>y2?+_^y?4)LI?4!zkbUrpt#V}`35mop z0hvSi05R8rq(D^id>(hQG!k+JixBd~q{foJOQ8U5LvVBcS-B?UY>RDg5EBza zNHeAutlq!PPF{d)A>y2V`t<20D{B(?4YlZbX^-|#0=1xMuWf8h6wk!ff+}GwK=XGg z#iW8A?k6Y|K$E#R{_6xU zHYL+!r0tVH*Ld zY^G}iK#pI8V_OAEBwhs>9{&m}1g(b{=$1He^0s>D;74Gr*vxm|^4uvM;ea3|Qe(mR z0-UI1W$gw_iH$h3aCjho2ciq=+qZ8b1?SDSSU?$4iw*$@XO-Im(JkiV^VV~}ZzN1< zO^rm!oRZhnJl~OWd;nz=1vWK0w!VAO^k}L7GSuBPjp7fGcVDX|63c?XOu~&+!s4r(&W#pxsGwb&L1XgESdGg5EeERfgIBCG;~?Bm8C-d$2^)V%YdP?nT9?ssyD}=8FUXzr;1(!&LzE`wOuEjP;c0XkT~gEM(7%G zV)m4iVv^4lpf-2zyvQkj3Rwj2!a}FuFvZlKi&Rw7Fqd{SZ9GmVo_kXffLXU26c&Ly zfYq-80cgmth&-8Z1H&KuxGIglAPAsOO*V!CfouKsZDGXxw?B{N5P!nd=V%_Feau>A zk!+QRzYhn1cVO{5uhO-)wpxTEc~PiHAT6a55eQ8{y_g1BFGsBcb}W<&BsZt1=wK$q zz-KvxO5;v?D{qY}Xwz|p5dK77zU=2U1EdjdKxa^!Y*Lq2_K zftJdW6b7MJz}Oq^4A5T?nhMAR675 zFJG)39KL~x39>K>>GA%&#VLG9PV28Z(XzU(`R)G-F$3wCgh3jm7AIz4Xn2u~%y?~b z1UjoxoXeYhv%Vb5VL=!iIfw3PxUujxDM@Wfm&VV0`-c>;KCALAQfMI)!1k#`-N}sS z_5*{185tOUjfW8%?-_a>EENt^cnCrGQzEkun?)uRdLBf=`smqK1t8K28GlEJ;Da19 zP;8g%q6UF_C=2>PlSrFk7hHs(07Uj%HNU~Gq#lfuO`d=p#7t^t1(KtO3_S>YzoEl3 zfKm+E`xm)J?ZB}ImiAR4C5c2un}M%`y_mHpfT$1nq|Bq)^)$7xQFWt$;AO$b7nl#+ z1s(=vlpn?eaV7@2xE4g@i20Z=HV4+6nZ>hESVhLNQU+IDCYF~m-?EgRJ$s_Aj!rt;7o)bK9xLlqKO+i5fD}q+ zcQ!`s%jgR1*uzPszqMfgIsbXL^6Wzp=s{2eVOZ58MtL(XBj79qy+r8h=a=}f zh>v^%&YDX}C@uQBx>1exKGQq3V^F;R?(|pM`6U}~b%nsQLlIN+OcyV7z6m&c$Q6V8 zl}WF9`)3JYvvv@{K+@KQ1w$giV08lU1P4|Zyl3z}6FZG>vasBM6b@){mYA}}tmn0e zBn%C9m{roKzH_O8zY38Y{#9!0Ew32Q?pi%=VT50UXgF6L(xml2rclW>jW zOF;oCgaKj90-*N-2t$HkaC;+DeQI`*zl53`ub%>7I$*%H)m0s6eWci!%cP|1!Cr^h z-e4h11du(C15@@VY*O$X^v7ymdv9^P5K_oMtbIV&h@SkJ!{NW|=^s%qn_vm_rYpUz1auWxJ!@fccr z8HL_-GGJCwPcz$H9Rr6UhNwa*)d0Em@!84U!vk)ar0mse0Q;GKFG+J1e2_P9a5wy3 z4YNoZ{pwu0-9=Vg7@QdfA`Pq|h#6*F-fv8}o8zcb%K7F8W6Upx1O?#WvtZ-)+iC*I zP|AFgq!kJBd47~7@Gct8{M-q2fpSn=Kq6W(Dceh?lE*h3&l4afY!fbvL2e)f%)h9) z0(F1yx3k(STXpC2K~e1q%hqwz(_}~*DJ<+O zY^($Mn&~WnKmRI{6Sn5$mXlH5$nd94Ql6c$Jj*s^91Jr6TNe}@Uq}%^&3FW*ATBNr zv{F5GGHY8~$o2ccc993D2TBXPW@Yo{_K7S!ZAJhNnsQ-2=vbaG47gN01RJ3Z1p~^0 z?kBw7Ko1wQfTTemfrIf>QLzr*rlr|$8@ib$Ier0OFVBUh&${!}O>J$w!P@2&bcAZl z#nSEW&{?obL;@DXjk>a3gt$n}tNL1@R&Q{fPR zC=4k9mSey7#?628h^@BvMpQaolqf*xW?X{hsi{l~$+9;f3<|1}tka5PvW62=40m=3 z=bt{OvKv$$E<)=CR&_Hhh<33PVVhA@m-96_6y^+sZvN$wF}Ee1eqr&x&dbZ|o*#L@ z{nspn7E|Bw59MFeON%O5w?&F!jstD&ov3uw=Q5{WmfD+R;;4)Y3x6#x5lzi5^H&x8 z)^0Yy$1YsEd7tQiJE;Ad#w%tA?)OoNxPF8rBn@1A&DUW-XGH zrlLYmPfq|lIc9yVvhkv=K!lOY1|Y%$a$q-V`^0O2n2~ryH2SNf)qrZqgh~sn9zGV? zR~FfS6pZ&1%rRkEb7^Vmm=P(s&yfWl4sTb9;PMj0b@}aQLtJzWfF=VDM)Hzyc)`s} zlp%+XIRJk~LNjO)GWp&+FAumj z**v}h!U1xj58SE4-L>iVQm*B6FkYFIGp=8|cFN1k3seOlPCtMBIwcm5Vu)bXL~ePx z!7ZSm#O6O9_@&ILb;z$Gsx!zP;H@6I$jTA|YJ*==3n=J}wXu=}AIO1OrK|8Muymk* zoL+IUvFEEG>%7eWC80aft0$4If0D8A-W!6e3=D6eqI`&tx2i~^6>=o-Mr73%D}Ng+ zY;5dSuZsy=2E*JnE|RP>f6S0h?#Nfj_k|(91Rg0^JV?qKJU4)UdE~9_N)wp2NDd$9 zCSY=bwUwBc_YwYp)ZsNg?yodXaw4uVt*1EQU_eF+9Np(@<8?r-6yN|372BZ?x1Om7 zx|va2xecjp#(4MN!qOlGOZ&P#{z`L{N>koyh-G7MuMdVG;%b6nwlM_^=Syo@0mY6Z zSOd~9HbBg9NI_ge8SktRsnKuEYH!V2@Opf(9NYs3{wfbo7!Xx|F)TyfW*aLRzK>us zOeDv9>Vl>cOT#WR->IbCo5(l7O$Sml80biH5j;IvbMv&G$0;oJ<>&G7kt?w9IioYI zaw+x0>cJTOt``%K=7|EM3%P#gz0LNmfNG}~F8hGIm20Kl?Cqkt=jQ&WS;XjXyuS=g za}Pao{0kDGVM5*E6BzK}!XS6Y0UkA!`)@SZ2{h&3ki(i5N3+8V(}9Q&NG47n?z|4O zX!%Nsq~#ijEH^kS>~h&MC5(Atq2aMA5e2pH;lEfq;`F!@vsudJv|D;gNtG8pj(Yk zAOzWMzvQ9jwXIq8qjiRIMu=3x1tzb)6Kebv$(hYfnSJkayiU`6rz0sG`QQp(9qeoO z<^H2|tXn0(egK3CijT3S<6?-Y*KSRl;i*{q( z&>U_MC&zb$?Mn~PLP}1K5dbC}9I$d90)^;$6ijy$g~AO+{#}4188fbNsRhnpGLDxZ zg20L6hoCH6vHFixxWQJwbJvr*P-LK$b3uOwgB$*J0UdzKND8-KFW4c4Mr3rmkg++x z>U>pZk1vfqJsq|IbN>tZU?JG=V{qx=L1rHSD7Zm8xxckA9gk*J%k$GdnpY-W;0qIg z3$%-i=6{W8SVPVPV0^M%ykXtD%hSa2C{RP4A%PBQoRZ=oDJdx=2m>pW<#pn@TzPU_ zLEu+!apF~bDWvzVciTz-t~M^9C+u%Dx54-W$Le$6LKLqB1>kNNur4Rl#G{d*RZhSCCc!G6O-v5J-aq#IJ|)=pCNg4P!6%JQC#K;1J&G zR)CHTSA_wRvqKVKR-W3me@QPs9F4A4FZAFqE>$PLsaH+-(KQcokDgqX69IXKKrT?aG2n& zs4Ub)8!sBJ^!|DIMUvJ*1^Sbm(gnBIkXr(Mnc9qEVwJ6%V6+2b+AuT8?(6Q36ypl? zUWX`)+Atrez?whq$#J~I?l3%kwrk~12KK#^tr6M0vh@raC;8_I0XRZW<~!qnpPXW0 zEcrW|JNp5NTwQfFR+X-FIuKJ->V)+c2~pR@i=hjvcH+OiCDmjC_-e3(j2Gv7|GY%K zAYGpt1#Yn{oMfoHka|ZM9QafAF4r%qbH1d{+AIiG7}dtg_NgR$?%C{hYV`c!#s1!& zK*EpNI(Uyn(l~gC6kqTJCh!|~B(PfHd1mD1yG419MyPD-@C;NN>FkA~HC418gi~B? zCTw#s@c(055}BPkbqcP%!V$|Gc7ff6T7{@{Z-I2X7igxv>06uJ^h3=S%0xnuX8TqL zO0>r~-z|9JNmeVer%Zp}`19UIN4bu|br|C-ZR*^2Re2RR`h=&53nbIkV|GX2sok(`Vg{U$Gw5c>Xb&Hl<*g!iXTGYy{+Xr@?Cvtmw&!3>3h*L<1tb1Qp=L>?H#SdmzdG9@X`;qg~>l2B=G^fj! zYQ5B>0#C5Duv?2}PQGDB+>DD#_!0O0XmV!Dp@{9kuPCG0nEURn2l;9u1p;n%%bv#R zxVksrB%{?XlIF|UOv5}LC zP?a@ew<6dA9ksDrKQ4cm#f)EX6DiHGdEoK&V}C*5%0ZeE)2j>Jrui-P9oN4;C6f8p z$}}4IBDku#TZx*ieWOE7;pU~A5zTlmYjV}!cCo6(c4_cbedM|n}UEYxn76b%J7`)ZH;%dzPfflZO?v?(i%LU-AwD) YFz0(&vz!9{2^*Bu1KIl-cXeL=4}OZtaR2}S literal 17555 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 Hdh)*jgB{+R diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/custom-alpha.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/custom-alpha.png index be98940b5754ae0d7b27622b4e838d6f3044342c..3421cec435a24f6715abec390a2ece610241b462 100644 GIT binary patch delta 569 zcmaFG|DJz>Nz^h=7srr_Id5-m%uTv0<@WIHYE4G(J_W_4qDwLlED|X<65@5xU!r2M ziN*HJ+C>#UI~eO9fBdoai(vNl zyLrd+woCv1TL&`q_wQ;Ox&Ffa_orhRy4}A1UyW|(2>g^n}$AACU*3{JK0PR9? z=`2K$0z>!nYmjpezk)dD*vpcrUsFPN$E|-{wwqUI_P^~Y0V-!dnUVQ3&}r&o|M>k1 V10@R<2F+mr0#8>zmvv4FO#rLsM+5)> delta 562 zcmaFQ|B8QtNz_757srr_Id5-m%uTv0<@WIHYE4EJC676mxGtF_Ts%3&NQl=#e~F*M zrj)7~(TBPA^#5D@eB}e~dkL)fZ||@F|Bt8c%G)yO>#s{=^rkQMkKB6srAgiYf41kI zf8N+<$XNfi>hI<+g4y3|?2bSFC^3KjeDC?2vVQ-qli_Rs`0wAoFPf{<&KKYKEc4B# z-S&Lq`OkZ5Yic-N$ZxxubF6H)?C!gHFN4G0ey!?#|Ni~QuV1%fsQf6s6E<@rndBX<9- zv*&9+Tv1;i|7%KUwcY&3A1iv!pFa=ui)r<|!}FeRcy9TolCOH+^SAHs?`QhezBg|D z$G?AV_wCy^4QQ3w?6WuWwkL1Dty_C))lIlRfL7Y<2Zq5V^ELN?!SnrhEh2dOjz8Y; zl5zfKd!`+X^{>DFN`I+*?cL8Bps=Bxy*+qv^@i_lxMpj8i@uvUG8UF)?uC*RozyJiEu6{1-oD!M? = null, items: List
= emptyList(), + margins: DivEdgeInsets? = null, + paddings: DivEdgeInsets? = null, variables: List? = null ): Div { return Div.Container( value = DivContainer( + background = backgrounds, items = items, + margins = margins, + paddings = paddings, variables = variables ) ) diff --git a/client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivTextUtils.kt b/client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivTextUtils.kt index 765147a8a..45e2e47d8 100644 --- a/client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivTextUtils.kt +++ b/client/android/test-utils/src/main/kotlin/com/yandex/div/test/data/DivTextUtils.kt @@ -1,5 +1,6 @@ package com.yandex.div.test.data +import com.yandex.div.evaluable.types.Color import com.yandex.div.json.expressions.Expression import com.yandex.div2.Div import com.yandex.div2.DivAction @@ -10,10 +11,30 @@ import com.yandex.div2.DivVariable import com.yandex.div2.DivVisibilityAction fun text( - action: DivAction? = null, disappearActions: List? = null, id: String? = null, + text: String, + triggers: List? = null, + variables: List? = null, + visibilityActions: List? = null +): Div { + return text( + disappearActions = disappearActions, + id = id, + text = constant(text), + triggers = triggers, + variables = variables, + visibilityActions = visibilityActions + ) +} + +fun text( + action: DivAction? = null, + disappearActions: List? = null, + fontSize: Long = 12, + id: String? = null, text: Expression, + textColor: Color? = null, triggers: List? = null, variables: List? = null, visibilityActions: List? = null @@ -22,8 +43,10 @@ fun text( value = DivText( action = action, disappearActions = disappearActions, + fontSize = constant(fontSize), id = id, text = text, + textColor = textColor?.let { constant(it.value) } ?: constant(0xFF000000.toInt()), variables = variables, variableTriggers = triggers, visibilityActions = visibilityActions