Supported compose renderer in demo app

commit_hash:707bc8cb58bdbe08c98f8376b3aae74b8299cbf4
This commit is contained in:
burstein
2026-04-06 18:32:01 +03:00
parent 62a9dcdb87
commit 809ad659b5
12 changed files with 349 additions and 108 deletions
+2
View File
@@ -1961,6 +1961,7 @@
"client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/DemoDivLottieRawResProvider.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/DemoDivLottieRawResProvider.kt",
"client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/DemoGlobalVariablesController.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/DemoGlobalVariablesController.kt",
"client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/DemoNestedScrollView.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/DemoNestedScrollView.kt",
"client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/DemoRendererFacade.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/DemoRendererFacade.kt",
"client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/Div2Activity.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/Div2Activity.kt",
"client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/Div2MetadataBottomSheet.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/Div2MetadataBottomSheet.kt",
"client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/Div2ScenarioActivity.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/Div2ScenarioActivity.kt",
@@ -1990,6 +1991,7 @@
"client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/editor/list/DivEditorViewHolder.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/editor/list/DivEditorViewHolder.kt",
"client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/histogram/DemoHistogramConfiguration.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/histogram/DemoHistogramConfiguration.kt",
"client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/video/DemoVideoCustomImageCache.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/div/video/DemoVideoCustomImageCache.kt",
"client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/font/ComposeFontFamilyProvider.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/font/ComposeFontFamilyProvider.kt",
"client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/font/RobotoFlexTypefaceProvider.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/font/RobotoFlexTypefaceProvider.kt",
"client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/font/YandexSansCondensedTypefaceProvider.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/font/YandexSansCondensedTypefaceProvider.kt",
"client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/regression/RegressionDiv2ViewCreator.kt":"divkit/public/client/android/divkit-demo-app/src/main/java/com/yandex/divkit/demo/regression/RegressionDiv2ViewCreator.kt",
@@ -726,6 +726,25 @@
}
]
},
{
"type": "settings_item",
"items": [
{
"type": "settings_item_title",
"text": "Compose Renderer by default"
},
{
"type": "setting_switch_state",
"id": "compose_renderer",
"active_click_log_id": "set_compose_renderer_disabled",
"active_click_change_state_url": "div-action://set_state?state_id=0/compose_renderer/inactive",
"active_click_change_settings_url": "div-action://set_preferences?name=compose_renderer&value=0",
"inactive_click_log_id": "set_compose_renderer_enabled",
"inactive_click_change_settings_url": "div-action://set_preferences?name=compose_renderer&value=1",
"inactive_click_change_state_url": "div-action://set_state?state_id=0/compose_renderer/active"
}
]
},
{
"type": "text",
"text": "@{app_version}",
@@ -0,0 +1,171 @@
package com.yandex.divkit.demo.div
import android.content.Context
import android.view.View
import android.view.ViewGroup
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.platform.ComposeView
import androidx.core.net.toUri
import com.yandex.div.DivDataTag
import com.yandex.div.compose.DivComposeConfiguration
import com.yandex.div.compose.DivView as ComposeDivView
import com.yandex.div.compose.DivContext as ComposeDivContext
import com.yandex.div.compose.createContext
import com.yandex.div.core.Div2Context
import com.yandex.div.core.annotations.InternalApi
import com.yandex.div.core.expression.variables.DivVariableController
import com.yandex.div.core.view2.Div2View
import com.yandex.div.data.DivParsingEnvironment
import com.yandex.div.json.ParsingErrorLogger
import com.yandex.div2.DivAction
import com.yandex.div2.DivData
import com.yandex.div2.DivPatch
import com.yandex.divkit.demo.font.ComposeFontFamilyProvider
import com.yandex.divkit.demo.utils.applyPatchByConfig
import org.json.JSONException
import org.json.JSONObject
interface DemoRendererFacade {
val view: View
val currentData: DivData?
fun setData(data: DivData, tag: DivDataTag)
fun performAction(action: String)
fun applyPatch(divPatch: DivPatch, errorCallback: () -> Unit): Boolean
fun dismissTooltips()
fun cleanup()
fun deactivate() {
cleanup()
view.visibility = View.GONE
}
fun activate(data: DivData? = null, tag: DivDataTag? = null) {
if (data != null && tag != null && currentData == null) {
setData(data, tag)
}
view.visibility = if (currentData != null) View.VISIBLE else View.GONE
}
}
class Div2RendererFacade(
container: ViewGroup,
private val divContext: Div2Context,
private val actionHandler: DemoDivActionHandler,
) : DemoRendererFacade {
private val divView = Div2View(divContext).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
init {
container.addView(divView)
}
override val view: View get() = divView
override val currentData: DivData? get() = divView.divData
override fun setData(data: DivData, tag: DivDataTag) {
divView.setData(data, tag)
}
override fun performAction(action: String) {
try {
val divAction = parseDivAction(action)
actionHandler.handleAction(divAction, divView, divView.expressionResolver)
} catch (_: JSONException) {
actionHandler.handleActionUrl(action.toUri(), divView)
}
}
override fun applyPatch(divPatch: DivPatch, errorCallback: () -> Unit): Boolean {
divView.applyPatchByConfig(divPatch) {
if (!it) errorCallback()
}
return true
}
override fun dismissTooltips() {
divContext.cancelTooltips()
}
override fun cleanup() {
divView.cleanup()
}
}
@OptIn(InternalApi::class)
class ComposeRendererFacade(
container: ViewGroup,
divVariableController: DivVariableController,
context: Context,
) : DemoRendererFacade {
private val composeDivContext: ComposeDivContext = DivComposeConfiguration(
fontFamilyProvider = ComposeFontFamilyProvider(context),
variableController = divVariableController,
).createContext(baseContext = context)
private val composeView = ComposeView(composeDivContext).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
visibility = View.GONE
}
private val divData = mutableStateOf<DivData?>(null)
init {
container.addView(composeView, 0)
composeView.setContent {
divData.value?.let { data ->
ComposeDivView(data = data)
}
}
}
override val view: View = composeView
override val currentData: DivData? get() = divData.value
override fun setData(data: DivData, tag: DivDataTag) {
divData.value = data
}
override fun performAction(action: String) {
val data = divData.value ?: return
try {
val divAction = parseDivAction(action)
composeDivContext.debugFeatures.performAction(data, divAction)
} catch (_: JSONException) {
val jsonAction = JSONObject().apply {
put("log_id", "menu_action")
put("url", action.toUri().toString())
}
val divAction = DivAction(DivParsingEnvironment(ParsingErrorLogger.LOG), jsonAction)
composeDivContext.debugFeatures.performAction(data, divAction)
}
}
override fun applyPatch(divPatch: DivPatch, errorCallback: () -> Unit): Boolean {
// Not supported yet
return false
}
override fun dismissTooltips() {
// Not supported yet
}
override fun cleanup() {
divData.value = null
}
}
private fun parseDivAction(action: String): DivAction {
val env = DivParsingEnvironment(ParsingErrorLogger.LOG)
return DivAction(env, JSONObject(action))
}
@@ -7,7 +7,6 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import androidx.annotation.WorkerThread
import androidx.appcompat.app.AppCompatActivity
@@ -23,11 +22,10 @@ import com.yandex.div.core.DivErrorsReporter
import com.yandex.div.core.DivViewFacade
import com.yandex.div.core.experiments.Experiment
import com.yandex.div.core.util.SafeAlertDialogBuilder
import com.yandex.div.core.view2.Div2View
import com.yandex.div.data.DivParsingEnvironment
import com.yandex.div.font.YandexSansDisplayDivTypefaceProvider
import com.yandex.div.font.YandexSansDivTypefaceProvider
import com.yandex.div.internal.Assert
import com.yandex.div.internal.KLog
import com.yandex.div.json.ParsingErrorLogger
import com.yandex.div.json.expressions.ExpressionResolver
import com.yandex.div.lottie.DivLottieExtensionHandler
@@ -35,7 +33,6 @@ import com.yandex.div.zoom.DivPinchToZoomConfiguration
import com.yandex.div.zoom.DivPinchToZoomExtensionHandler
import com.yandex.div2.DivAction
import com.yandex.div2.DivData
import com.yandex.div2.DivPatch
import com.yandex.divkit.demo.Container
import com.yandex.divkit.demo.R
import com.yandex.divkit.demo.databinding.ActivityDiv2ScenarioBinding
@@ -43,7 +40,6 @@ import com.yandex.divkit.demo.div.editor.DivEditorActivityStateKeeper
import com.yandex.divkit.demo.div.editor.DivEditorLogger
import com.yandex.divkit.demo.div.editor.DivEditorParsingErrorLogger
import com.yandex.divkit.demo.div.editor.DivEditorPresenter
import com.yandex.divkit.demo.div.editor.DivEditorState
import com.yandex.divkit.demo.div.editor.DivEditorUi
import com.yandex.divkit.demo.div.editor.DivEditorWebController
import com.yandex.divkit.demo.div.editor.list.DivEditorAdapter
@@ -51,7 +47,6 @@ import com.yandex.divkit.demo.div.histogram.LoggingHistogramBridge
import com.yandex.divkit.demo.font.RobotoFlexTypefaceProvider
import com.yandex.divkit.demo.font.YandexSansCondensedTypefaceProvider
import com.yandex.divkit.demo.utils.DivkitDemoUriHandler
import com.yandex.divkit.demo.utils.applyPatchByConfig
import com.yandex.divkit.demo.utils.coroutineScope
import com.yandex.divkit.demo.utils.lifecycleOwner
import com.yandex.divkit.demo.utils.loadText
@@ -64,7 +59,6 @@ import kotlinx.coroutines.withContext
import org.json.JSONException
import org.json.JSONObject
import java.util.UUID
import com.yandex.div.internal.KLog
class Div2ScenarioActivity : AppCompatActivity(), Div2MetadataBottomSheet.MetadataHost {
@@ -74,12 +68,12 @@ class Div2ScenarioActivity : AppCompatActivity(), Div2MetadataBottomSheet.Metada
private lateinit var editorStateKeeper: DivEditorActivityStateKeeper
private lateinit var div2Adapter: DivEditorAdapter
private lateinit var divContext: Div2Context
private lateinit var divEditorUi: DivEditorUi
private val actionHandler = DemoDivActionHandler(Container.uriHandler.apply { handlingActivity = this@Div2ScenarioActivity })
private val globalVariableController = DemoGlobalVariablesController()
private val preferences by lazy { getSharedPreferences("div2", Context.MODE_PRIVATE) }
private var json: String? = null
private var url: String? = null
private lateinit var div2View: Div2View
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -150,23 +144,33 @@ class Div2ScenarioActivity : AppCompatActivity(), Div2MetadataBottomSheet.Metada
div2Adapter = DivEditorAdapter(divContext)
globalVariableController.bindWith(divContext)
setupDiv2View()
val viewRenderer = Div2RendererFacade(binding.singleContainer, divContext, actionHandler)
val composeRenderer = ComposeRendererFacade(
binding.singleContainer,
divContext.divVariableController,
this
)
with(binding.div2Recycler) {
layoutManager = LinearLayoutManager(this@Div2ScenarioActivity, RecyclerView.VERTICAL, false)
adapter = div2Adapter
}
val divEditorUi = DivEditorUi(
divEditorUi = DivEditorUi(
this,
binding.metadataButton,
binding.error,
binding.singleContainer,
div2View,
divContext,
viewRenderer,
composeRenderer,
binding.div2Recycler,
div2Adapter,
this,
Container.flagPreferenceProvider.getExperimentFlag(Experiment.SHOW_RENDERING_TIME)
)
divEditorUi.useComposeRenderer = Container.preferences.useComposeRenderer
updateToolbarTitle()
val editorLogger = DivEditorLogger(this::setError)
editorStateKeeper = ViewModelProvider(this)[DivEditorActivityStateKeeper::class.java]
@@ -195,14 +199,19 @@ class Div2ScenarioActivity : AppCompatActivity(), Div2MetadataBottomSheet.Metada
initDiv()
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.div2_scenario_menu, menu)
menu?.findItem(R.id.div2_compose_renderer)?.isChecked = divEditorUi.useComposeRenderer
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.div2_compose_renderer -> {
item.isChecked = !item.isChecked
toggleRenderer()
true
}
R.id.div2_perform_action -> {
showActionDialog()
true
@@ -212,7 +221,11 @@ class Div2ScenarioActivity : AppCompatActivity(), Div2MetadataBottomSheet.Metada
true
}
R.id.div2_download_patch -> {
showDownloadDialog()
if (divEditorUi.useComposeRenderer) {
showToast("Patches are not supported in Compose renderer")
} else {
showDownloadDialog()
}
true
}
R.id.div2_load_div_json -> {
@@ -220,7 +233,7 @@ class Div2ScenarioActivity : AppCompatActivity(), Div2MetadataBottomSheet.Metada
true
}
R.id.div2_close_all_tooltips -> {
divContext.cancelTooltips()
divEditorUi.activeRenderer.dismissTooltips()
true
}
else -> super.onOptionsItemSelected(item)
@@ -255,7 +268,7 @@ class Div2ScenarioActivity : AppCompatActivity(), Div2MetadataBottomSheet.Metada
return@launch
}
div2View.setData(divData, DivDataTag(UUID.randomUUID().toString()))
divEditorUi.setDivData(divData, DivDataTag(UUID.randomUUID().toString()))
showToast("New div json applied!")
}
}
@@ -299,13 +312,7 @@ class Div2ScenarioActivity : AppCompatActivity(), Div2MetadataBottomSheet.Metada
.setPositiveButton("Perform action") { _, _ ->
val action = editText.text.toString()
preferences.edit().putString(KEY_DIV2_ACTION_URL, action).apply()
try {
val env = DivParsingEnvironment(ParsingErrorLogger.LOG)
val jsonObject = JSONObject(action)
actionHandler.handleAction(DivAction(env, jsonObject), div2View, div2View.expressionResolver)
} catch (e: JSONException) {
actionHandler.handleActionUrl(Uri.parse(action), div2View)
}
divEditorUi.activeRenderer.performAction(action)
}
adb.create().show()
}
@@ -360,7 +367,7 @@ class Div2ScenarioActivity : AppCompatActivity(), Div2MetadataBottomSheet.Metada
return@launch
}
applyPatch(divPatch) {
divEditorUi.applyPatch(divPatch) {
longToast("Error while applying patch!")
}
}
@@ -368,33 +375,20 @@ class Div2ScenarioActivity : AppCompatActivity(), Div2MetadataBottomSheet.Metada
adb.create().show()
}
private fun applyPatch(divPatch: DivPatch, errorCallback: () -> Unit) {
if (isSingleCardState()) {
div2View.applyPatchByConfig(divPatch) {
if (!it) errorCallback()
}
private fun toggleRenderer() {
divEditorUi.useComposeRenderer = !divEditorUi.useComposeRenderer
Container.preferences.useComposeRenderer = divEditorUi.useComposeRenderer
updateToolbarTitle()
}
private fun updateToolbarTitle() {
binding.toolbar.title = if (divEditorUi.useComposeRenderer) {
getString(R.string.demo) + " (Compose)"
} else {
div2Adapter.applyPatch(divPatch, errorCallback)
getString(R.string.demo)
}
}
private fun isSingleCardState(): Boolean {
if (!this::editorStateKeeper.isInitialized) {
return false
}
val state = editorStateKeeper.state
return state is DivEditorState.DivReceivedState && state.isSingleCard
}
private fun setupDiv2View() {
div2View = Div2View(divContext)
div2View.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
binding.singleContainer.addView(div2View)
}
@WorkerThread
private suspend fun loadJson(uri: Uri): JSONObject {
val data = when (uri.scheme) {
@@ -12,11 +12,13 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.yandex.div.DivDataTag
import com.yandex.div.core.Div2Context
import com.yandex.div.core.expression.variables.DivVariablesParser
import com.yandex.div.core.view2.Div2View
import com.yandex.div.data.Variable
import com.yandex.div.internal.Assert
import com.yandex.div2.DivData
import com.yandex.div2.DivPatch
import com.yandex.div2.DivSize
import com.yandex.divkit.demo.Container
import com.yandex.divkit.demo.div.DemoRendererFacade
import com.yandex.divkit.demo.div.Div2MetadataBottomSheet
import com.yandex.divkit.demo.div.editor.DivEditorScreenshot.takeScreenshot
import com.yandex.divkit.demo.div.editor.list.DivEditorAdapter
@@ -28,7 +30,9 @@ class DivEditorUi(
private val metadataButton: FloatingActionButton,
private val failedTextMessage: TextView,
private val divContainer: ViewGroup,
private val div2View: Div2View,
private val divContext: Div2Context,
private val viewRenderer: DemoRendererFacade,
private val composeRenderer: DemoRendererFacade,
private val div2Recycler: RecyclerView,
private val div2Adapter: DivEditorAdapter,
private val metadataHost: Div2MetadataBottomSheet.MetadataHost,
@@ -38,6 +42,22 @@ class DivEditorUi(
var onDiv2ViewDrawnListener: ((bitmap: Bitmap) -> Unit)? = null
var hasTemplates: Boolean = false
val activeRenderer: DemoRendererFacade
get() = if (useComposeRenderer) composeRenderer else viewRenderer
private val inactiveRenderer: DemoRendererFacade
get() = if (useComposeRenderer) viewRenderer else composeRenderer
var useComposeRenderer: Boolean = false
set(value) {
field = value
applyRendererVisibility()
}
private var isSingleCard: Boolean = false
private var lastDivData: DivData? = null
private var lastDivDataTag: DivDataTag? = null
private val debounceOnViewDrawObserver = DebounceOnViewDrawObserver {
updateRenderTime()
activity.takeScreenshot { screenshot: Bitmap ->
@@ -46,35 +66,57 @@ class DivEditorUi(
}
init {
div2View.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
div2View.divData?.states?.firstOrNull { it.stateId == div2View.currentStateId }?.let {
val height = when (it.div.value().height) {
is DivSize.MatchParent -> ViewGroup.LayoutParams.MATCH_PARENT
else -> ViewGroup.LayoutParams.WRAP_CONTENT
}
if (divContainer.layoutParams.height == height) return@addOnLayoutChangeListener
divContainer.doOnPreDraw {
divContainer.layoutParams.height = height
divContainer.requestLayout()
}
}
viewRenderer.view.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
val divData = viewRenderer.currentData ?: return@addOnLayoutChangeListener
divContainer.doOnPreDraw { adjustContainerHeight(divData) }
}
}
fun updateState(newState: DivEditorState) {
when (newState) {
is DivEditorState.InitialState -> {
showInitialState()
}
is DivEditorState.LoadingState -> {
showLoadingState()
}
is DivEditorState.DivReceivedState -> {
showDivReceivedState(newState)
}
is DivEditorState.FailedState -> {
showFailedState(newState)
}
is DivEditorState.InitialState -> showInitialState()
is DivEditorState.LoadingState -> showLoadingState()
is DivEditorState.DivReceivedState -> showDivReceivedState(newState)
is DivEditorState.FailedState -> showFailedState(newState)
}
}
fun setDivData(divData: DivData, tag: DivDataTag = DivDataTag(divData.logId)) {
isSingleCard = true
setRendererData(divData, tag)
applyRendererVisibility()
}
fun applyPatch(divPatch: DivPatch, errorCallback: () -> Unit) {
if (isSingleCard) {
activeRenderer.applyPatch(divPatch, errorCallback)
} else {
div2Adapter.applyPatch(divPatch, errorCallback)
}
}
private fun setRendererData(data: DivData, tag: DivDataTag) {
lastDivData = data
lastDivDataTag = tag
activeRenderer.setData(data, tag)
}
private fun applyRendererVisibility() {
if (!isSingleCard) return
inactiveRenderer.deactivate()
activeRenderer.activate(lastDivData, lastDivDataTag)
activeRenderer.currentData?.let { adjustContainerHeight(it) }
}
private fun adjustContainerHeight(divData: DivData) {
val rootHeight = divData.states.firstOrNull()?.div?.value()?.height
val containerHeight = when (rootHeight) {
is DivSize.MatchParent -> ViewGroup.LayoutParams.MATCH_PARENT
else -> ViewGroup.LayoutParams.WRAP_CONTENT
}
if (divContainer.layoutParams.height != containerHeight) {
divContainer.layoutParams.height = containerHeight
divContainer.requestLayout()
}
}
@@ -125,16 +167,17 @@ class DivEditorUi(
hideAll()
tryDeclareContextVariables(state.rawDiv)
isSingleCard = state.isSingleCard
if (state.isSingleCard) {
div2View.visibility = VISIBLE
val card = state.divDataList.first()
setRendererData(card, DivDataTag(card.logId))
div2Recycler.visibility = GONE
div2Adapter.clearList()
val card = state.divDataList.first()
div2View.setData(card, DivDataTag(card.logId))
applyRendererVisibility()
} else {
div2Recycler.visibility = VISIBLE
div2View.visibility = GONE
div2View.cleanup()
viewRenderer.cleanup()
composeRenderer.cleanup()
div2Adapter.setList(state.divDataList)
div2Recycler.viewTreeObserver.addOnDrawListener(debounceOnViewDrawObserver)
}
@@ -148,13 +191,14 @@ class DivEditorUi(
val result: List<Variable> = DivVariablesParser.parse(variables) { e ->
Assert.fail("Error during parsing of variable", e)
}
(div2View.context as Div2Context).divVariableController.declare(*result.toTypedArray())
divContext.divVariableController.declare(*result.toTypedArray())
}
private fun hideAll() {
metadataButton.visibility = GONE
failedTextMessage.visibility = GONE
viewRenderer.view.visibility = GONE
composeRenderer.view.visibility = GONE
divContainer.viewTreeObserver.removeOnDrawListener(debounceOnViewDrawObserver)
}
}
@@ -0,0 +1,25 @@
package com.yandex.divkit.demo.font
import android.content.Context
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import com.yandex.div.compose.DivFontFamilyProvider
import com.yandex.div.core.font.DivTypefaceProvider
import com.yandex.div.font.YandexSansDisplayDivTypefaceProvider
import com.yandex.div.font.YandexSansDivTypefaceProvider
class ComposeFontFamilyProvider(context: Context) : DivFontFamilyProvider {
private val defaultProvider = YandexSansDivTypefaceProvider(context)
private val additionalProviders = mapOf(
"display" to YandexSansDisplayDivTypefaceProvider(context) as DivTypefaceProvider,
"condensed" to YandexSansCondensedTypefaceProvider(context) as DivTypefaceProvider,
"roboto_flex" to RobotoFlexTypefaceProvider(context) as DivTypefaceProvider,
)
override fun getFontFamily(fontFamilyName: String?, weight: FontWeight): FontFamily {
val provider = fontFamilyName?.let { additionalProviders[it] } ?: defaultProvider
val typeface = provider.getTypefaceFor(weight.weight) ?: return FontFamily.Default
return FontFamily(typeface)
}
}
@@ -4,28 +4,21 @@ import android.os.Bundle
import android.widget.LinearLayout
import androidx.activity.ComponentActivity
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import coil3.ImageLoader
import coil3.request.allowHardware
import com.yandex.div.compose.DivComposeConfiguration
import com.yandex.div.compose.DivContext
import com.yandex.div.compose.DivFontFamilyProvider
import com.yandex.div.compose.DivView
import com.yandex.div.compose.internal.ImageLoaderProvider
import com.yandex.div.compose.createContext
import com.yandex.div.compose.internal.DivDebugConfiguration
import com.yandex.div.core.annotations.InternalApi
import com.yandex.div.core.font.DivTypefaceProvider
import com.yandex.div.data.DivParsingEnvironment
import com.yandex.div.font.YandexSansDivTypefaceProvider
import com.yandex.div.font.YandexSansDisplayDivTypefaceProvider
import com.yandex.div.json.ParsingErrorLogger
import com.yandex.div2.DivAction
import com.yandex.div2.DivData
import com.yandex.divkit.demo.R
import com.yandex.divkit.demo.font.RobotoFlexTypefaceProvider
import com.yandex.divkit.demo.font.YandexSansCondensedTypefaceProvider
import com.yandex.divkit.demo.font.ComposeFontFamilyProvider
import org.json.JSONObject
/**
@@ -51,19 +44,11 @@ class DivComposeScreenshotActivity : ComponentActivity() {
.build()
}
val defaultProvider = YandexSansDivTypefaceProvider(this)
val additionalProviders = mapOf(
"display" to YandexSansDisplayDivTypefaceProvider(this) as DivTypefaceProvider,
"condensed" to YandexSansCondensedTypefaceProvider(this) as DivTypefaceProvider,
"roboto_flex" to RobotoFlexTypefaceProvider(this) as DivTypefaceProvider,
)
val fontFamilyProvider = TypefaceProviderFontFamilyAdapter(defaultProvider, additionalProviders)
divContext = DivComposeConfiguration(
debugConfiguration = DivDebugConfiguration(
imageLoaderProvider = imageLoaderProvider
),
fontFamilyProvider = fontFamilyProvider,
fontFamilyProvider = ComposeFontFamilyProvider(this),
).createContext(baseContext = this)
intent.extras?.getString(EXTRA_DIV_ASSET_NAME)?.let {
@@ -102,15 +87,3 @@ class DivComposeScreenshotActivity : ComponentActivity() {
const val EXTRA_DIV_ASSET_NAME = "DivComposeScreenshotActivity.EXTRA_DIV_ASSET_NAME"
}
}
private class TypefaceProviderFontFamilyAdapter(
private val defaultProvider: DivTypefaceProvider,
private val additionalProviders: Map<String, DivTypefaceProvider>,
) : DivFontFamilyProvider {
override fun getFontFamily(fontFamilyName: String?, weight: FontWeight): FontFamily {
val provider = fontFamilyName?.let { additionalProviders[it] } ?: defaultProvider
val typeface = provider.getTypefaceFor(weight.weight) ?: return FontFamily.Default
return FontFamily(typeface)
}
}
@@ -27,4 +27,6 @@ class Preferences(context: Context) : DivkitDemoPreferences(context) {
var limitImageBitmapSizeEnabled by BooleanPreference(true)
var visualAssertionHandlerEnabled by BooleanPreference(BuildConfig.DEBUG)
var useComposeRenderer by BooleanPreference(false)
}
@@ -23,6 +23,7 @@ const val RENDER_EFFECT_ENABLED = "render_effect_enabled"
const val BACKGROUND_BINDING = "background_binding"
const val LIMIT_IMAGE_BITMAP_SIZE_ENABLED = "limit_image_bitmap_size_enabled"
const val VISUAL_ASSERTION_HANDLER = "visual_assertion_handler"
const val COMPOSE_RENDERER = "compose_renderer"
const val NIGHT_MODE = "night_mode"
const val NIGHT_MODE_NIGHT = "NIGHT"
@@ -62,6 +63,7 @@ internal object SettingsActionHandler {
BACKGROUND_BINDING -> Container.preferences.useBackgroundBinding = valueInt.toBooleanOrException()
LIMIT_IMAGE_BITMAP_SIZE_ENABLED -> Container.preferences.limitImageBitmapSizeEnabled = valueInt.toBooleanOrException()
VISUAL_ASSERTION_HANDLER -> Container.preferences.visualAssertionHandlerEnabled = valueInt.toBooleanOrException()
COMPOSE_RENDERER -> Container.preferences.useComposeRenderer = valueInt.toBooleanOrException()
else -> return false
}
@@ -108,6 +108,7 @@ class SettingsActivity : AppCompatActivity() {
val useAsyncBinding = Container.preferences.useBackgroundBinding
setPreferenceState(BACKGROUND_BINDING, useAsyncBinding)
setPreferenceState(VISUAL_ASSERTION_HANDLER, Container.preferences.visualAssertionHandlerEnabled)
setPreferenceState(COMPOSE_RENDERER, Container.preferences.useComposeRenderer)
}
private fun setPreferenceState(name: String, flag: Experiment) {
@@ -2,6 +2,13 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/div2_compose_renderer"
android:orderInCategory="50"
android:checkable="true"
app:showAsAction="never"
android:title="@string/compose_renderer" />
<item
android:id="@+id/div2_perform_action"
android:orderInCategory="200"
@@ -33,4 +33,5 @@
<string name="load_patch">Load patch</string>
<string name="load_div_json">Load another div json</string>
<string name="close_all_tooltips">Close all tooltips</string>
<string name="compose_renderer">Compose Renderer (Demo)</string>
</resources>