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