diff --git a/.mapping.json b/.mapping.json
index 8dbac65b3..f0ce217c2 100644
--- a/.mapping.json
+++ b/.mapping.json
@@ -604,6 +604,7 @@
"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/androidTest/kotlin/com/yandex/div/compose/DivViewPreview.kt":"divkit/public/client/android/compose/src/androidTest/kotlin/com/yandex/div/compose/DivViewPreview.kt",
+ "client/android/compose/src/main/AndroidManifest.xml":"divkit/public/client/android/compose/src/main/AndroidManifest.xml",
"client/android/compose/src/main/kotlin/com/yandex/div/compose/DivComposeConfiguration.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/DivComposeConfiguration.kt",
"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/DivException.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/DivException.kt",
@@ -640,11 +641,13 @@
"client/android/compose/src/main/kotlin/com/yandex/div/compose/expressions/EvaluatorWarningSender.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/expressions/EvaluatorWarningSender.kt",
"client/android/compose/src/main/kotlin/com/yandex/div/compose/extensions/DivExtensionEnvironment.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/extensions/DivExtensionEnvironment.kt",
"client/android/compose/src/main/kotlin/com/yandex/div/compose/extensions/DivExtensionHandler.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/extensions/DivExtensionHandler.kt",
+ "client/android/compose/src/main/kotlin/com/yandex/div/compose/images/ImageNetworkRestoration.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/images/ImageNetworkRestoration.kt",
"client/android/compose/src/main/kotlin/com/yandex/div/compose/images/ImageRequestListener.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/images/ImageRequestListener.kt",
"client/android/compose/src/main/kotlin/com/yandex/div/compose/images/ImageRequestParams.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/images/ImageRequestParams.kt",
"client/android/compose/src/main/kotlin/com/yandex/div/compose/internal/DivDebugConfiguration.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/internal/DivDebugConfiguration.kt",
"client/android/compose/src/main/kotlin/com/yandex/div/compose/internal/DivDebugFeatures.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/internal/DivDebugFeatures.kt",
"client/android/compose/src/main/kotlin/com/yandex/div/compose/internal/ImageLoaderProvider.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/internal/ImageLoaderProvider.kt",
+ "client/android/compose/src/main/kotlin/com/yandex/div/compose/internal/NetworkRestorationController.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/internal/NetworkRestorationController.kt",
"client/android/compose/src/main/kotlin/com/yandex/div/compose/triggers/DivTriggerStorage.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/triggers/DivTriggerStorage.kt",
"client/android/compose/src/main/kotlin/com/yandex/div/compose/utils/Alignment.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/utils/Alignment.kt",
"client/android/compose/src/main/kotlin/com/yandex/div/compose/utils/Context.kt":"divkit/public/client/android/compose/src/main/kotlin/com/yandex/div/compose/utils/Context.kt",
@@ -729,6 +732,8 @@
"client/android/compose/src/test/kotlin/com/yandex/div/compose/actions/UpdateStructureActionHandlerTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/actions/UpdateStructureActionHandlerTest.kt",
"client/android/compose/src/test/kotlin/com/yandex/div/compose/actions/VisibilityActionTrackerTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/actions/VisibilityActionTrackerTest.kt",
"client/android/compose/src/test/kotlin/com/yandex/div/compose/expressions/DivComposeExpressionResolverTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/expressions/DivComposeExpressionResolverTest.kt",
+ "client/android/compose/src/test/kotlin/com/yandex/div/compose/images/ImageNetworkRestorationTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/images/ImageNetworkRestorationTest.kt",
+ "client/android/compose/src/test/kotlin/com/yandex/div/compose/internal/NetworkRestorationControllerTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/internal/NetworkRestorationControllerTest.kt",
"client/android/compose/src/test/kotlin/com/yandex/div/compose/triggers/DivTriggerStorageTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/triggers/DivTriggerStorageTest.kt",
"client/android/compose/src/test/kotlin/com/yandex/div/compose/utils/DivReporterTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/utils/DivReporterTest.kt",
"client/android/compose/src/test/kotlin/com/yandex/div/compose/utils/ExpressionUtilsTest.kt":"divkit/public/client/android/compose/src/test/kotlin/com/yandex/div/compose/utils/ExpressionUtilsTest.kt",
diff --git a/client/android/compose/src/main/AndroidManifest.xml b/client/android/compose/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..b6303a2e6
--- /dev/null
+++ b/client/android/compose/src/main/AndroidManifest.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/client/android/compose/src/main/kotlin/com/yandex/div/compose/dagger/DivLocalComponent.kt b/client/android/compose/src/main/kotlin/com/yandex/div/compose/dagger/DivLocalComponent.kt
index 23a30006c..491fcb6a5 100644
--- a/client/android/compose/src/main/kotlin/com/yandex/div/compose/dagger/DivLocalComponent.kt
+++ b/client/android/compose/src/main/kotlin/com/yandex/div/compose/dagger/DivLocalComponent.kt
@@ -7,6 +7,7 @@ import com.yandex.div.compose.DivException
import com.yandex.div.compose.DivReporter
import com.yandex.div.compose.actions.DivActionHandlingContext
import com.yandex.div.compose.context.LocalDivViewContext
+import com.yandex.div.compose.internal.NetworkRestorationController
import com.yandex.div.compose.triggers.DivTriggerStorage
import com.yandex.div.compose.triggers.observe
import com.yandex.div.compose.variables.DivVariableAdapter
@@ -30,6 +31,7 @@ internal interface DivLocalComponent {
val actionHandlingContext: DivActionHandlingContext
val expressionResolver: ExpressionResolver
val functionProvider: FunctionProviderDecorator
+ val networkRestorationController: NetworkRestorationController
val reporter: DivReporter
val triggerStorage: DivTriggerStorage
val variableAdapter: DivVariableAdapter
diff --git a/client/android/compose/src/main/kotlin/com/yandex/div/compose/images/ImageNetworkRestoration.kt b/client/android/compose/src/main/kotlin/com/yandex/div/compose/images/ImageNetworkRestoration.kt
new file mode 100644
index 000000000..70c252620
--- /dev/null
+++ b/client/android/compose/src/main/kotlin/com/yandex/div/compose/images/ImageNetworkRestoration.kt
@@ -0,0 +1,49 @@
+@file:SuppressLint("ComposableNaming")
+
+package com.yandex.div.compose.images
+
+import android.Manifest
+import android.annotation.SuppressLint
+import androidx.annotation.RequiresPermission
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import coil3.compose.AsyncImagePainter
+import coil3.network.HttpException
+import com.yandex.div.compose.dagger.LocalComponent
+import java.io.InterruptedIOException
+import java.net.ConnectException
+import java.net.SocketException
+import java.net.SocketTimeoutException
+import java.net.UnknownHostException
+
+@Composable
+@RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE)
+internal fun AsyncImagePainter.observeNetworkRestoration() {
+ val controller = LocalComponent.current.networkRestorationController
+ LaunchedEffect(this, controller) {
+ controller.networkRestored.collect {
+ val state = state.value
+ if (state is AsyncImagePainter.State.Error && state.result.throwable.isNetworkConnectivityError()) {
+ restart()
+ }
+ }
+ }
+}
+
+private fun Throwable?.isNetworkConnectivityError(): Boolean {
+ var cause: Throwable? = this
+ while (cause != null) {
+ when (cause) {
+ is UnknownHostException,
+ is ConnectException,
+ is SocketTimeoutException,
+ is SocketException,
+ is InterruptedIOException -> return true
+ is HttpException -> {
+ if (cause.response.code == 408 || cause.response.code == 504) return true
+ }
+ }
+ cause = cause.cause
+ }
+ return false
+}
diff --git a/client/android/compose/src/main/kotlin/com/yandex/div/compose/internal/NetworkRestorationController.kt b/client/android/compose/src/main/kotlin/com/yandex/div/compose/internal/NetworkRestorationController.kt
new file mode 100644
index 000000000..451211bab
--- /dev/null
+++ b/client/android/compose/src/main/kotlin/com/yandex/div/compose/internal/NetworkRestorationController.kt
@@ -0,0 +1,49 @@
+package com.yandex.div.compose.internal
+
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.net.NetworkRequest
+import com.yandex.div.compose.dagger.DivContextScope
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import javax.inject.Inject
+
+/**
+ * Listens to system connectivity events and exposes a hot stream of "network restored" pulses.
+ *
+ * Exists as a process-wide bus so consumers (image painters, video players, etc.) don't have to
+ * each register their own [ConnectivityManager.NetworkCallback]. Subscribers decide locally whether
+ * to react.
+ */
+@DivContextScope
+internal class NetworkRestorationController @Inject constructor(context: Context) {
+
+ private val _networkRestored = MutableSharedFlow(
+ extraBufferCapacity = 1,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST,
+ )
+ val networkRestored: SharedFlow = _networkRestored.asSharedFlow()
+
+ private val callback = object : ConnectivityManager.NetworkCallback() {
+ override fun onAvailable(network: Network) {
+ _networkRestored.tryEmit(Unit)
+ }
+ }
+
+ init {
+ val connectivityManager = context.getSystemService(
+ Context.CONNECTIVITY_SERVICE
+ ) as ConnectivityManager
+ try {
+ val request: NetworkRequest = NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build()
+
+ connectivityManager.registerNetworkCallback(request, callback)
+ } catch (_: Throwable) { }
+ }
+}
diff --git a/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/image/DivImageView.kt b/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/image/DivImageView.kt
index c9c95148e..8b205b43c 100644
--- a/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/image/DivImageView.kt
+++ b/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/image/DivImageView.kt
@@ -1,6 +1,7 @@
package com.yandex.div.compose.views.image
import android.util.Base64
+import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
@@ -13,8 +14,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalDensity
-import coil3.compose.AsyncImage
+import coil3.compose.rememberAsyncImagePainter
import com.yandex.div.compose.images.ImageRequestParams
+import com.yandex.div.compose.images.observeNetworkRestoration
import com.yandex.div.compose.images.rememberImageRequest
import com.yandex.div.compose.utils.divContext
import com.yandex.div.compose.utils.imageLoader
@@ -78,10 +80,13 @@ internal fun DivImageView(
Box(modifier = backgroundModifier) {
if (!isImageLoaded && previewRequest != null) {
- AsyncImage(
- modifier = Modifier.fillMaxSize(),
+ val previewPainter = rememberAsyncImagePainter(
model = previewRequest,
imageLoader = imageLoader,
+ )
+ Image(
+ modifier = Modifier.fillMaxSize(),
+ painter = previewPainter,
contentDescription = null,
contentScale = contentScale,
alignment = alignment,
@@ -89,18 +94,22 @@ internal fun DivImageView(
)
}
- AsyncImage(
- modifier = Modifier.fillMaxSize(),
+ val imagePainter = rememberAsyncImagePainter(
model = imageRequest,
imageLoader = imageLoader,
- contentDescription = null,
- contentScale = contentScale,
- alignment = alignment,
- colorFilter = colorFilter,
onSuccess = {
isImageLoaded = true
}
)
+ imagePainter.observeNetworkRestoration()
+ Image(
+ modifier = Modifier.fillMaxSize(),
+ painter = imagePainter,
+ contentDescription = null,
+ contentScale = contentScale,
+ alignment = alignment,
+ colorFilter = colorFilter
+ )
}
}
diff --git a/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/modifiers/ImageBackgroundModifiers.kt b/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/modifiers/ImageBackgroundModifiers.kt
index 48ea44086..22fd2a942 100644
--- a/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/modifiers/ImageBackgroundModifiers.kt
+++ b/client/android/compose/src/main/kotlin/com/yandex/div/compose/views/modifiers/ImageBackgroundModifiers.kt
@@ -17,6 +17,7 @@ import com.yandex.div.compose.utils.imageLoader
import com.yandex.div.compose.utils.observedFloatValue
import com.yandex.div.compose.utils.observedValue
import com.yandex.div.compose.utils.toAlignment
+import com.yandex.div.compose.images.observeNetworkRestoration
import com.yandex.div.compose.views.image.resolveTransformations
import com.yandex.div.compose.views.image.toContentScale
import com.yandex.div2.DivImageBackground
@@ -45,6 +46,7 @@ internal fun Modifier.imageBackground(data: DivImageBackground): Modifier {
model = context.rememberImageRequest(imageRequestParams),
imageLoader = imageLoader
)
+ painter.observeNetworkRestoration()
return drawBehind {
val srcSize = painter.intrinsicSize
diff --git a/client/android/compose/src/test/kotlin/com/yandex/div/compose/Utils.kt b/client/android/compose/src/test/kotlin/com/yandex/div/compose/Utils.kt
index 7bf9cc556..580611cb1 100644
--- a/client/android/compose/src/test/kotlin/com/yandex/div/compose/Utils.kt
+++ b/client/android/compose/src/test/kotlin/com/yandex/div/compose/Utils.kt
@@ -6,6 +6,7 @@ import androidx.compose.ui.test.junit4.ComposeContentTestRule
import com.yandex.div.compose.dagger.DivLocalComponent
import com.yandex.div.compose.expressions.DivComposeExpressionResolver
import com.yandex.div.compose.internal.DivDebugConfiguration
+import com.yandex.div.compose.internal.NetworkRestorationController
import com.yandex.div.core.expression.variables.DivVariableController
import com.yandex.div.evaluable.function.GeneratedBuiltinFunctionProvider
import com.yandex.div.internal.expressions.FunctionProviderDecorator
@@ -26,7 +27,8 @@ internal fun createExpressionResolver(
internal fun mockLocalComponent(
reporter: DivReporter = TestReporter(),
- variableController: DivVariableController = DivVariableController()
+ variableController: DivVariableController = DivVariableController(),
+ networkRestorationController: NetworkRestorationController? = null,
): DivLocalComponent {
val resolver = createExpressionResolver(
reporter = reporter,
@@ -36,6 +38,9 @@ internal fun mockLocalComponent(
on { expressionResolver } doReturn resolver
on { this.reporter } doReturn reporter
on { this.variableController } doReturn variableController
+ networkRestorationController?.let {
+ on { this.networkRestorationController } doReturn it
+ }
}
}
diff --git a/client/android/compose/src/test/kotlin/com/yandex/div/compose/images/ImageNetworkRestorationTest.kt b/client/android/compose/src/test/kotlin/com/yandex/div/compose/images/ImageNetworkRestorationTest.kt
new file mode 100644
index 000000000..b6460d85f
--- /dev/null
+++ b/client/android/compose/src/test/kotlin/com/yandex/div/compose/images/ImageNetworkRestorationTest.kt
@@ -0,0 +1,210 @@
+package com.yandex.div.compose.images
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.net.ConnectivityManager
+import androidx.compose.foundation.Image
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import coil3.ImageLoader
+import coil3.asImage
+import coil3.compose.AsyncImagePainter
+import coil3.compose.rememberAsyncImagePainter
+import coil3.network.HttpException
+import coil3.network.NetworkResponse
+import coil3.request.ErrorResult
+import coil3.request.ImageRequest
+import coil3.request.ImageResult
+import coil3.request.SuccessResult
+import com.yandex.div.compose.dagger.LocalComponent
+import com.yandex.div.compose.internal.NetworkRestorationController
+import com.yandex.div.compose.mockLocalComponent
+import junit.framework.TestCase.assertEquals
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.Shadows
+import org.robolectric.shadows.ShadowConnectivityManager
+import org.robolectric.shadows.ShadowNetwork
+import java.net.UnknownHostException
+import java.util.concurrent.atomic.AtomicInteger
+
+@RunWith(AndroidJUnit4::class)
+class ImageNetworkRestorationTest {
+
+ @get:Rule
+ val composeRule = createComposeRule()
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+ private val shadowConnectivityManager: ShadowConnectivityManager = Shadows.shadowOf(
+ context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+ )
+
+ private val attempts = AtomicInteger(0)
+
+ @Volatile
+ private var nextResult: (ImageRequest) -> ImageResult = { networkErrorResult(it) }
+
+ private val localComponent = mockLocalComponent(
+ networkRestorationController = NetworkRestorationController(context)
+ )
+
+ private val imageLoader: ImageLoader = ImageLoader.Builder(context)
+ .components {
+ add { chain ->
+ attempts.incrementAndGet()
+ nextResult(chain.request)
+ }
+ }
+ .build()
+
+ @Test
+ fun `restarts on UnknownHostException`() = expectRestart {
+ networkErrorResult(it)
+ }
+
+ @Test
+ fun `restarts on HTTP 504`() = expectRestart {
+ httpErrorResult(it, code = 504)
+ }
+
+ @Test
+ fun `restarts on HTTP 408`() = expectRestart {
+ httpErrorResult(it, code = 408)
+ }
+
+ @Test
+ fun `does not restart on HTTP 500`() = expectNoRestart {
+ httpErrorResult(it, code = 500)
+ }
+
+ @Test
+ fun `does not restart on HTTP 404`() = expectNoRestart {
+ httpErrorResult(it, code = 404)
+ }
+
+ @Test
+ fun `does not restart on non-network error`() = expectNoRestart {
+ ErrorResult(
+ image = null,
+ request = it,
+ throwable = IllegalStateException("decoding failed"),
+ )
+ }
+
+ @Test
+ fun `does not restart when painter is in success state`() {
+ nextResult = ::successResult
+ setContent { rememberObservedPainter() }
+ composeRule.waitForIdle()
+ assertEquals(1, attempts.get())
+
+ triggerNetworkAvailable()
+ composeRule.waitForIdle()
+
+ assertEquals(1, attempts.get())
+ }
+
+ @Test
+ fun `restarts on every network event while still in network error`() {
+ setContent { rememberObservedPainter() }
+ composeRule.waitForIdle()
+
+ triggerNetworkAvailable()
+ composeRule.waitForIdle()
+ triggerNetworkAvailable()
+ composeRule.waitForIdle()
+
+ assertEquals(3, attempts.get())
+ }
+
+ @Test
+ fun `stops observing when painter leaves composition`() {
+ val visible = mutableStateOf(true)
+ setContent {
+ if (visible.value) rememberObservedPainter()
+ }
+ composeRule.waitForIdle()
+ val attemptsBefore = attempts.get()
+
+ visible.value = false
+ composeRule.waitForIdle()
+ triggerNetworkAvailable()
+ composeRule.waitForIdle()
+
+ assertEquals(attemptsBefore, attempts.get())
+ }
+
+ private fun expectRestart(error: (ImageRequest) -> ErrorResult) {
+ nextResult = error
+ setContent { rememberObservedPainter() }
+ composeRule.waitForIdle()
+ assertEquals(1, attempts.get())
+
+ triggerNetworkAvailable()
+ composeRule.waitForIdle()
+
+ assertEquals(2, attempts.get())
+ }
+
+ private fun expectNoRestart(error: (ImageRequest) -> ErrorResult) {
+ nextResult = error
+ setContent { rememberObservedPainter() }
+ composeRule.waitForIdle()
+ assertEquals(1, attempts.get())
+
+ triggerNetworkAvailable()
+ composeRule.waitForIdle()
+
+ assertEquals(1, attempts.get())
+ }
+
+ @Composable
+ private fun rememberObservedPainter(): AsyncImagePainter {
+ val painter = rememberAsyncImagePainter(
+ model = "https://example/img",
+ imageLoader = imageLoader,
+ )
+ painter.observeNetworkRestoration()
+ Image(painter = painter, contentDescription = null)
+ return painter
+ }
+
+ private fun setContent(content: @Composable () -> Unit) {
+ composeRule.setContent {
+ CompositionLocalProvider(LocalComponent provides localComponent) {
+ content()
+ }
+ }
+ }
+
+ private fun triggerNetworkAvailable() {
+ val network = ShadowNetwork.newInstance(1)
+ shadowConnectivityManager.networkCallbacks.toList().forEach { callback ->
+ callback.onAvailable(network)
+ }
+ }
+
+ private fun successResult(request: ImageRequest): SuccessResult {
+ val bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+ return SuccessResult(image = bitmap.asImage(), request = request)
+ }
+
+ private fun networkErrorResult(request: ImageRequest): ErrorResult =
+ ErrorResult(
+ image = null,
+ request = request,
+ throwable = UnknownHostException("offline"),
+ )
+
+ private fun httpErrorResult(request: ImageRequest, code: Int): ErrorResult =
+ ErrorResult(
+ image = null,
+ request = request,
+ throwable = HttpException(NetworkResponse().copy(code = code)),
+ )
+}
diff --git a/client/android/compose/src/test/kotlin/com/yandex/div/compose/internal/NetworkRestorationControllerTest.kt b/client/android/compose/src/test/kotlin/com/yandex/div/compose/internal/NetworkRestorationControllerTest.kt
new file mode 100644
index 000000000..11a1e4924
--- /dev/null
+++ b/client/android/compose/src/test/kotlin/com/yandex/div/compose/internal/NetworkRestorationControllerTest.kt
@@ -0,0 +1,130 @@
+package com.yandex.div.compose.internal
+
+import android.content.Context
+import android.net.ConnectivityManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import junit.framework.TestCase.assertEquals
+import junit.framework.TestCase.assertTrue
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.Shadows
+import org.robolectric.shadows.ShadowConnectivityManager
+import org.robolectric.shadows.ShadowNetwork
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class NetworkRestorationControllerTest {
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+ private val shadowConnectivityManager: ShadowConnectivityManager = Shadows.shadowOf(
+ context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+ )
+
+ @Test
+ fun `registers exactly one network callback on creation`() {
+ NetworkRestorationController(context)
+
+ assertEquals(1, shadowConnectivityManager.networkCallbacks.size)
+ }
+
+ @Test
+ fun `emits networkRestored when system reports onAvailable`() = runTest(
+ UnconfinedTestDispatcher()
+ ) {
+ val controller = NetworkRestorationController(context)
+ val received = mutableListOf()
+ val job = launch { controller.networkRestored.toList(received) }
+
+ triggerNetworkAvailable()
+
+ assertEquals(1, received.size)
+ job.cancel()
+ }
+
+ @Test
+ fun `emits separately for each onAvailable event`() = runTest(
+ UnconfinedTestDispatcher()
+ ) {
+ val controller = NetworkRestorationController(context)
+ val received = mutableListOf()
+ val job = launch { controller.networkRestored.toList(received) }
+
+ triggerNetworkAvailable()
+ triggerNetworkAvailable()
+ triggerNetworkAvailable()
+
+ assertEquals(3, received.size)
+ job.cancel()
+ }
+
+ @Test
+ fun `does not emit before any onAvailable event`() = runTest(
+ UnconfinedTestDispatcher()
+ ) {
+ val controller = NetworkRestorationController(context)
+ val received = mutableListOf()
+ val job = launch { controller.networkRestored.toList(received) }
+
+ assertTrue(received.isEmpty())
+ job.cancel()
+ }
+
+ @Test
+ fun `multiple subscribers all receive the same event`() = runTest(
+ UnconfinedTestDispatcher()
+ ) {
+ val controller = NetworkRestorationController(context)
+ val firstReceived = mutableListOf()
+ val secondReceived = mutableListOf()
+ val job1 = launch { controller.networkRestored.toList(firstReceived) }
+ val job2 = launch { controller.networkRestored.toList(secondReceived) }
+
+ triggerNetworkAvailable()
+
+ assertEquals(1, firstReceived.size)
+ assertEquals(1, secondReceived.size)
+ job1.cancel()
+ job2.cancel()
+ }
+
+ @Test
+ fun `late subscriber does not receive earlier events`() = runTest(
+ UnconfinedTestDispatcher()
+ ) {
+ val controller = NetworkRestorationController(context)
+
+ triggerNetworkAvailable()
+ triggerNetworkAvailable()
+
+ val received = mutableListOf()
+ val job = launch { controller.networkRestored.toList(received) }
+
+ assertTrue(received.isEmpty())
+
+ triggerNetworkAvailable()
+
+ assertEquals(1, received.size)
+ job.cancel()
+ }
+
+ @Test
+ fun `each controller registers its own callback`() {
+ NetworkRestorationController(context)
+ NetworkRestorationController(context)
+
+ assertEquals(2, shadowConnectivityManager.networkCallbacks.size)
+ }
+
+ private fun triggerNetworkAvailable() {
+ val network = ShadowNetwork.newInstance(1)
+ shadowConnectivityManager.networkCallbacks.toList().forEach { callback ->
+ callback.onAvailable(network)
+ }
+ }
+}
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png
index b773b7513..23b2f6bb9 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png
index 6f359b436..7c0cced60 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png
index 52ba83b81..cd79ece69 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png
index 64c7a25fd..c241da4dc 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png
index 948a9dd63..4904a3412 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png
index 6a0516773..351da93d8 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png
index 074008f6d..694270d23 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png
index 495842ff1..bc66b50a6 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API26_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png
index ed8811939..a8a106510 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png
index 6f359b436..9098d8c2c 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png
index 52ba83b81..4e4915557 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png
index 1a49aac45..3fe09521d 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png
index 948a9dd63..48a167749 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png
index 6a0516773..351da93d8 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png
index 074008f6d..694270d23 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png
index 495842ff1..bc66b50a6 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API28_XHDPI_720x1600/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png
index 1f45a83c3..2de76ac45 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png
index 1e1777195..d1b6c242a 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png
index 0894b61aa..964b7d020 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png
index 5876c8b9d..d774c1d58 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png
index c06ebb051..a01b5fa1d 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png
index 2c822d1bc..2898268b8 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png
index 4a981f18c..eb9a0db74 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API30_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png
index 1f45a83c3..2de76ac45 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png
index 1e1777195..d1b6c242a 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png
index 0894b61aa..964b7d020 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png
index 5876c8b9d..d774c1d58 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png
index c06ebb051..a01b5fa1d 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewPixelCopy/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png
index 2c822d1bc..9fa161b3a 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/baseline-with-images.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png
index f58bbe597..1b3792eb4 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-center-horizontal-alignment.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png
index aa15a5d5f..7ec5047a7 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-container/horizontal-orientation-space-between-alignment.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png
index c421a2c4a..efb5ba25a 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/blur.png differ
diff --git a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png
index 861274083..06814ec28 100644
Binary files a/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png and b/client/android/divkit-demo-app/src/screenshotTest/screenshots/API32_XXHDPI_1080x2400/viewRender/com.yandex.div.DivComposeScreenshotTest/div-image/custom-paddings.png differ