Slackbot
05/20/2021, 3:15 PMMarko Novakovic
05/20/2021, 3:16 PMimport 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)
}
}
Marko Novakovic
05/20/2021, 3:16 PM@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)
}
}