Updated Compose BOM to 2026.03.00
commit_hash:2c382f7fc098471c025ac6e4bdd4b55a2da075d3
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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!")
|
||||
)
|
||||
)
|
||||
@@ -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 <T : Any> constant(value: T): Expression<T> {
|
||||
return Expression.ConstantExpression(value)
|
||||
}
|
||||
|
||||
private fun expression(expression: String): Expression<String> {
|
||||
return Expression.MutableExpression<String, String>(
|
||||
expressionKey = "test",
|
||||
rawExpression = expression,
|
||||
converter = null,
|
||||
validator = { true },
|
||||
logger = ParsingErrorLogger.ASSERT,
|
||||
typeHelper = TYPE_HELPER_STRING
|
||||
)
|
||||
}
|
||||
@@ -44,7 +44,7 @@ class DivPreloaderTest {
|
||||
private val extensionHandlers = listOf<DivExtensionHandler>(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)
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<Div>,
|
||||
transitionIn: DivAppearanceTransition? = null
|
||||
): Div {
|
||||
return Div.Container(
|
||||
DivContainer(
|
||||
orientation = orientation.asExpression(),
|
||||
items = items,
|
||||
transitionIn = transitionIn
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun divGrid(items: List<Div>, transitionIn: DivAppearanceTransition? = null): Div {
|
||||
return Div.Grid(
|
||||
DivGrid(
|
||||
|
||||
@@ -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(""))
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<View> = 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<View> = 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<ClickableSpan>()?.firstOrNull()
|
||||
assertThat("No link found in $spannable", spannable, notNullValue())
|
||||
span?.onClick(view)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<View> = 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<View> = 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<View>(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<View>(
|
||||
description = "Waits view to become completely displayed.",
|
||||
timeoutInMillis = timeoutInMillis,
|
||||
condition = { view -> isCompletelyDisplayed().matches(view) },
|
||||
targetAction = targetAction,
|
||||
)
|
||||
|
||||
inline fun <reified ViewType : View> waitForViewConditionAndPerformAction(
|
||||
description: String,
|
||||
targetAction: ViewAction? = null,
|
||||
timeoutInMillis: Long = WAITING_TIMEOUT,
|
||||
condition: Matcher<ViewType>,
|
||||
) = waitForViewConditionAndPerformAction(description, targetAction, timeoutInMillis,
|
||||
{ view: ViewType -> condition.matches(view) }
|
||||
)
|
||||
|
||||
inline fun <reified ViewType : View> waitForViewConditionAndPerformAction(
|
||||
description: String,
|
||||
targetAction: ViewAction? = null,
|
||||
timeoutInMillis: Long = WAITING_TIMEOUT,
|
||||
crossinline condition: (view: ViewType) -> Boolean,
|
||||
): ViewAction {
|
||||
return object : ViewAction {
|
||||
|
||||
override fun getConstraints(): Matcher<View> = 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<View>,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<View>): ViewInteraction? {
|
||||
return try {
|
||||
check(matches(matcher))
|
||||
} catch (e: AssertionFailedError) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun ViewInteraction.isMatching(matcher: Matcher<View>): 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<View>) = 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<ViewHolder>(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<ViewHolder>(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<View> = 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 <T : View> ViewInteraction.configure(block: (T) -> Unit): ViewInteraction {
|
||||
return perform(ViewActions.actionWithAssertions(ConfigureViewAction(block)))
|
||||
}
|
||||
|
||||
private class ConfigureViewAction<T : View>(
|
||||
private val block: (T) -> Unit
|
||||
) : ViewAction {
|
||||
|
||||
override fun getDescription() = "ConfigureViewAction"
|
||||
|
||||
override fun getConstraints(): Matcher<View> = ViewMatchers.isDisplayed()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun perform(uiController: UiController, view: View) {
|
||||
val receiver = view as? T ?: return
|
||||
block(receiver)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -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 <reified T : ViewGroup.LayoutParams> 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")
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<out String>) =
|
||||
|
||||
@@ -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<out String>) =
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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<out String>, 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<out String>, grantResults: IntArray) =
|
||||
permissions.contains(permission) && grantResults[permissions.indexOf(permission)] == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
@@ -41,7 +41,7 @@ androidx-annotation = "1.9.1"
|
||||
androidx-appcompat = "1.6.1"
|
||||
androidx-browser = "1.8.0"
|
||||
androidx-collection = "1.5.0"
|
||||
androidx-compose-bom = "2024.04.00"
|
||||
androidx-compose-bom = "2026.03.00"
|
||||
androidx-constraint = "2.2.1"
|
||||
androidx-core = "1.16.0"
|
||||
androidx-espresso = "3.7.0"
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.yandex.div.test.data
|
||||
|
||||
import com.yandex.div.evaluable.types.Color
|
||||
import com.yandex.div2.DivBackground
|
||||
import com.yandex.div2.DivSolidBackground
|
||||
|
||||
fun solidBackground(color: Color): DivBackground {
|
||||
return DivBackground.Solid(
|
||||
value = DivSolidBackground(
|
||||
color = constant(color.value)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1,16 +1,24 @@
|
||||
package com.yandex.div.test.data
|
||||
|
||||
import com.yandex.div2.Div
|
||||
import com.yandex.div2.DivBackground
|
||||
import com.yandex.div2.DivContainer
|
||||
import com.yandex.div2.DivEdgeInsets
|
||||
import com.yandex.div2.DivVariable
|
||||
|
||||
fun container(
|
||||
backgrounds: List<DivBackground>? = null,
|
||||
items: List<Div> = emptyList(),
|
||||
margins: DivEdgeInsets? = null,
|
||||
paddings: DivEdgeInsets? = null,
|
||||
variables: List<DivVariable>? = null
|
||||
): Div {
|
||||
return Div.Container(
|
||||
value = DivContainer(
|
||||
background = backgrounds,
|
||||
items = items,
|
||||
margins = margins,
|
||||
paddings = paddings,
|
||||
variables = variables
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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<DivDisappearAction>? = null,
|
||||
id: String? = null,
|
||||
text: String,
|
||||
triggers: List<DivTrigger>? = null,
|
||||
variables: List<DivVariable>? = null,
|
||||
visibilityActions: List<DivVisibilityAction>? = null
|
||||
): Div {
|
||||
return text(
|
||||
disappearActions = disappearActions,
|
||||
id = id,
|
||||
text = constant(text),
|
||||
triggers = triggers,
|
||||
variables = variables,
|
||||
visibilityActions = visibilityActions
|
||||
)
|
||||
}
|
||||
|
||||
fun text(
|
||||
action: DivAction? = null,
|
||||
disappearActions: List<DivDisappearAction>? = null,
|
||||
fontSize: Long = 12,
|
||||
id: String? = null,
|
||||
text: Expression<String>,
|
||||
textColor: Color? = null,
|
||||
triggers: List<DivTrigger>? = null,
|
||||
variables: List<DivVariable>? = null,
|
||||
visibilityActions: List<DivVisibilityAction>? = 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
|
||||
|
||||