This message was deleted.
# compose
s
This message was deleted.
m
Copy code
import android.graphics.drawable.Drawable
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
import coil.ImageLoader
import coil.request.ImageRequest
import coil.size.Scale
import coil.target.Target
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import ui.extensions.computeDominantTopSectionColor
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.launch
import kotlin.math.min

private val images = listOf(
    "<https://images.unsplash.com/photo-1621306558057-1d040ee57bb9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=2100&q=80>",
    "<https://images.unsplash.com/photo-1621356312324-14b9222243a5?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=2301&q=80>",
    "<https://images.unsplash.com/photo-1621299873440-34f6220cb8b3?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=2100&q=80>",
    "<https://images.unsplash.com/photo-1621299873077-9147006e0f80?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=2100&q=80>",
    "<https://images.unsplash.com/photo-1621346552452-c24d0eb61762?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=934&q=80>",
    "<https://images.unsplash.com/photo-1621467092735-823551499c31?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1500&q=80>",
    "<https://images.unsplash.com/photo-1621503212131-cb3bf5058dd4?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1500&q=80>",
)

private val imageUrl: String
    get() = images.random()

@OptIn(InternalCoroutinesApi::class)
@Composable
fun AdaptiveStatusBarScreen() {
    val systemUiController = rememberSystemUiController()

    var image by remember { mutableStateOf(imageUrl) }
    var statusBarColor by remember { mutableStateOf(Color.Transparent) }
    val scrollState = rememberScrollState()

    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(scrollState),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        CollapsingParallaxImage(
            modifier = Modifier
                .background(statusBarColor)
                .graphicsLayer {
                    alpha = min(1f, 1 - (scrollState.value / scrollState.maxValue.toFloat()))
                },
            url = image,
            changeStatusBarColor = { color, isLight ->
                statusBarColor = color
                systemUiController.setStatusBarColor(color, isLight)
            }
        )
        Spacer(modifier = Modifier.height(32.dp))
        Button(onClick = { image = imageUrl }) {
            Text(text = "Next please!")
        }
        repeat(10) {
            Spacer(modifier = Modifier.height(16.dp))
            Text(modifier = Modifier.padding(16.dp), text = it.toString())
        }
    }
}

@Composable
fun CollapsingParallaxImage(
    modifier: Modifier = Modifier,
    url: String,
    changeStatusBarColor: (Color, Boolean) -> Unit,
) {
    BoxWithConstraints(modifier = modifier.height(250.dp)) {
        val coroutineScope = rememberCoroutineScope()
        val loader = ImageLoader(LocalContext.current)
        var image by remember { mutableStateOf(ImageBitmap(1, 1)) }
        val target = remember {
            object : Target {
                override fun onSuccess(result: Drawable) {
                    val bitmap = result.toBitmap()
                    image = bitmap.asImageBitmap()
                    coroutineScope.launch {
                        val (statusBarColor, isLight) = bitmap
                            .computeDominantTopSectionColor()
                        changeStatusBarColor(statusBarColor, isLight)
                    }
                }
            }
        }

        val request = ImageRequest.Builder(LocalContext.current).run {
            data(url)
            target(target)
            scale(Scale.FILL)
            allowHardware(false)
            build()
        }

        LaunchedEffect(url) {
            loader.enqueue(request)
        }

        Image(bitmap = image, contentScale = ContentScale.FillWidth, contentDescription = null)
    }
}
Copy code
@OptIn(ExperimentalCoroutinesApi::class)
suspend fun Bitmap.computeDominantTopSectionColor(): Pair<Color, Boolean> =
    suspendCancellableCoroutine { continuation ->
        Palette.from(this)
            .setRegion(0, 0, this.width, 24)
            .maximumColorCount(3)
            .generate { palette ->
                palette ?: continuation.cancel()

                val statusBarColorRgb = palette!!.dominantSwatch?.rgb
                statusBarColorRgb ?: continuation.cancel()

                val hsl = FloatArray(3)
                ColorUtils.colorToHSL(statusBarColorRgb!!, hsl)
                val isLight = hsl[2] >= 0.5
                continuation.resume(Color(statusBarColorRgb) to isLight)
            }
    }