DivComposeConfiguration & DivDebugConfiguration refactoring

commit_hash:b90f514d2c683f856b7fcc3d3e77426792647917
This commit is contained in:
pkurchatov
2026-04-09 19:10:34 +03:00
parent b73027c043
commit 6b6fc2b603
17 changed files with 127 additions and 68 deletions
@@ -20,8 +20,10 @@ import com.yandex.div2.DivEdgeInsets
@Preview
@Composable
fun DivViewPreview() {
val divContext = DivComposeConfiguration()
.createContext(baseContext = LocalContext.current)
val divContext = DivContext(
baseContext = LocalContext.current,
configuration = DivComposeConfiguration()
)
CompositionLocalProvider(LocalContext provides divContext) {
DivView(data = testData)
}
@@ -1,14 +1,10 @@
package com.yandex.div.compose
import android.content.Context
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import com.yandex.div.compose.actions.DivExternalActionHandler
import com.yandex.div.compose.dagger.Names
import com.yandex.div.compose.dagger.`Yatagan$DivContextComponent`
import com.yandex.div.compose.internal.DivDebugConfiguration
import com.yandex.div.core.annotations.ExperimentalApi
import com.yandex.div.core.annotations.InternalApi
import com.yandex.div.core.expression.variables.DivVariableController
import com.yandex.yatagan.Module
import com.yandex.yatagan.Provides
@@ -22,7 +18,7 @@ import javax.inject.Named
* val configuration = DivComposeConfiguration(
* reporter = MyReporter()
* )
* val divContext = configuration.createContext(baseContext = activity)
* val divContext = DivContext(baseContext = activity, configuration = configuration)
* ComposeView(divContext).setContent {
* DivView(data = data)
* }
@@ -33,10 +29,6 @@ class DivComposeConfiguration(
@get:Provides
val actionHandler: DivExternalActionHandler = defaultActionHandler,
@get:Provides
@property:InternalApi
val debugConfiguration: DivDebugConfiguration = DivDebugConfiguration(),
@get:Provides
val fontFamilyProvider: DivFontFamilyProvider = defaultFontFamilyProvider,
@@ -48,14 +40,6 @@ class DivComposeConfiguration(
val variableController: DivVariableController = DivVariableController(),
)
fun DivComposeConfiguration.createContext(baseContext: Context): DivContext {
val contextComponent = `Yatagan$DivContextComponent`.builder()
.baseContext(baseContext)
.configuration(this)
.build()
return DivContext(contextComponent)
}
private val defaultActionHandler = object : DivExternalActionHandler {}
private val defaultFontFamilyProvider = object : DivFontFamilyProvider {
@@ -1,11 +1,13 @@
package com.yandex.div.compose
import android.content.Context
import android.content.ContextWrapper
import androidx.annotation.MainThread
import androidx.annotation.VisibleForTesting
import com.yandex.div.compose.context.DivLocalContext
import com.yandex.div.compose.context.DivViewContext
import com.yandex.div.compose.dagger.DivContextComponent
import com.yandex.div.compose.dagger.`Yatagan$DivContextComponent`
import com.yandex.div.compose.internal.DivDebugConfiguration
import com.yandex.div.compose.internal.DivDebugFeatures
import com.yandex.div.core.annotations.ExperimentalApi
import com.yandex.div.core.annotations.InternalApi
@@ -17,7 +19,6 @@ import com.yandex.div2.DivBase
import com.yandex.div2.DivData
import com.yandex.div2.DivTrigger
import com.yandex.div2.DivVariable
import javax.inject.Inject
/**
* An implementation of [android.content.Context] that must be used for composing [DivView]s.
@@ -25,13 +26,33 @@ import javax.inject.Inject
* Example usage:
*
* val configuration = DivComposeConfiguration()
* val divContext = configuration.createContext(baseContext = activity)
* val divContext = DivContext(activity, configuration)
*/
@ExperimentalApi
class DivContext @Inject @MainThread internal constructor(
class DivContext private constructor(
internal val component: DivContextComponent
) : ContextWrapper(component.baseContext) {
/**
* Creates a [DivContext] with provided [DivComposeConfiguration].
*/
constructor(
baseContext: Context,
configuration: DivComposeConfiguration
) : this(createComponent(baseContext, configuration, DivDebugConfiguration()))
/**
* Creates a [DivContext] with provided [DivComposeConfiguration] and [DivDebugConfiguration].
*
* Do not use this constructor in production environment.
*/
@InternalApi
constructor(
baseContext: Context,
configuration: DivComposeConfiguration,
debugConfiguration: DivDebugConfiguration
) : this(createComponent(baseContext, configuration, debugConfiguration))
@InternalApi
@VisibleForTesting
val debugFeatures: DivDebugFeatures
@@ -112,3 +133,15 @@ class DivContext @Inject @MainThread internal constructor(
return localComponent.context
}
}
private fun createComponent(
baseContext: Context,
configuration: DivComposeConfiguration,
debugConfiguration: DivDebugConfiguration
): DivContextComponent {
return `Yatagan$DivContextComponent`.builder()
.baseContext(baseContext)
.configuration(configuration)
.debugConfiguration(debugConfiguration)
.build()
}
@@ -8,6 +8,7 @@ import com.yandex.div.compose.DivReporter
import com.yandex.div.compose.actions.DivActionHandler
import com.yandex.div.compose.actions.VisibilityActionTracker
import com.yandex.div.compose.context.DivViewContextStorage
import com.yandex.div.compose.internal.DivDebugConfiguration
import com.yandex.div.compose.internal.DivDebugFeatures
import com.yandex.div.core.expression.variables.DivVariableController
import com.yandex.yatagan.BindsInstance
@@ -46,6 +47,9 @@ internal interface DivContextComponent {
@BindsInstance
fun configuration(configuration: DivComposeConfiguration): Builder
@BindsInstance
fun debugConfiguration(configuration: DivDebugConfiguration): Builder
fun build(): DivContextComponent
}
}
@@ -43,7 +43,7 @@ internal object DivContextModule {
context: Context,
debugConfiguration: DivDebugConfiguration
): ImageLoader {
val debugImageLoader = debugConfiguration.imageLoaderProvider?.provide(context)
val debugImageLoader = debugConfiguration.imageLoaderProvider?.provide()
if (debugImageLoader != null) {
return debugImageLoader
}
@@ -4,10 +4,12 @@ import com.yandex.div.core.annotations.InternalApi
import kotlinx.coroutines.CoroutineScope
/**
* Provides debug components for [com.yandex.div.compose.DivComposeConfiguration].
* Provides debug configuration for [com.yandex.div.compose.DivContext].
*
* This class is intended for debugging and testing only. Do not use it in the production
* environment.
*
* @see com.yandex.div.compose.DivContext
*/
@InternalApi
class DivDebugConfiguration(
@@ -11,6 +11,8 @@ import javax.inject.Inject
/**
* Provides access to the features not intended to be used in production environment.
*
* @see com.yandex.div.compose.DivContext.debugFeatures
*/
@DivContextScope
@InternalApi
@@ -1,10 +1,17 @@
package com.yandex.div.compose.internal
import android.content.Context
import coil3.ImageLoader
import com.yandex.div.core.annotations.InternalApi
/**
* Provides an [ImageLoader] that is used to load images inside [com.yandex.div.compose.DivView]s.
*
* This interface is intended for testing purposes only. You should not implement it in the
* production environment.
*
* @see DivDebugConfiguration
*/
@InternalApi
fun interface ImageLoaderProvider {
fun provide(context: Context): ImageLoader
fun provide(): ImageLoader
}
@@ -39,10 +39,13 @@ class DivViewRecompositionTest {
private lateinit var divContext: DivContext
@Before
fun setup() {
divContext = DivComposeConfiguration(
reporter = TestReporter()
).createContext(baseContext = activity)
fun setUp() {
divContext = DivContext(
baseContext = activity,
configuration = DivComposeConfiguration(
reporter = TestReporter()
)
)
}
@Test
@@ -42,11 +42,14 @@ class DivViewWithTriggersTest {
private lateinit var divContext: DivContext
@Before
fun setup() {
divContext = DivComposeConfiguration(
reporter = TestReporter(),
variableController = variableController
).createContext(baseContext = activity)
fun setUp() {
divContext = DivContext(
baseContext = activity,
configuration = DivComposeConfiguration(
reporter = TestReporter(),
variableController = variableController
)
)
}
@Test
@@ -43,16 +43,17 @@ class DivViewWithVisibilityActionsTest {
declare(counter, visibility)
}
private val testScope = TestScope()
private val configuration = DivComposeConfiguration(
debugConfiguration = DivDebugConfiguration(
coroutineScope = testScope
),
reporter = TestReporter(),
variableController = variableController
)
private val testScope = TestScope()
private val debugConfiguration = DivDebugConfiguration(
coroutineScope = testScope
)
@Test
fun `visibility action is triggered after delay`() {
setContent(
@@ -271,6 +272,7 @@ class DivViewWithVisibilityActionsTest {
) {
rule.setContent(
configuration = configuration,
debugConfiguration = debugConfiguration,
data = data(content, variables = variables)
)
}
@@ -28,10 +28,13 @@ class IntegrationTest(testCaseParsingResult: ParsingResult<IntegrationTestCase>)
val divData = testCase.parseDivData() ?: return
val variableController = DivVariableController()
val divContext = DivComposeConfiguration(
reporter = Reporter(testCase.logger),
variableController = variableController
).createContext(baseContext = getApplicationContext())
val divContext = DivContext(
baseContext = getApplicationContext(),
configuration = DivComposeConfiguration(
reporter = Reporter(testCase.logger),
variableController = variableController
)
)
testCase.declareResultVariables(
variables = divData.variables ?: emptyList(),
@@ -4,6 +4,7 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import com.yandex.div.compose.expressions.DivComposeExpressionResolver
import com.yandex.div.compose.internal.DivDebugConfiguration
import com.yandex.div.core.expression.variables.DivVariableController
import com.yandex.div.evaluable.function.GeneratedBuiltinFunctionProvider
import com.yandex.div.internal.expressions.FunctionProviderDecorator
@@ -22,10 +23,15 @@ internal fun createExpressionResolver(
fun ComposeContentTestRule.setContent(
configuration: DivComposeConfiguration,
debugConfiguration: DivDebugConfiguration = DivDebugConfiguration(),
data: DivData
) {
setContent {
val divContext = configuration.createContext(baseContext = LocalContext.current)
val divContext = DivContext(
baseContext = LocalContext.current,
configuration = configuration,
debugConfiguration = debugConfiguration
)
CompositionLocalProvider(LocalContext provides divContext) {
DivView(data = data)
}
@@ -9,10 +9,10 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.yandex.div.compose.DivComposeConfiguration
import com.yandex.div.compose.DivContext
import com.yandex.div.compose.TestReporter
import com.yandex.div.compose.context.DivLocalContext
import com.yandex.div.compose.context.LocalDivContext
import com.yandex.div.compose.createContext
import com.yandex.div.compose.createExpressionResolver
import com.yandex.div.core.expression.variables.DivVariableController
import com.yandex.div.data.Variable
@@ -147,11 +147,13 @@ class DivVariableControllerTest {
private fun setContent(content: @Composable () -> Unit) {
composeRule.setContent {
val configuration = DivComposeConfiguration(
reporter = reporter,
variableController = variableController
val divContext = DivContext(
baseContext = LocalContext.current,
configuration = DivComposeConfiguration(
reporter = reporter,
variableController = variableController
)
)
val divContext = configuration.createContext(baseContext = LocalContext.current)
CompositionLocalProvider(
LocalContext provides divContext,
LocalDivContext provides localContext,
@@ -9,10 +9,10 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.yandex.div.compose.DivComposeConfiguration
import com.yandex.div.compose.DivContext
import com.yandex.div.compose.TestReporter
import com.yandex.div.compose.context.DivLocalContext
import com.yandex.div.compose.context.LocalDivContext
import com.yandex.div.compose.createContext
import com.yandex.div.compose.createExpressionResolver
import com.yandex.div.core.expression.variables.DivVariableController
import com.yandex.div.data.DivModelInternalApi
@@ -191,11 +191,13 @@ class ObserveActiveStateTest {
private fun setContent(content: @Composable () -> Unit) {
composeRule.setContent {
val configuration = DivComposeConfiguration(
reporter = reporter,
variableController = variableController
val divContext = DivContext(
baseContext = LocalContext.current,
configuration = DivComposeConfiguration(
reporter = reporter,
variableController = variableController
)
)
val divContext = configuration.createContext(baseContext = LocalContext.current)
CompositionLocalProvider(
LocalContext provides divContext,
LocalDivContext provides localContext,
@@ -10,7 +10,6 @@ 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
@@ -104,10 +103,13 @@ class ComposeRendererFacade(
context: Context,
) : DemoRendererFacade {
private val composeDivContext: ComposeDivContext = DivComposeConfiguration(
fontFamilyProvider = ComposeFontFamilyProvider(context),
variableController = divVariableController,
).createContext(baseContext = context)
private val composeDivContext = ComposeDivContext(
baseContext = context,
configuration = DivComposeConfiguration(
fontFamilyProvider = ComposeFontFamilyProvider(context),
variableController = divVariableController,
)
)
private val composeView = ComposeView(composeDivContext).apply {
layoutParams = ViewGroup.LayoutParams(
@@ -10,7 +10,6 @@ import com.yandex.div.compose.DivComposeConfiguration
import com.yandex.div.compose.DivContext
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.data.DivParsingEnvironment
@@ -37,19 +36,22 @@ class DivComposeScreenshotActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val imageLoaderProvider = ImageLoaderProvider { context ->
ImageLoader.Builder(context = context)
val imageLoaderProvider = ImageLoaderProvider {
ImageLoader.Builder(context = this)
.allowHardware(false)
.eventListener(imageLoadingTracker)
.build()
}
divContext = DivComposeConfiguration(
divContext = DivContext(
baseContext = this,
configuration = DivComposeConfiguration(
fontFamilyProvider = ComposeFontFamilyProvider(this)
),
debugConfiguration = DivDebugConfiguration(
imageLoaderProvider = imageLoaderProvider
),
fontFamilyProvider = ComposeFontFamilyProvider(this),
).createContext(baseContext = this)
)
)
intent.extras?.getString(EXTRA_DIV_ASSET_NAME)?.let {
setDivData(DivAssetReader(this).read(it))