Skip to content

Instantly share code, notes, and snippets.

@marenovakovic
Last active September 23, 2021 13:07
Show Gist options
  • Save marenovakovic/ee3d7a4f7822ab78e451664d0e6f5c60 to your computer and use it in GitHub Desktop.
Save marenovakovic/ee3d7a4f7822ab78e451664d0e6f5c60 to your computer and use it in GitHub Desktop.
import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
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.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.graphics.ColorUtils
import androidx.core.graphics.drawable.toBitmap
import androidx.palette.graphics.Palette
import coil.ImageLoader
import coil.request.ImageRequest
import coil.target.Target
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import yourApp.data.images
import yourApp.Theme
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
import kotlin.math.min
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Jetpack_compose_adaptive_status_barTheme {
Surface(color = MaterialTheme.colors.background) {
AdaptiveStatusBarScreen()
}
}
}
}
}
private val imageUrl: String
get() = images.random()
@OptIn(InternalCoroutinesApi::class)
@Composable
fun AdaptiveStatusBarScreen() {
var image by remember { mutableStateOf(imageUrl) }
val scrollState = rememberScrollState()
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(scrollState),
horizontalAlignment = Alignment.CenterHorizontally,
) {
ParallaxImage(url = image, scrollState = scrollState)
Spacer(modifier = Modifier.height(32.dp))
Button(onClick = { image = imageUrl }) {
Text(text = "Next please!")
}
Spacer(modifier = Modifier.height(32.dp))
Text(text = stringResource(id = R.string.lorem_ipsum))
}
}
@Composable
fun ParallaxImage(
modifier: Modifier = Modifier,
url: String,
scrollState: ScrollState,
) {
val coroutineScope = rememberCoroutineScope()
val systemUiController = rememberSystemUiController()
var parallaxColor by remember { mutableStateOf(Color.Transparent) }
var image by remember { mutableStateOf(ImageBitmap(1, 1)) }
val loader = ImageLoader(LocalContext.current)
val target = rememberCoilTarget { bitmap ->
image = bitmap.asImageBitmap()
coroutineScope.launch {
val (color, isLight) = bitmap
.computeDominantTopSectionColor()
parallaxColor = color
systemUiController.setStatusBarColor(color, isLight)
}
}
val request = rememberDefaultImageRequest(url = url, target = target)
LaunchedEffect(url) {
loader.execute(request)
}
Image(
modifier = modifier
.height(250.dp)
.fillMaxWidth()
.background(parallaxColor)
.graphicsLayer { alpha = min(1f, 1 - (scrollState.value / 400f)) },
bitmap = image,
contentScale = ContentScale.FillWidth,
contentDescription = null,
)
}
@Composable
fun rememberCoilTarget(onBitmapReady: (Bitmap) -> Unit): Target = remember {
object : Target {
override fun onSuccess(result: Drawable) = onBitmapReady(result.toBitmap())
}
}
@OptIn(ExperimentalCoroutinesApi::class)
suspend fun Bitmap.computeDominantTopSectionColor(): Pair<Color, Boolean> =
suspendCancellableCoroutine { continuation ->
Palette.from(this)
.setRegion(0, 0, this.width, 24.dp.value.toInt())
.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)
}
}
object DefaultRequest {
operator fun invoke(context: Context, url: String, target: Target) =
ImageRequest.Builder(context)
.data(url)
.target(target)
.allowHardware(false)
.build()
}
@Composable
fun rememberDefaultImageRequest(url: String, target: Target): ImageRequest {
val context = LocalContext.current
return remember(url) { DefaultRequest(context, url, target) }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment