mirror of
https://github.com/divkit/divkit.git
synced 2026-06-06 20:07:59 +00:00
Supported compose renderer in demo app
commit_hash:707bc8cb58bdbe08c98f8376b3aae74b8299cbf4
This commit is contained in:
@@ -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}",
|
||||
|
||||
+171
@@ -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))
|
||||
}
|
||||
+40
-46
@@ -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) {
|
||||
|
||||
+77
-33
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+25
@@ -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)
|
||||
}
|
||||
}
|
||||
+2
-29
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -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)
|
||||
}
|
||||
|
||||
+2
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user