diff --git a/.mapping.json b/.mapping.json index 759df7dd7..c397d74e0 100644 --- a/.mapping.json +++ b/.mapping.json @@ -522,6 +522,11 @@ "client/android/coil/jacoco.excludes":"divkit/public/client/android/coil/jacoco.excludes", "client/android/coil/proguard-rules.pro":"divkit/public/client/android/coil/proguard-rules.pro", "client/android/coil/src/main/java/com/yandex/div/coil/CoilDivImageLoader.kt":"divkit/public/client/android/coil/src/main/java/com/yandex/div/coil/CoilDivImageLoader.kt", + "client/android/compose-interop/build.gradle":"divkit/public/client/android/compose-interop/build.gradle", + "client/android/compose-interop/jacoco.excludes":"divkit/public/client/android/compose-interop/jacoco.excludes", + "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/div-application.gradle":"divkit/public/client/android/div-application.gradle", "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", diff --git a/client/android/compose-interop/build.gradle b/client/android/compose-interop/build.gradle new file mode 100644 index 000000000..ef0d5898f --- /dev/null +++ b/client/android/compose-interop/build.gradle @@ -0,0 +1,35 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +apply from: "${project.projectDir}/../div-library.gradle" +apply from: "${project.projectDir}/../publish-android.gradle" + +android { + namespace "com.yandex.div.compose.interop" + + buildFeatures { + compose true + } + + composeOptions { + kotlinCompilerExtensionVersion = "1.4.8" // compatible with Kotlin 1.8.22 + } +} + +dependencies { + implementation project(path: ":assertion") + implementation project(path: ":logging") + implementation project(path: ":div") + + implementation libs.androidx.core + + implementation platform(libs.androidx.compose.bom) + implementation libs.androidx.compose.foundation + implementation libs.androidx.compose.ui.tooling.preview + debugImplementation libs.androidx.compose.ui.tooling +} + +tasks.withType(KotlinCompile).configureEach { + compilerOptions { + freeCompilerArgs.add("-Xexplicit-api=strict") + } +} diff --git a/client/android/compose-interop/jacoco.excludes b/client/android/compose-interop/jacoco.excludes new file mode 100644 index 000000000..231d1f0fb --- /dev/null +++ b/client/android/compose-interop/jacoco.excludes @@ -0,0 +1,7 @@ +##################### +# Generated classes # +##################### +**/R.class +**/R$*.class +**/BuildConfig.* +**/Manifest*.* diff --git a/client/android/compose-interop/proguard-rules.pro b/client/android/compose-interop/proguard-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/client/android/compose-interop/src/main/java/com/yandex/div/compose/interop/DivContexts.kt b/client/android/compose-interop/src/main/java/com/yandex/div/compose/interop/DivContexts.kt new file mode 100644 index 000000000..6d8537a35 --- /dev/null +++ b/client/android/compose-interop/src/main/java/com/yandex/div/compose/interop/DivContexts.kt @@ -0,0 +1,20 @@ +package com.yandex.div.compose.interop + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.ProvidableCompositionLocal +import androidx.compose.runtime.staticCompositionLocalOf +import com.yandex.div.core.Div2Context + +public val LocalDivContext: ProvidableCompositionLocal = staticCompositionLocalOf { + noLocalDivContextProvided() +} + +@Composable +public fun Div2Context.scoped(content: @Composable () -> Unit) { + CompositionLocalProvider(LocalDivContext provides this, content) +} + +private fun noLocalDivContextProvided(): Nothing { + error("LocalDivContext not found. Make sure 'Div2Context.scoped()' was called.") +} diff --git a/client/android/compose-interop/src/main/java/com/yandex/div/compose/interop/DivViewInterop.kt b/client/android/compose-interop/src/main/java/com/yandex/div/compose/interop/DivViewInterop.kt new file mode 100644 index 000000000..00f9c3eaf --- /dev/null +++ b/client/android/compose-interop/src/main/java/com/yandex/div/compose/interop/DivViewInterop.kt @@ -0,0 +1,36 @@ +@file:Suppress("FunctionName") + +package com.yandex.div.compose.interop + +import android.view.View +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.viewinterop.AndroidView +import com.yandex.div.DivDataTag +import com.yandex.div.core.Div2Context +import com.yandex.div.core.view2.Div2View +import com.yandex.div2.DivData + +private val noopReset: (View) -> Unit = {} + +@Composable +public fun DivView( + data: DivData, + tag: DivDataTag, + modifier: Modifier = Modifier +) { + val context: Div2Context = LocalDivContext.current + AndroidView( + modifier = modifier, + factory = { + Div2View(context) + }, + update = { view -> + view.setData(data, tag) + }, + onReset = noopReset, + onRelease = { view -> + view.cleanup() + } + ) +} diff --git a/client/android/gradle/libs.versions.toml b/client/android/gradle/libs.versions.toml index f78c0dccb..fc08a8957 100644 --- a/client/android/gradle/libs.versions.toml +++ b/client/android/gradle/libs.versions.toml @@ -36,11 +36,12 @@ strikt = "0.28.2" tinkoffAllure = "2.5.1" yatagan = "1.5.0" -androidx-activity = "1.3.1" +androidx-activity = "1.8.2" androidx-annotation = "1.1.0" androidx-appcompat = "1.2.0" androidx-browser = "1.2.0" androidx-collection = "1.1.0" +androidx-compose-bom = "2024.04.00" androidx-constraint = "2.1.1" androidx-core = "1.7.0" androidx-datstore = "1.0.0" @@ -85,6 +86,12 @@ androidx-viewpager = { module = "androidx.viewpager:viewpager", version.ref = "a androidx-viewpager2 = { module = "androidx.viewpager2:viewpager2", version.ref = "androidx-viewpager2" } androidx-work = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-work" } +androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidx-compose-bom" } +androidx-compose-foundation = { module = "androidx.compose.foundation:foundation" } +androidx-compose-ui = { module = "androidx.compose.ui:ui" } +androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } +androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" } + androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso" } androidx-espresso-contrib = { module = "androidx.test.espresso:espresso-contrib", version.ref = "androidx-espresso" } androidx-espresso-intents = { module = "androidx.test.espresso:espresso-intents", version.ref = "androidx-espresso" } @@ -180,4 +187,3 @@ zxing-embedded = { module = "com.journeyapps:zxing-android-embedded", version = apiGenerator = { id = "com.yandex.divkit.api-generator", version = "unspecified" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } screenshotTestPlugin = { id = "com.yandex.test.screenshot-test-plugin", version = "unspecified" } - diff --git a/client/android/settings.gradle b/client/android/settings.gradle index 344c4f04c..172ab6637 100644 --- a/client/android/settings.gradle +++ b/client/android/settings.gradle @@ -25,6 +25,7 @@ include ':api-generator-test' include ':assertion' include ':beacon' include ':coil' +include ':compose-interop' include ':div' include ':div-core' include ':div-data'