diff --git a/README.md b/README.md index 57bf671..72bddd7 100644 --- a/README.md +++ b/README.md @@ -4,29 +4,27 @@ Apple's Liquid Glass effect for Android Jetpack Compose. -⚠️ The library is in alpha stage, every API may be changed, use it on your own risk. - ## Docs [![JitPack Release](https://jitpack.io/v/Kyant0/AndroidLiquidGlass.svg)](https://jitpack.io/#Kyant0/AndroidLiquidGlass) +⚠️ The library is in alpha stage, every API may be changed, use it on your own risk. + [Latest docs](https://kyant.gitbook.io/backdrop) [legacy docs (before 1.0.0-alpha10)](docs/LegacyDocs.md) ## Demos +- [Catalog](./catalog/release/catalog-release.apk) + +![Catalog screenshot](artworks/catalog.jpg) + - [Playground app](./app/release/app-release.apk), Android 13 and above is required. ![Playground screenshot](artworks/playground_app.jpg) -- [Catalog](./catalog/release/catalog-release.apk) - -| | | -|:---------------------------------------------------------------------------------:|:---------------------------------------------------------------------------:| -| Control center | Bottom tabs | - -## Comparing with iOS +## Comparing effects with iOS iOS device: iPhone 16 Pro Max (emulator), using [GlassExplorer](https://github.com/ktiays/GlassExplorer) diff --git a/artworks/catalog.jpg b/artworks/catalog.jpg new file mode 100644 index 0000000..a094c78 Binary files /dev/null and b/artworks/catalog.jpg differ diff --git a/artworks/catalog_bottom_tabs.jpg b/artworks/catalog_bottom_tabs.jpg deleted file mode 100644 index dcb1eb9..0000000 Binary files a/artworks/catalog_bottom_tabs.jpg and /dev/null differ diff --git a/artworks/catalog_control_center.jpg b/artworks/catalog_control_center.jpg deleted file mode 100644 index 9bdb41f..0000000 Binary files a/artworks/catalog_control_center.jpg and /dev/null differ diff --git a/backdrop/src/main/java/com/kyant/backdrop/ContentBackdropModifier.kt b/backdrop/src/main/java/com/kyant/backdrop/ContentBackdropModifier.kt index 75aa265..2688a34 100644 --- a/backdrop/src/main/java/com/kyant/backdrop/ContentBackdropModifier.kt +++ b/backdrop/src/main/java/com/kyant/backdrop/ContentBackdropModifier.kt @@ -30,19 +30,19 @@ import com.kyant.backdrop.shadow.Shadow import com.kyant.backdrop.shadow.ShadowElement fun Modifier.contentBackdrop( - shapeProvider: () -> Shape, + shape: () -> Shape, highlight: (() -> Highlight?)? = DefaultHighlight, shadow: (() -> Shadow?)? = DefaultShadow, - layerBlock: (GraphicsLayerScope.() -> Unit)? = null, + layer: (GraphicsLayerScope.() -> Unit)? = null, onDrawBehind: (DrawScope.() -> Unit)? = null, onDrawSurface: (DrawScope.() -> Unit)? = null, effects: BackdropEffectScope.() -> Unit ): Modifier { - val shapeProvider = CachedBackdropShapeProvider(shapeProvider) + val shapeProvider = CachedBackdropShapeProvider(shape) return this .then( - if (layerBlock != null) { - Modifier.graphicsLayer(layerBlock) + if (layer != null) { + Modifier.graphicsLayer(layer) } else { Modifier } @@ -70,7 +70,7 @@ fun Modifier.contentBackdrop( .then( ContentBackdropElement( shapeProvider = shapeProvider, - layerBlock = layerBlock, + layerBlock = layer, onDrawBehind = onDrawBehind, onDrawSurface = onDrawSurface, effects = effects diff --git a/backdrop/src/main/java/com/kyant/backdrop/DrawBackdropModifier.kt b/backdrop/src/main/java/com/kyant/backdrop/DrawBackdropModifier.kt index 6e73d35..665a6a6 100644 --- a/backdrop/src/main/java/com/kyant/backdrop/DrawBackdropModifier.kt +++ b/backdrop/src/main/java/com/kyant/backdrop/DrawBackdropModifier.kt @@ -41,20 +41,21 @@ private val DefaultOnDrawBackdrop: DrawScope.(DrawScope.() -> Unit) -> Unit = { fun Modifier.drawBackdrop( backdrop: Backdrop, - shapeProvider: () -> Shape, + shape: () -> Shape, highlight: (() -> Highlight?)? = DefaultHighlight, shadow: (() -> Shadow?)? = DefaultShadow, - layerBlock: (GraphicsLayerScope.() -> Unit)? = null, + layer: (GraphicsLayerScope.() -> Unit)? = null, onDrawBehind: (DrawScope.() -> Unit)? = null, onDrawBackdrop: DrawScope.(drawBackdrop: DrawScope.() -> Unit) -> Unit = DefaultOnDrawBackdrop, onDrawSurface: (DrawScope.() -> Unit)? = null, + onDrawFront: (DrawScope.() -> Unit)? = null, effects: BackdropEffectScope.() -> Unit ): Modifier { - val shapeProvider = CachedBackdropShapeProvider(shapeProvider) + val shapeProvider = CachedBackdropShapeProvider(shape) return this .then( - if (layerBlock != null) { - Modifier.graphicsLayer(layerBlock) + if (layer != null) { + Modifier.graphicsLayer(layer) } else { Modifier } @@ -83,10 +84,11 @@ fun Modifier.drawBackdrop( DrawBackdropElement( backdrop = backdrop, shapeProvider = shapeProvider, - layerBlock = layerBlock, + layerBlock = layer, onDrawBehind = onDrawBehind, onDrawBackdrop = onDrawBackdrop, onDrawSurface = onDrawSurface, + onDrawFront = onDrawFront, effects = effects ) ) @@ -99,6 +101,7 @@ private class DrawBackdropElement( val onDrawBehind: (DrawScope.() -> Unit)?, val onDrawBackdrop: DrawScope.(drawBackdrop: DrawScope.() -> Unit) -> Unit, val onDrawSurface: (DrawScope.() -> Unit)?, + val onDrawFront: (DrawScope.() -> Unit)?, val effects: BackdropEffectScope.() -> Unit ) : ModifierNodeElement() { @@ -110,6 +113,7 @@ private class DrawBackdropElement( onDrawBehind = onDrawBehind, onDrawBackdrop = onDrawBackdrop, onDrawSurface = onDrawSurface, + onDrawFront = onDrawFront, effects = effects ) } @@ -121,6 +125,7 @@ private class DrawBackdropElement( node.onDrawBehind = onDrawBehind node.onDrawBackdrop = onDrawBackdrop node.onDrawSurface = onDrawSurface + node.onDrawFront = onDrawFront node.effects = effects node.invalidateDrawCache() } @@ -133,6 +138,7 @@ private class DrawBackdropElement( properties["onDrawBehind"] = onDrawBehind properties["onDrawBackdrop"] = onDrawBackdrop properties["onDrawSurface"] = onDrawSurface + properties["onDrawFront"] = onDrawFront properties["effects"] = effects } @@ -146,6 +152,7 @@ private class DrawBackdropElement( if (onDrawBehind != other.onDrawBehind) return false if (onDrawBackdrop != other.onDrawBackdrop) return false if (onDrawSurface != other.onDrawSurface) return false + if (onDrawFront != other.onDrawFront) return false if (effects != other.effects) return false return true @@ -158,6 +165,7 @@ private class DrawBackdropElement( result = 31 * result + (onDrawBehind?.hashCode() ?: 0) result = 31 * result + onDrawBackdrop.hashCode() result = 31 * result + (onDrawSurface?.hashCode() ?: 0) + result = 31 * result + (onDrawFront?.hashCode() ?: 0) result = 31 * result + effects.hashCode() return result } @@ -170,6 +178,7 @@ private class DrawBackdropNode( var onDrawBehind: (DrawScope.() -> Unit)?, var onDrawBackdrop: DrawScope.(drawBackdrop: DrawScope.() -> Unit) -> Unit, var onDrawSurface: (DrawScope.() -> Unit)?, + var onDrawFront: (DrawScope.() -> Unit)?, var effects: BackdropEffectScope.() -> Unit ) : LayoutModifierNode, DrawModifierNode, GlobalPositionAwareModifierNode, ObserverModifierNode, Modifier.Node() { @@ -249,6 +258,8 @@ private class DrawBackdropNode( onDrawSurface?.invoke(this) drawContent() + + onDrawFront?.invoke(this) } override fun onGloballyPositioned(coordinates: LayoutCoordinates) { diff --git a/catalog/release/baselineProfiles/0/catalog-release.dm b/catalog/release/baselineProfiles/0/catalog-release.dm index ecbad5f..72aaf41 100644 Binary files a/catalog/release/baselineProfiles/0/catalog-release.dm and b/catalog/release/baselineProfiles/0/catalog-release.dm differ diff --git a/catalog/release/baselineProfiles/1/catalog-release.dm b/catalog/release/baselineProfiles/1/catalog-release.dm index dc35607..97a6cbc 100644 Binary files a/catalog/release/baselineProfiles/1/catalog-release.dm and b/catalog/release/baselineProfiles/1/catalog-release.dm differ diff --git a/catalog/release/catalog-release.apk b/catalog/release/catalog-release.apk index 6d80c53..5b95822 100644 Binary files a/catalog/release/catalog-release.apk and b/catalog/release/catalog-release.apk differ diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/BackdropDemoScaffold.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/BackdropDemoScaffold.kt new file mode 100644 index 0000000..8458ce3 --- /dev/null +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/BackdropDemoScaffold.kt @@ -0,0 +1,96 @@ +package com.kyant.backdrop.catalog + +import android.graphics.BitmapFactory +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.PickVisualMediaRequest +import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.DrawableRes +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.BasicText +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.graphics.painter.BitmapPainter +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.kyant.backdrop.Backdrop +import com.kyant.backdrop.backdrop +import com.kyant.backdrop.catalog.components.LiquidButton +import com.kyant.backdrop.rememberBackdrop + +@Composable +fun BackdropDemoScaffold( + modifier: Modifier = Modifier, + @DrawableRes initialPainterResId: Int = R.drawable.wallpaper_light, + content: @Composable (backdrop: Backdrop) -> Unit +) { + Box( + Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + var painter: Painter? by remember { mutableStateOf(null) } + val context = LocalContext.current + val pickMedia = rememberLauncherForActivityResult( + ActivityResultContracts.PickVisualMedia() + ) { uri -> + if (uri != null) { + try { + context.contentResolver.openInputStream(uri)?.use { inputStream -> + val imageBitmap = BitmapFactory.decodeStream(inputStream)?.asImageBitmap() + if (imageBitmap != null) { + painter = BitmapPainter(imageBitmap) + } + } + } catch (_: Exception) { + } + } + } + + val backdrop = rememberBackdrop() + + Image( + painter ?: painterResource(initialPainterResId), + null, + Modifier + .backdrop(backdrop) + .then(modifier) + .fillMaxSize(), + contentScale = ContentScale.Crop + ) + + content(backdrop) + + LiquidButton( + { pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) }, + backdrop, + Modifier + .padding(16f.dp) + .navigationBarsPadding() + .height(56f.dp) + .align(Alignment.BottomCenter), + tint = Color(0xFF0088FF) + ) { + BasicText( + "Pick an image", + Modifier.padding(horizontal = 8f.dp), + style = TextStyle(Color.White, 16f.sp) + ) + } + } +} diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/Block.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/Block.kt new file mode 100644 index 0000000..0e94dcb --- /dev/null +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/Block.kt @@ -0,0 +1,8 @@ +package com.kyant.backdrop.catalog + +import androidx.compose.runtime.Composable + +@Composable +fun Block(content: @Composable () -> Unit) { + content() +} diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/CatalogDestination.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/CatalogDestination.kt index c671d85..518d144 100644 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/CatalogDestination.kt +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/CatalogDestination.kt @@ -2,12 +2,12 @@ package com.kyant.backdrop.catalog enum class CatalogDestination { Home, + Buttons, + Dialog, GlassPlayground, AdaptiveLuminanceGlass, ControlCenter, BottomTabs, - IconButton, - Dialog, ScrollContainer, LazyScrollContainer, } diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/MainActivity.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/MainActivity.kt index 3eba7b2..2a4a0c1 100644 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/MainActivity.kt +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/MainActivity.kt @@ -8,8 +8,6 @@ import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.graphics.Color import androidx.core.view.WindowCompat -import com.kyant.backdrop.catalog.theme.LocalContentColor -import com.kyant.backdrop.catalog.theme.ripple class MainActivity : ComponentActivity() { @@ -21,8 +19,7 @@ class MainActivity : ComponentActivity() { val isLightTheme = !isSystemInDarkTheme() CompositionLocalProvider( - LocalContentColor provides if (isLightTheme) Color.Black else Color.White, - LocalIndication provides ripple() + LocalIndication provides ripple(color = if (isLightTheme) Color.Black else Color.White) ) { MainContent() } diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/MainContent.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/MainContent.kt index f1cd0f9..e218b5a 100644 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/MainContent.kt +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/MainContent.kt @@ -8,11 +8,11 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import com.kyant.backdrop.catalog.destinations.AdaptiveLuminanceGlassContent import com.kyant.backdrop.catalog.destinations.BottomTabsContent +import com.kyant.backdrop.catalog.destinations.ButtonsContent import com.kyant.backdrop.catalog.destinations.ControlCenterContent import com.kyant.backdrop.catalog.destinations.DialogContent import com.kyant.backdrop.catalog.destinations.GlassPlaygroundContent import com.kyant.backdrop.catalog.destinations.HomeContent -import com.kyant.backdrop.catalog.destinations.IconButtonContent import com.kyant.backdrop.catalog.destinations.LazyScrollContainerContent import com.kyant.backdrop.catalog.destinations.ScrollContainerContent @@ -26,12 +26,15 @@ fun MainContent() { when (destination) { CatalogDestination.Home -> HomeContent(onNavigate = { destination = it }) + + CatalogDestination.Buttons -> ButtonsContent() + CatalogDestination.BottomTabs -> BottomTabsContent() + CatalogDestination.Dialog -> DialogContent() + + CatalogDestination.ControlCenter -> ControlCenterContent() + CatalogDestination.GlassPlayground -> GlassPlaygroundContent() CatalogDestination.AdaptiveLuminanceGlass -> AdaptiveLuminanceGlassContent() - CatalogDestination.ControlCenter -> ControlCenterContent() - CatalogDestination.BottomTabs -> BottomTabsContent() - CatalogDestination.IconButton -> IconButtonContent() - CatalogDestination.Dialog -> DialogContent() CatalogDestination.ScrollContainer -> ScrollContainerContent() CatalogDestination.LazyScrollContainer -> LazyScrollContainerContent() } diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/theme/Ripple.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/Ripple.kt similarity index 89% rename from catalog/src/main/java/com/kyant/backdrop/catalog/theme/Ripple.kt rename to catalog/src/main/java/com/kyant/backdrop/catalog/Ripple.kt index f106a71..bb02585 100644 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/theme/Ripple.kt +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/Ripple.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.kyant.backdrop.catalog.theme +package com.kyant.backdrop.catalog import androidx.compose.foundation.IndicationNodeFactory import androidx.compose.foundation.interaction.InteractionSource @@ -24,12 +24,10 @@ import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorProducer -import androidx.compose.ui.graphics.isSpecified import androidx.compose.ui.node.CompositionLocalConsumerModifierNode import androidx.compose.ui.node.DelegatableNode import androidx.compose.ui.node.DelegatingNode import androidx.compose.ui.node.ObserverModifierNode -import androidx.compose.ui.node.currentValueOf import androidx.compose.ui.node.observeReads import androidx.compose.ui.unit.Dp @@ -100,6 +98,7 @@ private constructor( private val colorProducer: ColorProducer?, private val color: Color ) : IndicationNodeFactory { + constructor( bounded: Boolean, radius: Dp, @@ -138,6 +137,7 @@ private class DelegatingThemeAwareRippleNode( private val radius: Dp, private val color: ColorProducer, ) : DelegatingNode(), CompositionLocalConsumerModifierNode, ObserverModifierNode { + private var rippleNode: DelegatableNode? = null override fun onAttach() { @@ -157,25 +157,14 @@ private class DelegatingThemeAwareRippleNode( } private fun attachNewRipple() { - val calculateColor = ColorProducer { - val userDefinedColor = color() - if (userDefinedColor.isSpecified) { - userDefinedColor - } else { - currentValueOf(LocalContentColor) - } - } - - val calculateRippleAlpha = { RippleDefaults.RippleAlpha } - rippleNode = delegate( createRippleModifierNode( - interactionSource, - bounded, - radius, - calculateColor, - calculateRippleAlpha + interactionSource = interactionSource, + bounded = bounded, + radius = radius, + color = color, + rippleAlpha = { RippleDefaults.RippleAlpha } ) ) } diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/components/LiquidButton.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/components/LiquidButton.kt new file mode 100644 index 0000000..df4ed69 --- /dev/null +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/components/LiquidButton.kt @@ -0,0 +1,212 @@ +package com.kyant.backdrop.catalog.components + +import android.graphics.RuntimeShader +import android.os.Build +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.VectorConverter +import androidx.compose.animation.core.VisibilityThreshold +import androidx.compose.animation.core.spring +import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.BlendMode +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ShaderBrush +import androidx.compose.ui.graphics.isSpecified +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.fastCoerceAtMost +import androidx.compose.ui.util.fastCoerceIn +import androidx.compose.ui.util.lerp +import com.kyant.backdrop.Backdrop +import com.kyant.backdrop.catalog.utils.inspectDragGestures +import com.kyant.backdrop.drawBackdrop +import com.kyant.backdrop.effects.blur +import com.kyant.backdrop.effects.refraction +import com.kyant.backdrop.effects.saturation +import com.kyant.capsule.ContinuousCapsule +import kotlinx.coroutines.launch +import kotlin.math.abs +import kotlin.math.atan2 +import kotlin.math.cos +import kotlin.math.sin +import kotlin.math.tanh + +@Composable +fun LiquidButton( + onClick: () -> Unit, + backdrop: Backdrop, + modifier: Modifier = Modifier, + isInteractive: Boolean = true, + tint: Color = Color.Unspecified, + surfaceColor: Color = Color.Unspecified, + content: @Composable RowScope.() -> Unit +) { + val animationScope = rememberCoroutineScope() + val scaleProgressAnimation = remember { Animatable(0f) } + var pressStartPosition by remember { mutableStateOf(Offset.Zero) } + val offsetAnimation = remember { Animatable(Offset.Zero, Offset.VectorConverter) } + + val interactiveHighlightShader = remember { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + RuntimeShader( + """ +uniform float2 size; +layout(color) uniform half4 color; +uniform float radius; +uniform float2 offset; + +half4 main(float2 coord) { + float2 center = offset; + float dist = distance(coord, center); + float intensity = smoothstep(radius, radius * 0.5, dist); + return color * intensity; +}""" + ) + } else { + null + } + } + + Row( + modifier + .drawBackdrop( + backdrop, + { ContinuousCapsule }, + layer = if (isInteractive) { + { + val width = size.width + val height = size.height + + val scaleProgress = scaleProgressAnimation.value + val maxScale = 0.1f + val scale = lerp(1f, 1f + maxScale, scaleProgress) + + val maxOffset = size.minDimension + val initialDerivative = 0.05f + val offset = offsetAnimation.value + translationX = maxOffset * tanh(initialDerivative * offset.x / maxOffset) + translationY = maxOffset * tanh(initialDerivative * offset.y / maxOffset) + + val maxDragScale = 0.1f + val offsetAngle = atan2(offset.y, offset.x) + scaleX = + scale + + maxDragScale * abs(cos(offsetAngle) * offset.x / size.maxDimension) * + (width / height).fastCoerceAtMost(1f) + scaleY = + scale + + maxDragScale * abs(sin(offsetAngle) * offset.y / size.maxDimension) * + (height / width).fastCoerceAtMost(1f) + } + } else { + null + }, + onDrawSurface = { + if (tint.isSpecified) { + drawRect(tint, blendMode = BlendMode.Hue) + drawRect(tint.copy(alpha = 0.75f)) + } + if (surfaceColor.isSpecified) { + drawRect(surfaceColor) + } + }, + onDrawFront = if (isInteractive) { + { + val progress = scaleProgressAnimation.value.fastCoerceIn(0f, 1f) + if (progress > 0f) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && interactiveHighlightShader != null) { + drawRect( + Color.White.copy(0.1f * progress), + blendMode = BlendMode.Plus + ) + interactiveHighlightShader.apply { + val offset = pressStartPosition + offsetAnimation.value + setFloatUniform("size", size.width, size.height) + setColorUniform("color", Color.White.copy(0.15f * progress).toArgb()) + setFloatUniform("radius", size.maxDimension) + setFloatUniform( + "offset", + offset.x.fastCoerceIn(0f, size.width), + offset.y.fastCoerceIn(0f, size.height) + ) + } + drawRect( + ShaderBrush(interactiveHighlightShader), + blendMode = BlendMode.Plus + ) + } else { + drawRect( + Color.White.copy(0.25f * progress), + blendMode = BlendMode.Plus + ) + } + } + } + } else { + null + } + ) { + saturation() + blur(2f.dp.toPx()) + refraction(12f.dp.toPx(), 24f.dp.toPx()) + } + .clickable( + interactionSource = null, + indication = if (isInteractive) null else LocalIndication.current, + role = Role.Button, + onClick = onClick + ) + .then( + if (isInteractive) { + Modifier.pointerInput(animationScope) { + val progressAnimationSpec = spring(0.5f, 300f, 0.001f) + val offsetAnimationSpec = spring(1f, 300f, Offset.VisibilityThreshold) + val onDragStop: () -> Unit = { + animationScope.launch { + launch { scaleProgressAnimation.animateTo(0f, progressAnimationSpec) } + launch { offsetAnimation.animateTo(Offset.Zero, offsetAnimationSpec) } + } + } + inspectDragGestures( + onDragStart = { down -> + pressStartPosition = down.position + animationScope.launch { + launch { scaleProgressAnimation.animateTo(1f, progressAnimationSpec) } + launch { offsetAnimation.snapTo(Offset.Zero) } + } + }, + onDragEnd = { onDragStop() }, + onDragCancel = onDragStop + ) { _, dragAmount -> + animationScope.launch { + offsetAnimation.snapTo(offsetAnimation.value + dragAmount) + } + } + } + } else { + Modifier + } + ) + .height(48f.dp) + .padding(horizontal = 16f.dp), + horizontalArrangement = Arrangement.spacedBy(8f.dp, Alignment.CenterHorizontally), + verticalAlignment = Alignment.CenterVertically, + content = content + ) +} diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/components/Text.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/components/Text.kt deleted file mode 100644 index 7490cc2..0000000 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/components/Text.kt +++ /dev/null @@ -1,64 +0,0 @@ -package com.kyant.backdrop.catalog.components - -import androidx.compose.foundation.text.BasicText -import androidx.compose.foundation.text.TextAutoSize -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorProducer -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.style.TextOverflow -import com.kyant.backdrop.catalog.theme.LocalContentColor - -const val LoremIpsum: String = - """Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.""" - -@Composable -fun Text( - text: String, - style: TextStyle, - modifier: Modifier = Modifier, - color: Color = LocalContentColor.current, - overflow: TextOverflow = TextOverflow.Ellipsis, - softWrap: Boolean = true, - maxLines: Int = Int.MAX_VALUE, - minLines: Int = 1, - autoSize: TextAutoSize? = null, -) { - BasicText( - text = text, - modifier = modifier, - style = style, - overflow = overflow, - softWrap = softWrap, - maxLines = maxLines, - minLines = minLines, - color = { color }, - autoSize = autoSize - ) -} - -@Composable -fun Text( - text: () -> String, - style: TextStyle, - color: ColorProducer, - modifier: Modifier = Modifier, - overflow: TextOverflow = TextOverflow.Ellipsis, - softWrap: Boolean = true, - maxLines: Int = Int.MAX_VALUE, - minLines: Int = 1, - autoSize: TextAutoSize? = null, -) { - BasicText( - text = text(), - modifier = modifier, - style = style, - overflow = overflow, - softWrap = softWrap, - maxLines = maxLines, - minLines = minLines, - color = color, - autoSize = autoSize - ) -} diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/AdaptiveLuminanceGlassContent.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/AdaptiveLuminanceGlassContent.kt index 27f2116..185b923 100644 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/AdaptiveLuminanceGlassContent.kt +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/AdaptiveLuminanceGlassContent.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size +import androidx.compose.foundation.text.BasicText import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember @@ -25,14 +26,15 @@ import androidx.compose.ui.graphics.rememberGraphicsLayer import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.compose.ui.util.lerp import androidx.core.graphics.scale import com.kyant.backdrop.backdrop +import com.kyant.backdrop.catalog.Block import com.kyant.backdrop.catalog.R -import com.kyant.backdrop.catalog.components.Text -import com.kyant.backdrop.catalog.theme.TextStyles import com.kyant.backdrop.drawBackdrop import com.kyant.backdrop.effects.blur import com.kyant.backdrop.effects.colorFilter @@ -112,7 +114,7 @@ fun AdaptiveLuminanceGlassContent() { .drawBackdrop( backdrop, { ContinuousRoundedRectangle(24f.dp) }, - layerBlock = { + layer = { val offset = offsetAnimation.value val zoom = zoomAnimation.value val rotation = rotationAnimation.value @@ -172,11 +174,13 @@ fun AdaptiveLuminanceGlassContent() { .align(Alignment.Center), contentAlignment = Alignment.Center ) { - Text( - { "luminance:\n${"%.2f".format(luminanceAnimation.value)}" }, - TextStyles.body.copy(textAlign = TextAlign.Center), - color = { contentColorAnimation.value } - ) + Block { + BasicText( + "luminance:\n${"%.2f".format(luminanceAnimation.value)}", + style = TextStyle(Color.Unspecified, 16f.sp, textAlign = TextAlign.Center), + color = { contentColorAnimation.value } + ) + } } } } diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/BottomTabsContent.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/BottomTabsContent.kt index d825156..f0027fa 100644 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/BottomTabsContent.kt +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/BottomTabsContent.kt @@ -2,7 +2,6 @@ package com.kyant.backdrop.catalog.destinations import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.spring -import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.draggable @@ -13,11 +12,11 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.text.BasicText import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -35,7 +34,6 @@ import androidx.compose.ui.graphics.CompositingStrategy import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.layer.drawLayer import androidx.compose.ui.graphics.rememberGraphicsLayer -import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.layout import androidx.compose.ui.res.painterResource import androidx.compose.ui.semantics.clearAndSetSemantics @@ -49,13 +47,12 @@ import androidx.compose.ui.util.fastCoerceIn import androidx.compose.ui.util.fastRoundToInt import androidx.compose.ui.util.lerp import com.kyant.backdrop.backdrop +import com.kyant.backdrop.catalog.BackdropDemoScaffold import com.kyant.backdrop.catalog.R -import com.kyant.backdrop.catalog.components.Text -import com.kyant.backdrop.catalog.theme.LocalContentColor import com.kyant.backdrop.drawBackdrop import com.kyant.backdrop.effects.blur +import com.kyant.backdrop.effects.dispersion import com.kyant.backdrop.effects.refraction -import com.kyant.backdrop.effects.refractionWithDispersion import com.kyant.backdrop.effects.saturation import com.kyant.backdrop.highlight.Highlight import com.kyant.backdrop.rememberBackdrop @@ -66,6 +63,7 @@ import kotlinx.coroutines.launch @Composable fun BottomTabsContent() { val isLightTheme = !isSystemInDarkTheme() + val contentColor = if (isLightTheme) Color.Black else Color.White val accentColor = if (isLightTheme) Color(0xFF0088FF) else Color(0xFF0091FF) @@ -74,9 +72,8 @@ fun BottomTabsContent() { else Color(0xFF121212).copy(0.4f) val airplaneModeIcon = painterResource(R.drawable.flight_40px) - val iconColorFilter = ColorFilter.tint(LocalContentColor.current) + val iconColorFilter = ColorFilter.tint(contentColor) - val backdrop = rememberBackdrop() val tabsBackdrop = rememberBackdrop() val tabsLayer = rememberGraphicsLayer().apply { @@ -89,20 +86,9 @@ fun BottomTabsContent() { val scaleYAnimation = remember { Animatable(0f) } val tabOffsetAnimation = remember { Animatable(0f) } - Box(Modifier.fillMaxSize()) { - Image( - painterResource(R.drawable.system_home_screen_light), - null, - Modifier - .backdrop(backdrop) - .fillMaxSize(), - contentScale = ContentScale.Crop - ) - + BackdropDemoScaffold { backdrop -> Box( - Modifier - .padding(40f.dp) - .align(Alignment.Center), + Modifier.padding(40f.dp), contentAlignment = Alignment.CenterStart ) { Box( @@ -131,7 +117,7 @@ fun BottomTabsContent() { .drawBackdrop( backdrop, { ContinuousCapsule }, - layerBlock = { + layer = { val progress = pressAnimation.value val scale = lerp(1f, 1f + 2f.dp.toPx() / size.height, progress) scaleX = scale @@ -167,9 +153,9 @@ fun BottomTabsContent() { .size(28f.dp) .paint(airplaneModeIcon, colorFilter = iconColorFilter) ) - Text( + BasicText( "Tab ${index + 1}", - TextStyle(fontSize = 12f.sp) + style = TextStyle(contentColor, 12f.sp) ) } } @@ -263,7 +249,7 @@ fun BottomTabsContent() { offset = DpOffset(0f.dp, 4f.dp * progress) ) }, - layerBlock = { + layer = { translationX = tabOffsetAnimation.value.fastCoerceIn(0f, size.width * 3) scaleX = lerp(1f, 1f + 20f.dp.toPx() / size.height, scaleXAnimation.value) scaleY = lerp(1f, 1f + 20f.dp.toPx() / size.height, scaleYAnimation.value) @@ -278,10 +264,14 @@ fun BottomTabsContent() { } ) { val progress = pressAnimation.value - refractionWithDispersion( + refraction( 12f.dp.toPx() * progress, 12f.dp.toPx() * progress ) + dispersion( + 12f.dp.toPx() * progress, + 24f.dp.toPx() * progress + ) } .draggable( rememberDraggableState { delta -> diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/ButtonsContent.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/ButtonsContent.kt new file mode 100644 index 0000000..27e0d82 --- /dev/null +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/ButtonsContent.kt @@ -0,0 +1,63 @@ +package com.kyant.backdrop.catalog.destinations + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.text.BasicText +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.kyant.backdrop.catalog.BackdropDemoScaffold +import com.kyant.backdrop.catalog.components.LiquidButton + +@Composable +fun ButtonsContent() { + BackdropDemoScaffold { backdrop -> + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16f.dp) + ) { + LiquidButton( + {}, + backdrop + ) { + BasicText( + "Transparent Liquid Button", + style = TextStyle(Color.Black, 15f.sp) + ) + } + LiquidButton( + {}, + backdrop, + surfaceColor = Color.White.copy(0.3f) + ) { + BasicText( + "Surface Liquid Button", + style = TextStyle(Color.Black, 15f.sp) + ) + } + LiquidButton( + {}, + backdrop, + tint = Color(0xFF0088FF) + ) { + BasicText( + "Tinted Liquid Button", + style = TextStyle(Color.White, 15f.sp) + ) + } + LiquidButton( + {}, + backdrop, + tint = Color(0xFFFF8D28) + ) { + BasicText( + "Tinted Liquid Button", + style = TextStyle(Color.White, 15f.sp) + ) + } + } + } +} diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/ControlCenterContent.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/ControlCenterContent.kt index da2fcf2..91f0ef7 100644 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/ControlCenterContent.kt +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/ControlCenterContent.kt @@ -3,7 +3,6 @@ package com.kyant.backdrop.catalog.destinations import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.EaseIn import androidx.compose.animation.core.spring -import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.Orientation @@ -21,7 +20,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember @@ -32,7 +30,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.draw.paint import androidx.compose.ui.draw.scale -import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.BlurEffect import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter @@ -40,7 +37,6 @@ import androidx.compose.ui.graphics.GraphicsLayerScope import androidx.compose.ui.graphics.TransformOrigin import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.layout import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp @@ -49,18 +45,16 @@ import androidx.compose.ui.util.fastCoerceIn import androidx.compose.ui.util.fastRoundToInt import androidx.compose.ui.util.lerp import com.kyant.backdrop.BackdropEffectScope -import com.kyant.backdrop.backdrop -import com.kyant.backdrop.catalog.ProgressConverter +import com.kyant.backdrop.catalog.BackdropDemoScaffold import com.kyant.backdrop.catalog.R -import com.kyant.backdrop.catalog.rememberUISensor -import com.kyant.backdrop.catalog.theme.LocalContentColor +import com.kyant.backdrop.catalog.utils.ProgressConverter +import com.kyant.backdrop.catalog.utils.rememberUISensor import com.kyant.backdrop.contentBackdrop import com.kyant.backdrop.drawBackdrop import com.kyant.backdrop.effects.refraction import com.kyant.backdrop.effects.saturation import com.kyant.backdrop.highlight.Highlight import com.kyant.backdrop.highlight.HighlightStyle -import com.kyant.backdrop.rememberBackdrop import com.kyant.capsule.ContinuousCapsule import com.kyant.capsule.ContinuousRoundedRectangle import kotlinx.coroutines.launch @@ -104,7 +98,6 @@ fun ControlCenterContent() { } val maxDragHeight = 1000f - val backdrop = rememberBackdrop() val uiSensor = rememberUISensor() val glassShape = { itemShape } val glassHighlight = { Highlight { HighlightStyle.Dynamic(angle = uiSensor.gravityAngle) } } @@ -134,217 +127,154 @@ fun ControlCenterContent() { } } - CompositionLocalProvider(LocalContentColor provides Color.White) { - Box( + val backdropModifier = Modifier + .draggable( + rememberDraggableState { delta -> + val targetProgress = enterProgressAnimation.value + delta / maxDragHeight + animationScope.launch { + launch { + enterProgressAnimation.snapTo(targetProgress) + } + launch { + safeEnterProgressAnimation.snapTo(targetProgress.fastCoerceIn(0f, 1f)) + } + } + }, + Orientation.Vertical, + onDragStopped = { velocity -> + val targetProgress = when { + velocity < 0f -> 0f + velocity > 0f -> 1f + else -> if (enterProgressAnimation.value < 0.5f) 0f else 1f + } + animationScope.launch { + launch { + enterProgressAnimation.animateTo( + targetProgress, + if (targetProgress > 0.5f) { + spring(0.5f, 300f, 0.5f / maxDragHeight) + } else { + spring(1f, 300f, 0.01f) + }, + velocity / maxDragHeight + ) + } + launch { + safeEnterProgressAnimation.animateTo( + targetProgress, + spring(1f, 300f, 0.01f) + ) + } + } + } + ) + .drawWithContent { + val progress = safeEnterProgressAnimation.value + + drawContent() + drawRect(dimColor.copy(dimColor.alpha * progress)) + } + .graphicsLayer { + val progress = safeEnterProgressAnimation.value + + val blurRadius = 4f.dp.toPx() * progress + if (blurRadius > 0f) { + renderEffect = BlurEffect(blurRadius, blurRadius) + } + } + + BackdropDemoScaffold( + backdropModifier, + initialPainterResId = R.drawable.system_home_screen_light + ) { backdrop -> + Column( Modifier - .draggable( - rememberDraggableState { delta -> - val targetProgress = enterProgressAnimation.value + delta / maxDragHeight - animationScope.launch { - launch { - enterProgressAnimation.snapTo(targetProgress) - } - launch { - safeEnterProgressAnimation.snapTo(targetProgress.fastCoerceIn(0f, 1f)) - } - } - }, - Orientation.Vertical, - onDragStopped = { velocity -> - val targetProgress = when { - velocity < 0f -> 0f - velocity > 0f -> 1f - else -> if (enterProgressAnimation.value < 0.5f) 0f else 1f - } - animationScope.launch { - launch { - enterProgressAnimation.animateTo( - targetProgress, - if (targetProgress > 0.5f) { - spring(0.5f, 300f, 0.5f / maxDragHeight) - } else { - spring(1f, 300f, 0.01f) - }, - velocity / maxDragHeight - ) - } - launch { - safeEnterProgressAnimation.animateTo( - targetProgress, - spring(1f, 300f, 0.01f) - ) - } - } - } - ) - .fillMaxSize() + .padding(top = 80f.dp) + .systemBarsPadding() + .displayCutoutPadding() + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally ) { - Image( - painterResource(R.drawable.system_home_screen_light), - null, - Modifier - .backdrop(backdrop) - .drawWithContent { - val progress = safeEnterProgressAnimation.value - - drawContent() - drawRect(dimColor.copy(dimColor.alpha * progress)) - } - .graphicsLayer { - val progress = safeEnterProgressAnimation.value - - val blurRadius = 4f.dp.toPx() * progress - if (blurRadius > 0f) { - renderEffect = BlurEffect(blurRadius, blurRadius) - } - } - .fillMaxSize(), - contentScale = ContentScale.Crop - ) - - Column( - Modifier - .padding(top = 80f.dp) - .systemBarsPadding() - .displayCutoutPadding() - .fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally + Row( + horizontalArrangement = Arrangement.spacedBy(itemSpacing), + verticalAlignment = Alignment.CenterVertically ) { - Row( - horizontalArrangement = Arrangement.spacedBy(itemSpacing), - verticalAlignment = Alignment.CenterVertically + Box( + Modifier + .drawBackdrop( + backdrop, + { ContinuousRoundedRectangle(40f.dp) }, + layer = glassLayer, + highlight = null, + shadow = null, + onDrawSurface = glassSurface, + effects = glassEffects + ) + .contentBackdrop( + { ContinuousRoundedRectangle(40f.dp) }, + highlight = glassHighlight, + shadow = null + ) { + refraction(12f.dp.toPx(), size.minDimension / 4f, true) + } + .size(itemTwoSpanSize) + .padding(itemSpacing) ) { Box( Modifier - .drawBackdrop( - backdrop, - { ContinuousRoundedRectangle(40f.dp) }, - layerBlock = { - glassLayer() - }, - highlight = null, - shadow = null, - onDrawSurface = glassSurface, - effects = glassEffects - ) - .contentBackdrop( - { ContinuousRoundedRectangle(40f.dp) }, - highlight = glassHighlight, - shadow = null - ) { - refraction(12f.dp.toPx(), size.minDimension / 4f, true) - } - .size(itemTwoSpanSize) - .padding(itemSpacing) - ) { - Box( - Modifier - .clip(innerItemShape) - .background(inactiveItemColor) - .clickable {} - .scale(innerItemIconScale) - .paint(airplaneModeIcon, colorFilter = iconColorFilter) - .size(innerItemSize) - .align(Alignment.TopStart) - ) - Box( - Modifier - .clip(innerItemShape) - .background(activeItemColor) - .clickable {} - .scale(innerItemIconScale) - .paint(airplaneModeIcon, colorFilter = iconColorFilter) - .size(innerItemSize) - .align(Alignment.TopEnd) - ) - Box( - Modifier - .clip(innerItemShape) - .background(activeItemColor) - .clickable {} - .scale(innerItemIconScale) - .paint(airplaneModeIcon, colorFilter = iconColorFilter) - .size(innerItemSize) - .align(Alignment.BottomStart) - ) - } + .clip(innerItemShape) + .background(inactiveItemColor) + .clickable {} + .scale(innerItemIconScale) + .paint(airplaneModeIcon, colorFilter = iconColorFilter) + .size(innerItemSize) + .align(Alignment.TopStart) + ) Box( Modifier - .drawBackdrop( - backdrop, - glassShape, - layerBlock = glassLayer, - highlight = glassHighlight, - shadow = null, - onDrawSurface = glassSurface, - effects = glassEffects - ) - .size(itemTwoSpanSize) + .clip(innerItemShape) + .background(activeItemColor) + .clickable {} + .scale(innerItemIconScale) + .paint(airplaneModeIcon, colorFilter = iconColorFilter) + .size(innerItemSize) + .align(Alignment.TopEnd) + ) + Box( + Modifier + .clip(innerItemShape) + .background(activeItemColor) + .clickable {} + .scale(innerItemIconScale) + .paint(airplaneModeIcon, colorFilter = iconColorFilter) + .size(innerItemSize) + .align(Alignment.BottomStart) ) } - - Spacer(spacerLayoutModifier) - - Row( - horizontalArrangement = Arrangement.spacedBy(itemSpacing, Alignment.CenterHorizontally), - verticalAlignment = Alignment.CenterVertically - ) { - Column( - verticalArrangement = Arrangement.spacedBy(itemSpacing) - ) { - Row( - horizontalArrangement = Arrangement.spacedBy(itemSpacing), - verticalAlignment = Alignment.CenterVertically - ) { - Box( - Modifier - .drawBackdrop( - backdrop, - glassShape, - layerBlock = glassLayer, - highlight = glassHighlight, - shadow = null, - onDrawSurface = glassSurface, - effects = glassEffects - ) - .clickable {} - .paint(airplaneModeIcon, colorFilter = iconColorFilter) - .size(itemSize) - ) - Box( - Modifier - .drawBackdrop( - backdrop, - glassShape, - layerBlock = glassLayer, - highlight = glassHighlight, - shadow = null, - onDrawSurface = glassSurface, - effects = glassEffects - ) - .clickable {} - .paint(airplaneModeIcon, colorFilter = iconColorFilter) - .size(itemSize) - ) - } - Box( - Modifier - .drawBackdrop( - backdrop, - glassShape, - layerBlock = glassLayer, - highlight = glassHighlight, - shadow = null, - onDrawSurface = { - drawRect(accentColor, blendMode = BlendMode.Hue) - drawRect(accentColor.copy(0.6f)) - }, - effects = glassEffects - ) - .size(itemTwoSpanSize, itemSize) + Box( + Modifier + .drawBackdrop( + backdrop, + glassShape, + layer = glassLayer, + highlight = glassHighlight, + shadow = null, + onDrawSurface = glassSurface, + effects = glassEffects ) - } + .size(itemTwoSpanSize) + ) + } + Spacer(spacerLayoutModifier) + + Row( + horizontalArrangement = Arrangement.spacedBy(itemSpacing, Alignment.CenterHorizontally), + verticalAlignment = Alignment.CenterVertically + ) { + Column( + verticalArrangement = Arrangement.spacedBy(itemSpacing) + ) { Row( horizontalArrangement = Arrangement.spacedBy(itemSpacing), verticalAlignment = Alignment.CenterVertically @@ -354,32 +284,46 @@ fun ControlCenterContent() { .drawBackdrop( backdrop, glassShape, - layerBlock = glassLayer, + layer = glassLayer, highlight = glassHighlight, shadow = null, onDrawSurface = glassSurface, effects = glassEffects ) - .size(itemSize, itemTwoSpanSize) + .clickable {} + .paint(airplaneModeIcon, colorFilter = iconColorFilter) + .size(itemSize) ) Box( Modifier .drawBackdrop( backdrop, glassShape, - layerBlock = glassLayer, + layer = glassLayer, highlight = glassHighlight, shadow = null, onDrawSurface = glassSurface, effects = glassEffects ) - .size(itemSize, itemTwoSpanSize) + .clickable {} + .paint(airplaneModeIcon, colorFilter = iconColorFilter) + .size(itemSize) ) } + Box( + Modifier + .drawBackdrop( + backdrop, + glassShape, + layer = glassLayer, + highlight = glassHighlight, + shadow = null, + effects = glassEffects + ) + .size(itemTwoSpanSize, itemSize) + ) } - Spacer(spacerLayoutModifier) - Row( horizontalArrangement = Arrangement.spacedBy(itemSpacing), verticalAlignment = Alignment.CenterVertically @@ -389,73 +333,107 @@ fun ControlCenterContent() { .drawBackdrop( backdrop, glassShape, - layerBlock = glassLayer, + layer = glassLayer, highlight = glassHighlight, shadow = null, onDrawSurface = glassSurface, effects = glassEffects ) - .size(itemTwoSpanSize) + .size(itemSize, itemTwoSpanSize) ) + Box( + Modifier + .drawBackdrop( + backdrop, + glassShape, + layer = glassLayer, + highlight = glassHighlight, + shadow = null, + onDrawSurface = glassSurface, + effects = glassEffects + ) + .size(itemSize, itemTwoSpanSize) + ) + } + } - Column( - verticalArrangement = Arrangement.spacedBy(itemSpacing) + Spacer(spacerLayoutModifier) + + Row( + horizontalArrangement = Arrangement.spacedBy(itemSpacing), + verticalAlignment = Alignment.CenterVertically + ) { + Box( + Modifier + .drawBackdrop( + backdrop, + glassShape, + layer = glassLayer, + highlight = glassHighlight, + shadow = null, + onDrawSurface = glassSurface, + effects = glassEffects + ) + .size(itemTwoSpanSize) + ) + + Column( + verticalArrangement = Arrangement.spacedBy(itemSpacing) + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(itemSpacing), + verticalAlignment = Alignment.CenterVertically ) { - Row( - horizontalArrangement = Arrangement.spacedBy(itemSpacing), - verticalAlignment = Alignment.CenterVertically - ) { - Box( - Modifier - .drawBackdrop( - backdrop, - glassShape, - layerBlock = glassLayer, - highlight = glassHighlight, - shadow = null, - onDrawSurface = glassSurface, - effects = glassEffects - ) - .clickable {} - .paint(airplaneModeIcon, colorFilter = iconColorFilter) - .size(itemSize) - ) - Box( - Modifier - .drawBackdrop( - backdrop, - glassShape, - layerBlock = glassLayer, - highlight = glassHighlight, - shadow = null, - onDrawSurface = glassSurface, - effects = glassEffects - ) - .clickable {} - .paint(airplaneModeIcon, colorFilter = iconColorFilter) - .size(itemSize) - ) - } - Row( - horizontalArrangement = Arrangement.spacedBy(itemSpacing), - verticalAlignment = Alignment.CenterVertically - ) { - Box( - Modifier - .drawBackdrop( - backdrop, - glassShape, - layerBlock = glassLayer, - highlight = glassHighlight, - shadow = null, - onDrawSurface = glassSurface, - effects = glassEffects - ) - .clickable {} - .paint(airplaneModeIcon, colorFilter = iconColorFilter) - .size(itemSize) - ) - } + Box( + Modifier + .drawBackdrop( + backdrop, + glassShape, + layer = glassLayer, + highlight = glassHighlight, + shadow = null, + onDrawSurface = glassSurface, + effects = glassEffects + ) + .clickable {} + .paint(airplaneModeIcon, colorFilter = iconColorFilter) + .size(itemSize) + ) + Box( + Modifier + .drawBackdrop( + backdrop, + glassShape, + layer = glassLayer, + highlight = glassHighlight, + shadow = null, + onDrawSurface = glassSurface, + effects = glassEffects + ) + .clickable {} + .paint(airplaneModeIcon, colorFilter = iconColorFilter) + .size(itemSize) + ) + } + Row( + horizontalArrangement = Arrangement.spacedBy(itemSpacing), + verticalAlignment = Alignment.CenterVertically + ) { + Box( + Modifier + .drawBackdrop( + backdrop, + glassShape, + layer = glassLayer, + highlight = glassHighlight, + shadow = null, + onDrawSurface = glassSurface, + effects = glassEffects + ) + .clickable {} + .paint(airplaneModeIcon, colorFilter = iconColorFilter) + .size(itemSize) + ) } } } diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/DialogContent.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/DialogContent.kt index b5b9ac9..4ad8142 100644 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/DialogContent.kt +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/DialogContent.kt @@ -1,6 +1,5 @@ package com.kyant.backdrop.catalog.destinations -import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.isSystemInDarkTheme @@ -12,40 +11,33 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.BasicText import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.drawWithCache -import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.graphics.layer.drawLayer -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.kyant.backdrop.backdrop +import com.kyant.backdrop.catalog.BackdropDemoScaffold import com.kyant.backdrop.catalog.R -import com.kyant.backdrop.catalog.components.LoremIpsum -import com.kyant.backdrop.catalog.components.Text -import com.kyant.backdrop.catalog.theme.LocalContentColor import com.kyant.backdrop.drawBackdrop import com.kyant.backdrop.effects.blur import com.kyant.backdrop.effects.colorFilter import com.kyant.backdrop.effects.refraction import com.kyant.backdrop.highlight.Highlight import com.kyant.backdrop.highlight.HighlightStyle -import com.kyant.backdrop.rememberBackdrop import com.kyant.capsule.ContinuousCapsule import com.kyant.capsule.ContinuousRoundedRectangle @Composable fun DialogContent() { val isLightTheme = !isSystemInDarkTheme() + val contentColor = if (isLightTheme) Color.Black else Color.White val accentColor = if (isLightTheme) Color(0xFF0088FF) else Color(0xFF0091FF) @@ -56,20 +48,13 @@ fun DialogContent() { if (isLightTheme) Color(0xFF29293A).copy(0.23f) else Color(0xFF121212).copy(0.56f) - val backdrop = rememberBackdrop() - - Box(Modifier.fillMaxSize()) { - Image( - painterResource(R.drawable.system_home_screen_light), - null, + BackdropDemoScaffold( + initialPainterResId = R.drawable.system_home_screen_light + ) { backdrop -> + Box( Modifier - .backdrop(backdrop) - .drawWithContent { - drawContent() - drawRect(dimColor) - } - .fillMaxSize(), - contentScale = ContentScale.Crop + .background(dimColor) + .fillMaxSize() ) Column( @@ -89,49 +74,27 @@ fun DialogContent() { refraction(24f.dp.toPx(), 48f.dp.toPx(), true) } .fillMaxWidth() - .align(Alignment.Center) ) { - Text( + BasicText( "Dialog Title", - TextStyle(fontSize = 24f.sp, fontWeight = FontWeight.Medium), - Modifier.padding(28f.dp, 24f.dp, 28f.dp, 12f.dp) + Modifier.padding(28f.dp, 24f.dp, 28f.dp, 12f.dp), + style = TextStyle(contentColor, 24f.sp, FontWeight.Medium) ) - Text( + BasicText( LoremIpsum, - TextStyle(fontSize = 15f.sp), Modifier .then( if (isLightTheme) { // plus darker Modifier - .drawWithCache { - val bottomLayer = obtainGraphicsLayer().apply { - blendMode = BlendMode.Overlay - } - val topLayer = obtainGraphicsLayer().apply { - blendMode = BlendMode.Luminosity - } - - onDrawWithContent { - bottomLayer.record { - this@onDrawWithContent.drawContent() - } - topLayer.record { - this@onDrawWithContent.drawContent() - } - - drawLayer(bottomLayer) - drawLayer(topLayer) - } - } } else { // plus lighter Modifier.graphicsLayer(blendMode = BlendMode.Plus) } ) .padding(24f.dp, 12f.dp, 24f.dp, 12f.dp), - color = LocalContentColor.current.copy(0.68f), + style = TextStyle(contentColor.copy(0.68f), 15f.sp), maxLines = 5 ) @@ -153,9 +116,9 @@ fun DialogContent() { horizontalArrangement = Arrangement.spacedBy(4f.dp, Alignment.CenterHorizontally), verticalAlignment = Alignment.CenterVertically ) { - Text( + BasicText( "Cancel", - TextStyle(fontSize = 16f.sp) + style = TextStyle(contentColor, 16f.sp) ) } Row( @@ -169,13 +132,15 @@ fun DialogContent() { horizontalArrangement = Arrangement.spacedBy(4f.dp, Alignment.CenterHorizontally), verticalAlignment = Alignment.CenterVertically ) { - Text( + BasicText( "Okay", - TextStyle(fontSize = 16f.sp), - color = Color.White + style = TextStyle(Color.White, 16f.sp) ) } } } } } + +private const val LoremIpsum = + """Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.""" diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/GlassPlaygroundContent.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/GlassPlaygroundContent.kt index ccaccb4..ce7cb05 100644 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/GlassPlaygroundContent.kt +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/GlassPlaygroundContent.kt @@ -2,16 +2,13 @@ package com.kyant.backdrop.catalog.destinations import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.VectorConverter -import androidx.compose.foundation.Image import androidx.compose.foundation.gestures.detectTransformGestures import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.paint import androidx.compose.ui.geometry.Offset @@ -19,15 +16,13 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.TransformOrigin import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import com.kyant.backdrop.backdrop +import com.kyant.backdrop.catalog.BackdropDemoScaffold import com.kyant.backdrop.catalog.R import com.kyant.backdrop.drawBackdrop import com.kyant.backdrop.effects.refraction import com.kyant.backdrop.effects.saturation -import com.kyant.backdrop.rememberBackdrop import com.kyant.capsule.ContinuousRoundedRectangle import kotlinx.coroutines.launch import kotlin.math.PI @@ -41,29 +36,18 @@ fun GlassPlaygroundContent() { if (isLightTheme) Color(0xFF0088FF) else Color(0xFF0091FF) - val backdrop = rememberBackdrop() - val animationScope = rememberCoroutineScope() val offsetAnimation = remember { Animatable(Offset.Zero, Offset.VectorConverter) } val zoomAnimation = remember { Animatable(1f) } val rotationAnimation = remember { Animatable(0f) } - Box(Modifier.fillMaxSize()) { - Image( - painterResource(R.drawable.wallpaper_light), - null, - Modifier - .backdrop(backdrop) - .fillMaxSize(), - contentScale = ContentScale.Crop - ) - + BackdropDemoScaffold { backdrop -> Box( Modifier .drawBackdrop( backdrop, { ContinuousRoundedRectangle(48f.dp) }, - layerBlock = { + layer = { val offset = offsetAnimation.value val zoom = zoomAnimation.value val rotation = rotationAnimation.value @@ -107,7 +91,6 @@ fun GlassPlaygroundContent() { colorFilter = ColorFilter.tint(accentColor) ) .size(256f.dp) - .align(Alignment.Center) ) } } diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/HomeContent.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/HomeContent.kt index 0c69eb9..441cf69 100644 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/HomeContent.kt +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/HomeContent.kt @@ -1,6 +1,7 @@ package com.kyant.backdrop.catalog.destinations import androidx.compose.foundation.clickable +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.displayCutoutPadding @@ -9,16 +10,22 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.BasicText import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.kyant.backdrop.catalog.CatalogDestination -import com.kyant.backdrop.catalog.components.Text -import com.kyant.backdrop.catalog.theme.TextStyles @Composable fun HomeContent(onNavigate: (CatalogDestination) -> Unit) { + val isLightTheme = !isSystemInDarkTheme() + val contentColor = if (isLightTheme) Color.Black else Color.White + Column( Modifier .verticalScroll(rememberScrollState()) @@ -27,76 +34,55 @@ fun HomeContent(onNavigate: (CatalogDestination) -> Unit) { .fillMaxSize(), verticalArrangement = Arrangement.spacedBy(16f.dp) ) { - Text( + BasicText( "Backdrop Catalog", - TextStyles.headline, - Modifier.padding(16f.dp, 40f.dp, 16f.dp, 16f.dp) + Modifier.padding(16f.dp, 40f.dp, 16f.dp, 16f.dp), + style = TextStyle(contentColor, 28f.sp, FontWeight.Medium) ) + Column { - Text( - "Glass playground", - TextStyles.body, - Modifier - .clickable { onNavigate(CatalogDestination.GlassPlayground) } - .padding(16f.dp) - .fillMaxWidth() - ) - Text( - "Adaptive luminance glass", - TextStyles.body, - Modifier - .clickable { onNavigate(CatalogDestination.AdaptiveLuminanceGlass) } - .padding(16f.dp) - .fillMaxWidth() - ) - Text( - "Control center", - TextStyles.body, - Modifier - .clickable { onNavigate(CatalogDestination.ControlCenter) } - .padding(16f.dp) - .fillMaxWidth() - ) - Text( - "Bottom tabs", - TextStyles.body, - Modifier - .clickable { onNavigate(CatalogDestination.BottomTabs) } - .padding(16f.dp) - .fillMaxWidth() - ) - Text( - "Icon button", - TextStyles.body, - Modifier - .clickable { onNavigate(CatalogDestination.IconButton) } - .padding(16f.dp) - .fillMaxWidth() - ) - Text( - "Dialog", - TextStyles.body, - Modifier - .clickable { onNavigate(CatalogDestination.Dialog) } - .padding(16f.dp) - .fillMaxWidth() - ) - Text( - "Scroll container", - TextStyles.body, - Modifier - .clickable { onNavigate(CatalogDestination.ScrollContainer) } - .padding(16f.dp) - .fillMaxWidth() - ) - Text( - "Lazy scroll container", - TextStyles.body, - Modifier - .clickable { onNavigate(CatalogDestination.LazyScrollContainer) } - .padding(16f.dp) - .fillMaxWidth() - ) + Subtitle("Liquid glass components") + ListItem({ onNavigate(CatalogDestination.Buttons) }, "Buttons") + ListItem({ onNavigate(CatalogDestination.BottomTabs) }, "Bottom tabs") + ListItem({ onNavigate(CatalogDestination.Dialog) }, "Dialog") + + Subtitle("System UIs") + ListItem({ onNavigate(CatalogDestination.ControlCenter) }, "Control center") + + Subtitle("Experiments") + ListItem({ onNavigate(CatalogDestination.GlassPlayground) }, "Glass playground") + ListItem({ onNavigate(CatalogDestination.AdaptiveLuminanceGlass) }, "Adaptive luminance glass") + ListItem({ onNavigate(CatalogDestination.ScrollContainer) }, "Scroll container") + ListItem({ onNavigate(CatalogDestination.LazyScrollContainer) }, "Lazy scroll container") } } } + +@Composable +private fun Subtitle(label: String) { + BasicText( + label, + Modifier + .padding(16f.dp, 24f.dp, 16f.dp, 8f.dp) + .fillMaxWidth(), + style = TextStyle(Color(0xFF0088FF), 15f.sp, FontWeight.Medium) + ) +} + +@Composable +private fun ListItem( + onClick: () -> Unit, + label: String +) { + val isLightTheme = !isSystemInDarkTheme() + val contentColor = if (isLightTheme) Color.Black else Color.White + + BasicText( + label, + Modifier + .clickable(onClick = onClick) + .padding(16f.dp) + .fillMaxWidth(), + style = TextStyle(contentColor, 17f.sp) + ) +} diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/IconButtonContent.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/IconButtonContent.kt deleted file mode 100644 index f2420d3..0000000 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/IconButtonContent.kt +++ /dev/null @@ -1,163 +0,0 @@ -package com.kyant.backdrop.catalog.destinations - -import androidx.compose.animation.core.Animatable -import androidx.compose.animation.core.VectorConverter -import androidx.compose.animation.core.spring -import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.drawWithContent -import androidx.compose.ui.draw.paint -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.BlendMode -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.dp -import com.kyant.backdrop.backdrop -import com.kyant.backdrop.catalog.R -import com.kyant.backdrop.catalog.inspectDragGestures -import com.kyant.backdrop.drawBackdrop -import com.kyant.backdrop.effects.blur -import com.kyant.backdrop.effects.refraction -import com.kyant.backdrop.effects.saturation -import com.kyant.backdrop.rememberBackdrop -import kotlinx.coroutines.launch -import kotlin.math.abs -import kotlin.math.atan2 -import kotlin.math.cos -import kotlin.math.sin -import kotlin.math.tanh - -@Composable -fun IconButtonContent() { - val isLightTheme = !isSystemInDarkTheme() - val accentColor = - if (isLightTheme) Color(0xFF0088FF) - else Color(0xFF0091FF) - val containerColor = - if (isLightTheme) Color(0xFFFAFAFA).copy(0.4f) - else Color(0xFF121212).copy(0.4f) - - val backdrop = rememberBackdrop() - - val animationScope = rememberCoroutineScope() - val pressAnimation = remember { Animatable(0f) } - val offsetAnimation = remember { Animatable(Offset.Zero, Offset.VectorConverter) } - val scaleAnimation = remember { Animatable(0f) } - - Box(Modifier.fillMaxSize()) { - Image( - painterResource(R.drawable.system_home_screen_light), - null, - Modifier - .backdrop(backdrop) - .fillMaxSize(), - contentScale = ContentScale.Crop - ) - - Box( - Modifier - .drawBackdrop( - backdrop, - { CircleShape }, - layerBlock = { - val offset = offsetAnimation.value - val scale = 1f + 12f.dp.toPx() / size.minDimension * scaleAnimation.value - val offsetAngle = atan2(offset.y, offset.x) - val deltaScale = 4f.dp.toPx() / size.minDimension - translationX = 12f.dp.toPx() * tanh(0.5f * offset.x / size.width) - translationY = 12f.dp.toPx() * tanh(0.5f * offset.y / size.height) - scaleX = scale + deltaScale * abs(cos(offsetAngle) * offset.x / size.width) - scaleY = scale + deltaScale * abs(sin(offsetAngle) * offset.y / size.height) - }, - onDrawSurface = { drawRect(containerColor) } - ) { - saturation() - blur(8f.dp.toPx()) - refraction(12f.dp.toPx(), size.minDimension / 2f) - } - .drawWithContent { - drawContent() - drawRect( - Color.White.copy(0.3f * pressAnimation.value), - blendMode = BlendMode.Plus - ) - } - .clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = null - ) {} - .pointerInput(animationScope) { - val onDragStop: () -> Unit = { - animationScope.launch { - launch { - pressAnimation.animateTo( - 0f, - spring(1f, 300f) - ) - } - launch { - offsetAnimation.animateTo( - Offset.Zero, - spring(1f, 300f) - ) - } - launch { - scaleAnimation.animateTo( - 0f, - spring(0.5f, 300f) - ) - } - } - } - - inspectDragGestures( - onDragStart = { down -> - animationScope.launch { - launch { - pressAnimation.animateTo( - 1f, - spring(1f, 300f) - ) - } - launch { - offsetAnimation.snapTo(Offset.Zero) - } - launch { - scaleAnimation.animateTo( - 1f, - spring(0.5f, 300f) - ) - } - } - }, - onDragEnd = { onDragStop() }, - onDragCancel = onDragStop - ) { _, dragAmount -> - animationScope.launch { - offsetAnimation.snapTo(offsetAnimation.value + dragAmount) - } - } - } - .paint( - painterResource(R.drawable.flight_40px), - colorFilter = ColorFilter.tint(accentColor) - ) - .size(64f.dp) - .align(Alignment.Center) - ) - } -} diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/LazyScrollContainerContent.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/LazyScrollContainerContent.kt index f1df237..2f837d1 100644 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/LazyScrollContainerContent.kt +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/LazyScrollContainerContent.kt @@ -1,6 +1,5 @@ package com.kyant.backdrop.catalog.destinations -import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues @@ -15,31 +14,16 @@ import androidx.compose.foundation.layout.windowInsetsTopHeight import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import com.kyant.backdrop.backdrop -import com.kyant.backdrop.catalog.R +import com.kyant.backdrop.catalog.BackdropDemoScaffold import com.kyant.backdrop.drawBackdrop import com.kyant.backdrop.effects.refraction import com.kyant.backdrop.effects.saturation -import com.kyant.backdrop.rememberBackdrop import com.kyant.capsule.ContinuousRoundedRectangle @Composable fun LazyScrollContainerContent() { - val backdrop = rememberBackdrop() - - Box(Modifier.fillMaxSize()) { - Image( - painterResource(R.drawable.wallpaper_light), - null, - Modifier - .backdrop(backdrop) - .fillMaxSize(), - contentScale = ContentScale.Crop - ) - + BackdropDemoScaffold { backdrop -> LazyColumn( Modifier.fillMaxSize(), contentPadding = PaddingValues(16f.dp), diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/ScrollContainerContent.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/ScrollContainerContent.kt index 615e1ef..a4e36bf 100644 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/ScrollContainerContent.kt +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/destinations/ScrollContainerContent.kt @@ -1,6 +1,5 @@ package com.kyant.backdrop.catalog.destinations -import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -14,31 +13,16 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import com.kyant.backdrop.backdrop -import com.kyant.backdrop.catalog.R +import com.kyant.backdrop.catalog.BackdropDemoScaffold import com.kyant.backdrop.drawBackdrop import com.kyant.backdrop.effects.refraction import com.kyant.backdrop.effects.saturation -import com.kyant.backdrop.rememberBackdrop import com.kyant.capsule.ContinuousRoundedRectangle @Composable fun ScrollContainerContent() { - val backdrop = rememberBackdrop() - - Box(Modifier.fillMaxSize()) { - Image( - painterResource(R.drawable.wallpaper_light), - null, - Modifier - .backdrop(backdrop) - .fillMaxSize(), - contentScale = ContentScale.Crop - ) - + BackdropDemoScaffold { backdrop -> Column( Modifier .verticalScroll(rememberScrollState()) diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/theme/ContentColor.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/theme/ContentColor.kt deleted file mode 100644 index 5417179..0000000 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/theme/ContentColor.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.kyant.backdrop.catalog.theme - -import androidx.compose.runtime.ProvidableCompositionLocal -import androidx.compose.runtime.staticCompositionLocalOf -import androidx.compose.ui.graphics.Color - -val LocalContentColor: ProvidableCompositionLocal = - staticCompositionLocalOf { Color.Black } diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/theme/TextStyles.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/theme/TextStyles.kt deleted file mode 100644 index e951483..0000000 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/theme/TextStyles.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.kyant.backdrop.catalog.theme - -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp - -object TextStyles { - - val body: TextStyle = TextStyle(fontSize = 16f.sp) - - val headline: TextStyle = TextStyle(fontSize = 28f.sp, fontWeight = FontWeight.Medium) -} diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/DragGestureInspector.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/utils/DragGestureInspector.kt similarity index 98% rename from catalog/src/main/java/com/kyant/backdrop/catalog/DragGestureInspector.kt rename to catalog/src/main/java/com/kyant/backdrop/catalog/utils/DragGestureInspector.kt index 4fed13e..d3ae410 100644 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/DragGestureInspector.kt +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/utils/DragGestureInspector.kt @@ -1,4 +1,4 @@ -package com.kyant.backdrop.catalog +package com.kyant.backdrop.catalog.utils import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/ProgressConverter.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/utils/ProgressConverter.kt similarity index 89% rename from catalog/src/main/java/com/kyant/backdrop/catalog/ProgressConverter.kt rename to catalog/src/main/java/com/kyant/backdrop/catalog/utils/ProgressConverter.kt index 940d52f..f485233 100644 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/ProgressConverter.kt +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/utils/ProgressConverter.kt @@ -1,4 +1,4 @@ -package com.kyant.backdrop.catalog +package com.kyant.backdrop.catalog.utils import kotlin.math.abs import kotlin.math.exp diff --git a/catalog/src/main/java/com/kyant/backdrop/catalog/UISensor.kt b/catalog/src/main/java/com/kyant/backdrop/catalog/utils/UISensor.kt similarity index 98% rename from catalog/src/main/java/com/kyant/backdrop/catalog/UISensor.kt rename to catalog/src/main/java/com/kyant/backdrop/catalog/utils/UISensor.kt index c8c89a0..78afb4f 100644 --- a/catalog/src/main/java/com/kyant/backdrop/catalog/UISensor.kt +++ b/catalog/src/main/java/com/kyant/backdrop/catalog/utils/UISensor.kt @@ -1,4 +1,4 @@ -package com.kyant.backdrop.catalog +package com.kyant.backdrop.catalog.utils import android.content.Context import android.hardware.Sensor diff --git a/liquid-glass/src/main/java/com/kyant/liquidglass/Deprecated.kt b/liquid-glass/src/main/java/com/kyant/liquidglass/Deprecated.kt index d90e9cc..a7e3bb5 100644 --- a/liquid-glass/src/main/java/com/kyant/liquidglass/Deprecated.kt +++ b/liquid-glass/src/main/java/com/kyant/liquidglass/Deprecated.kt @@ -48,7 +48,7 @@ fun Modifier.liquidGlass( this .drawBackdrop( backdrop = backdrop, - shapeProvider = { style().shape }, + shape = { style().shape }, highlight = { style().highlight?.let { Highlight(