mirror of
https://github.com/divkit/divkit.git
synced 2026-05-07 20:02:32 +00:00
Added compose module
commit_hash:929b87b6271554e0fc8433fd817424d45077af19
This commit is contained in:
@@ -596,6 +596,19 @@
|
||||
"client/android/compose-interop/proguard-rules.pro":"divkit/public/client/android/compose-interop/proguard-rules.pro",
|
||||
"client/android/compose-interop/src/main/java/com/yandex/div/compose/interop/DivContexts.kt":"divkit/public/client/android/compose-interop/src/main/java/com/yandex/div/compose/interop/DivContexts.kt",
|
||||
"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/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/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/views/DivBlockView.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivBlockView.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivContainerView.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivContainerView.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivImageView.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivImageView.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivTextView.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/DivTextView.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/Utils.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/Utils.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/modifiers/BackgroundModifiers.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/modifiers/BackgroundModifiers.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/modifiers/Modifiers.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/modifiers/Modifiers.kt",
|
||||
"client/android/compose/src/main/kotlin/com/yandex/div/compose/views/modifiers/SizeModifiers.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/modifiers/SizeModifiers.kt",
|
||||
"client/android/div-common.gradle":"divkit/public/client/android/div-common.gradle",
|
||||
"client/android/div-core/build.gradle":"divkit/public/client/android/div-core/build.gradle",
|
||||
"client/android/div-core/proguard-rules.pro":"divkit/public/client/android/div-core/proguard-rules.pro",
|
||||
|
||||
@@ -30,6 +30,7 @@ buildscript {
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.kotlin.multiplatform.library) apply false
|
||||
alias(libs.plugins.android.library) apply false
|
||||
alias(libs.plugins.kotlin.multiplatform) apply false
|
||||
}
|
||||
|
||||
@@ -124,6 +125,7 @@ buildTimeTracker {
|
||||
apiValidation {
|
||||
ignoredProjects += [
|
||||
"api-generator-test",
|
||||
"compose",
|
||||
"divkit-demo-app",
|
||||
"divkit-perftests",
|
||||
"divkit-regression-testing",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.compose.compiler)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
}
|
||||
|
||||
apply from: "${project.projectDir}/../div-library.gradle"
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
}
|
||||
|
||||
apply(from = "../div-library.gradle")
|
||||
|
||||
android {
|
||||
namespace = "com.yandex.div.compose"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":div-data"))
|
||||
|
||||
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)
|
||||
|
||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.yandex.div.compose
|
||||
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import androidx.annotation.MainThread
|
||||
import com.yandex.div.json.expressions.ExpressionResolver
|
||||
|
||||
class DivContext @MainThread internal constructor(
|
||||
baseContext: Context,
|
||||
internal val expressionResolver: ExpressionResolver
|
||||
) : ContextWrapper(baseContext)
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.yandex.div.compose
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.yandex.div.compose.views.DivBlockView
|
||||
import com.yandex.div2.DivData
|
||||
|
||||
@Composable
|
||||
fun DivView(
|
||||
data: DivData,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
DivBlockView(
|
||||
data = data.states.first().div,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
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 androidx.core.net.toUri
|
||||
import com.yandex.div.json.expressions.Expression
|
||||
import com.yandex.div.json.expressions.ExpressionResolver
|
||||
import com.yandex.div2.Div
|
||||
import com.yandex.div2.DivBackground
|
||||
import com.yandex.div2.DivContainer
|
||||
import com.yandex.div2.DivData
|
||||
import com.yandex.div2.DivEdgeInsets
|
||||
import com.yandex.div2.DivFixedSize
|
||||
import com.yandex.div2.DivImage
|
||||
import com.yandex.div2.DivSize
|
||||
import com.yandex.div2.DivSolidBackground
|
||||
import com.yandex.div2.DivText
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun DivViewPreview() {
|
||||
val divContext = DivContext(
|
||||
baseContext = LocalContext.current,
|
||||
expressionResolver = ExpressionResolver.EMPTY
|
||||
)
|
||||
CompositionLocalProvider(LocalContext provides divContext) {
|
||||
DivView(data = testData)
|
||||
}
|
||||
}
|
||||
|
||||
private val testData = DivData(
|
||||
logId = "preview",
|
||||
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.Image(
|
||||
value = DivImage(
|
||||
width = fixedSize(48),
|
||||
height = fixedSize(48),
|
||||
imageUrl = constant("https://yastatic.net/s3/home/divkit/logo.png".toUri())
|
||||
)
|
||||
),
|
||||
Div.Text(
|
||||
value = DivText(
|
||||
fontSize = constant(36),
|
||||
text = constant("Hello!"),
|
||||
textColor = constant(0xFFBF0000.toInt()),
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
private fun <T : Any> constant(value: T): Expression<T> {
|
||||
return Expression.ConstantExpression(value)
|
||||
}
|
||||
|
||||
private fun fixedSize(value: Long): DivSize {
|
||||
return DivSize.Fixed(
|
||||
value = DivFixedSize(value = constant(value))
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.yandex.div.compose.views
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.yandex.div.compose.views.modifiers.apply
|
||||
import com.yandex.div2.Div
|
||||
|
||||
@Composable
|
||||
internal fun DivBlockView(
|
||||
data: Div,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val modifier = modifier.apply(data.value())
|
||||
when (data) {
|
||||
is Div.Container -> DivContainerView(modifier, data.value)
|
||||
is Div.Image -> DivImageView(modifier, data.value)
|
||||
is Div.Text -> DivTextView(modifier, data.value)
|
||||
else -> TODO()
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.yandex.div.compose.views
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.yandex.div2.Div
|
||||
import com.yandex.div2.DivContainer
|
||||
|
||||
@Composable
|
||||
internal fun DivContainerView(
|
||||
modifier: Modifier,
|
||||
data: DivContainer
|
||||
) {
|
||||
when (data.orientation.evaluate()) {
|
||||
DivContainer.Orientation.HORIZONTAL ->
|
||||
HorizontalView(
|
||||
modifier = modifier,
|
||||
items = data.items.orEmpty()
|
||||
)
|
||||
|
||||
DivContainer.Orientation.VERTICAL ->
|
||||
VerticalView(
|
||||
modifier = modifier,
|
||||
items = data.items.orEmpty()
|
||||
)
|
||||
|
||||
DivContainer.Orientation.OVERLAP ->
|
||||
OverlapView(
|
||||
modifier = modifier,
|
||||
items = data.items.orEmpty()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun HorizontalView(
|
||||
modifier: Modifier,
|
||||
items: List<Div>
|
||||
) {
|
||||
Row(modifier = modifier) {
|
||||
items.forEach {
|
||||
DivBlockView(data = it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun VerticalView(
|
||||
modifier: Modifier,
|
||||
items: List<Div>
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
items.forEach {
|
||||
DivBlockView(data = it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OverlapView(
|
||||
modifier: Modifier,
|
||||
items: List<Div>
|
||||
) {
|
||||
Box(modifier = modifier) {
|
||||
items.forEach {
|
||||
DivBlockView(data = it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.yandex.div.compose.views
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import coil3.compose.AsyncImage
|
||||
import com.yandex.div2.DivImage
|
||||
|
||||
@Composable
|
||||
internal fun DivImageView(
|
||||
modifier: Modifier,
|
||||
data: DivImage
|
||||
) {
|
||||
AsyncImage(
|
||||
modifier = modifier,
|
||||
model = data.imageUrl.evaluate(),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.yandex.div.compose.views
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.yandex.div2.DivText
|
||||
|
||||
@Composable
|
||||
internal fun DivTextView(
|
||||
modifier: Modifier,
|
||||
data: DivText
|
||||
) {
|
||||
Text(
|
||||
text = data.text.evaluate(expressionResolver),
|
||||
modifier = modifier,
|
||||
color = data.textColor.evaluate().toColor(),
|
||||
fontSize = data.fontSize.evaluate().toFloat().sp,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.yandex.div.compose.views
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.yandex.div.compose.DivContext
|
||||
import com.yandex.div.json.expressions.Expression
|
||||
import com.yandex.div.json.expressions.ExpressionResolver
|
||||
|
||||
internal fun Int.toColor(): Color {
|
||||
return Color(this)
|
||||
}
|
||||
|
||||
internal fun Long.toDp(): Dp {
|
||||
return toFloat().dp
|
||||
}
|
||||
|
||||
internal val divContext: DivContext
|
||||
@Composable
|
||||
get() = LocalContext.current as DivContext
|
||||
|
||||
internal val expressionResolver: ExpressionResolver
|
||||
@Composable
|
||||
get() = divContext.expressionResolver
|
||||
|
||||
@Composable
|
||||
internal fun <T : Any> Expression<T>.evaluate(): T {
|
||||
return evaluate(expressionResolver)
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.yandex.div.compose.views.modifiers
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.yandex.div.compose.views.evaluate
|
||||
import com.yandex.div.compose.views.toColor
|
||||
import com.yandex.div2.DivBackground
|
||||
import com.yandex.div2.DivSolidBackground
|
||||
|
||||
@Composable
|
||||
internal fun Modifier.backgrounds(value: List<DivBackground>): Modifier {
|
||||
var modifier = this
|
||||
value.forEach { background ->
|
||||
when (background) {
|
||||
is DivBackground.Solid ->
|
||||
modifier = modifier.solidBackground(background.value)
|
||||
|
||||
is DivBackground.Image -> TODO()
|
||||
is DivBackground.LinearGradient -> TODO()
|
||||
is DivBackground.NinePatch -> TODO()
|
||||
is DivBackground.RadialGradient -> TODO()
|
||||
}
|
||||
}
|
||||
return modifier
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Modifier.solidBackground(value: DivSolidBackground): Modifier {
|
||||
return background(
|
||||
color = value.color.evaluate().toColor()
|
||||
)
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.yandex.div.compose.views.modifiers
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import com.yandex.div.compose.views.evaluate
|
||||
import com.yandex.div.compose.views.toDp
|
||||
import com.yandex.div2.DivBase
|
||||
import com.yandex.div2.DivEdgeInsets
|
||||
|
||||
@Composable
|
||||
internal fun Modifier.apply(data: DivBase): Modifier {
|
||||
var modifier = this
|
||||
.width(data.width)
|
||||
.height(data.height)
|
||||
|
||||
data.margins?.let {
|
||||
modifier = modifier.padding(it)
|
||||
}
|
||||
|
||||
data.background?.let {
|
||||
modifier = modifier.backgrounds(it)
|
||||
}
|
||||
|
||||
data.paddings?.let {
|
||||
modifier = modifier.padding(it)
|
||||
}
|
||||
|
||||
data.id?.let {
|
||||
modifier = modifier.testTag(it)
|
||||
}
|
||||
|
||||
return modifier
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Modifier.padding(value: DivEdgeInsets): Modifier {
|
||||
return padding(
|
||||
start = (value.start ?: value.left).evaluate().toDp(),
|
||||
end = (value.end ?: value.right).evaluate().toDp(),
|
||||
top = value.top.evaluate().toDp(),
|
||||
bottom = value.bottom.evaluate().toDp()
|
||||
)
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.yandex.div.compose.views.modifiers
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.yandex.div.compose.views.evaluate
|
||||
import com.yandex.div.compose.views.toDp
|
||||
import com.yandex.div2.DivSize
|
||||
|
||||
@Composable
|
||||
internal fun Modifier.height(height: DivSize): Modifier {
|
||||
return when (height) {
|
||||
is DivSize.MatchParent -> fillMaxHeight()
|
||||
is DivSize.WrapContent -> wrapContentHeight()
|
||||
is DivSize.Fixed -> height(height.value.value.evaluate().toDp())
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun Modifier.width(width: DivSize): Modifier {
|
||||
return when (width) {
|
||||
is DivSize.MatchParent -> fillMaxWidth()
|
||||
is DivSize.WrapContent -> wrapContentWidth()
|
||||
is DivSize.Fixed -> width(width.value.value.evaluate().toDp())
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,7 @@ androidx-work = { module = "androidx.work:work-runtime-ktx", version.ref = "andr
|
||||
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidx-compose-bom" }
|
||||
androidx-compose-foundation = { module = "androidx.compose.foundation:foundation" }
|
||||
androidx-compose-material3 = { module = "androidx.compose.material3:material3" }
|
||||
androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
|
||||
androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
|
||||
|
||||
@@ -119,6 +120,7 @@ appmetrica-plugin = { module = "io.appmetrica.analytics:gradle", version.ref = "
|
||||
buildTimeTracker = { module = "com.asarkar.gradle:build-time-tracker", version.ref = "buildTimeTracker" }
|
||||
|
||||
coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" }
|
||||
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
|
||||
coil-gif = { module = "io.coil-kt.coil3:coil-gif", version.ref = "coil" }
|
||||
coil-network = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" }
|
||||
coil-network-cachecontrol = { module = "io.coil-kt.coil3:coil-network-cache-control", version.ref = "coil" }
|
||||
@@ -194,8 +196,9 @@ zxing-embedded = { module = "com.journeyapps:zxing-android-embedded", version =
|
||||
[plugins]
|
||||
apiGenerator = { id = "com.yandex.divkit.api-generator", version = "unspecified" }
|
||||
android-kotlin-multiplatform-library = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" }
|
||||
android-library = { id = "com.android.library", version.ref = "agp" }
|
||||
buildkonfig = { id = "com.codingfeline.buildkonfig", version = "0.17.1" }
|
||||
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
kotlin-ksp = { id = "com.google.devtools.ksp", version.ref = "kotlin-ksp" }
|
||||
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
||||
|
||||
@@ -25,6 +25,7 @@ include ':api-generator-test'
|
||||
include ':assertion'
|
||||
include ':beacon'
|
||||
include ':coil'
|
||||
include ':compose'
|
||||
include ':compose-interop'
|
||||
include ':div'
|
||||
include ':div-core'
|
||||
@@ -45,6 +46,7 @@ include ':div-states'
|
||||
include ':div-storage'
|
||||
include ':div-svg'
|
||||
include ':div-video'
|
||||
include ':div-video-m3'
|
||||
include ':divkit-demo-app'
|
||||
include ':divkit-perftests'
|
||||
include ':divkit-regression-testing'
|
||||
@@ -59,7 +61,6 @@ include ':screenshot-test-runtime'
|
||||
include ':ui-test-common'
|
||||
include ':utils'
|
||||
include ':video-custom'
|
||||
include ':div-video-m3'
|
||||
|
||||
def internalSettings = file("settings.internal.gradle")
|
||||
if (internalSettings.exists()) {
|
||||
|
||||
Reference in New Issue
Block a user