Add workaround for blur in ElementLogoAtom for OS < 9. (#1061)

* Add workaround for blur in `ElementLogoAtom` for OS < 9.

* Update screenshots

* Pass `useBlurredShadow` to `ElementLogoAtom`

* Update screenshots

---------

Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
Jorge Martin Espinosa 2023-08-16 17:13:49 +02:00 committed by GitHub
parent 8a62abe93e
commit 75c19a7e04
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 178 additions and 84 deletions

View file

@ -16,7 +16,6 @@
package io.element.android.libraries.designsystem.atomic.atoms
import android.graphics.BlurMaskFilter
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@ -27,24 +26,16 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.blur
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.RoundRect
import androidx.compose.ui.graphics.ClipOp
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.clipPath
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.R
import io.element.android.libraries.designsystem.modifiers.blurCompat
import io.element.android.libraries.designsystem.modifiers.blurredShapeShadow
import io.element.android.libraries.designsystem.modifiers.canUseBlurMaskFilter
import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.theme.ElementTheme
@ -53,6 +44,7 @@ import io.element.android.libraries.theme.ElementTheme
fun ElementLogoAtom(
size: ElementLogoAtomSize,
modifier: Modifier = Modifier,
useBlurredShadow: Boolean = canUseBlurMaskFilter(),
darkTheme: Boolean = isSystemInDarkTheme(),
) {
val blur = if (darkTheme) 160.dp else 24.dp
@ -66,22 +58,35 @@ fun ElementLogoAtom(
.border(size.borderWidth, borderColor, RoundedCornerShape(size.cornerRadius)),
contentAlignment = Alignment.Center,
) {
Box(
Modifier
.size(size.outerSize)
.shapeShadow(
color = shadowColor,
cornerRadius = size.cornerRadius,
blurRadius = size.shadowRadius,
offsetY = 8.dp,
)
)
if (useBlurredShadow) {
Box(
Modifier
.size(size.outerSize)
.blurredShapeShadow(
color = shadowColor,
cornerRadius = size.cornerRadius,
blurRadius = size.shadowRadius,
offsetY = 8.dp,
)
)
} else {
Box(
Modifier
.size(size.outerSize)
.shadow(
elevation = size.shadowRadius,
shape = RoundedCornerShape(size.cornerRadius),
clip = false,
ambientColor = shadowColor
)
)
}
Box(
Modifier
.clip(RoundedCornerShape(size.cornerRadius))
.size(size.outerSize)
.background(backgroundColor)
.blur(blur)
.blurCompat(blur)
)
Image(
modifier = Modifier.size(size.logoSize),
@ -121,44 +126,6 @@ sealed class ElementLogoAtomSize(
)
}
fun Modifier.shapeShadow(
color: Color = Color.Black,
cornerRadius: Dp = 0.dp,
offsetX: Dp = 0.dp,
offsetY: Dp = 0.dp,
blurRadius: Dp = 0.dp,
) = then(
drawBehind {
drawIntoCanvas { canvas ->
val path = Path().apply {
addRoundRect(RoundRect(Rect(Offset.Zero, size), CornerRadius(cornerRadius.toPx())))
}
clipPath(path, ClipOp.Difference) {
val paint = Paint()
val frameworkPaint = paint.asFrameworkPaint()
if (blurRadius != 0.dp) {
frameworkPaint.maskFilter = BlurMaskFilter(blurRadius.toPx(), BlurMaskFilter.Blur.NORMAL)
}
frameworkPaint.color = color.toArgb()
val leftPixel = offsetX.toPx()
val topPixel = offsetY.toPx()
val rightPixel = size.width + topPixel
val bottomPixel = size.height + leftPixel
canvas.drawRect(
left = leftPixel,
top = topPixel,
right = rightPixel,
bottom = bottomPixel,
paint = paint,
)
}
}
}
)
@Composable
@DayNightPreviews
internal fun ElementLogoAtomMediumPreview() {
@ -172,7 +139,19 @@ internal fun ElementLogoAtomLargePreview() {
}
@Composable
private fun ContentToPreview(elementLogoAtomSize: ElementLogoAtomSize) {
@DayNightPreviews
internal fun ElementLogoAtomMediumNoBlurShadowPreview() {
ContentToPreview(ElementLogoAtomSize.Medium, useBlurredShadow = false)
}
@Composable
@DayNightPreviews
internal fun ElementLogoAtomLargeNoBlurShadowPreview() {
ContentToPreview(ElementLogoAtomSize.Large, useBlurredShadow = false)
}
@Composable
private fun ContentToPreview(elementLogoAtomSize: ElementLogoAtomSize, useBlurredShadow: Boolean = true) {
ElementPreview {
Box(
Modifier
@ -180,7 +159,7 @@ private fun ContentToPreview(elementLogoAtomSize: ElementLogoAtomSize) {
.background(ElementTheme.colors.bgSubtlePrimary),
contentAlignment = Alignment.Center
) {
ElementLogoAtom(elementLogoAtomSize)
ElementLogoAtom(elementLogoAtomSize, useBlurredShadow = useBlurredShadow)
}
}
}

View file

@ -0,0 +1,103 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.designsystem.modifiers
import android.graphics.BlurMaskFilter
import android.os.Build
import androidx.annotation.ChecksSdkIntAtLeast
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.BlurredEdgeTreatment
import androidx.compose.ui.draw.blur
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.RoundRect
import androidx.compose.ui.graphics.ClipOp
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.clipPath
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
/**
* @return true if the blur modifier is supported on the current OS version.
*
* The docs say the `blur` modifier is only supported on Android 12+:
* https://developer.android.com/reference/kotlin/androidx/compose/ui/Modifier#(androidx.compose.ui.Modifier).blur(androidx.compose.ui.unit.Dp,androidx.compose.ui.draw.BlurredEdgeTreatment)
* */
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S)
fun canUseBlur(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
@Composable
fun canUseBlurMaskFilter() = !LocalView.current.isHardwareAccelerated
fun Modifier.blurredShapeShadow(
color: Color = Color.Black,
cornerRadius: Dp = 0.dp,
offsetX: Dp = 0.dp,
offsetY: Dp = 0.dp,
blurRadius: Dp = 0.dp,
) = then(
drawBehind {
drawIntoCanvas { canvas ->
val path = Path().apply {
addRoundRect(RoundRect(Rect(Offset.Zero, size), CornerRadius(cornerRadius.toPx())))
}
// Draw the blurred shadow, then cut out the shape from it
clipPath(path, ClipOp.Difference) {
val paint = Paint()
val frameworkPaint = paint.asFrameworkPaint()
if (blurRadius != 0.dp) {
frameworkPaint.maskFilter = BlurMaskFilter(blurRadius.toPx(), BlurMaskFilter.Blur.NORMAL)
}
frameworkPaint.color = color.toArgb()
val leftPixel = offsetX.toPx()
val topPixel = offsetY.toPx()
val rightPixel = size.width + topPixel
val bottomPixel = size.height + leftPixel
canvas.drawRect(
left = leftPixel,
top = topPixel,
right = rightPixel,
bottom = bottomPixel,
paint = paint,
)
}
}
}
)
fun Modifier.blurCompat(
radius: Dp,
edgeTreatment: BlurredEdgeTreatment = BlurredEdgeTreatment.Rectangle
): Modifier = composed {
when {
radius.value == 0f -> this
canUseBlur() -> blur(radius, edgeTreatment)
else -> this // Added in case we find a way to make this work on older devices
}
}

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6d2eab27a1132893542b240399a4003b4049b72903a822077f69dd9dafd26f71
size 299106
oid sha256:2cc8d498816a04c537ad2835068a2badab656aa723a900b471c8f79320d7967c
size 300473

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fbad2f74e329a424e75ef96cb0c427a7baba044f3267346756324c39ebd6add6
size 404196
oid sha256:634dc94c58f6e7aed8abf9c11f33e81a65e81c439b247321a398e3728d5696d4
size 401397

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:56f53e2c13480d5f31d38c671612c81fa802e1f35a55ada99d544917f01b91ba
size 338952
oid sha256:4bf8563c80e874ff79850e37bbf71609aac72c62c74cbe8255e403bd00feadc3
size 326514

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cfe3663daab250bcd733ab746e4864472691b40cf927a3cd73d15b323dec2b68
size 329945
oid sha256:45984b598d30a2511e03644927a9f1f34b4173f2eebb8a328a19df8643385f18
size 317243

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c449e6e4162f12a6be7faa668b930b294489ae217c2a7493f5671145a926079e
size 340944
oid sha256:1d96057db48fd7e3461fde86b84c09688c0af49e51bcd94affa2df9c9785b6f5
size 327755

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0355120442254880360b982df6c2c7cce84a471f632436c96e79ffafed35de14
size 324378
oid sha256:4007453c80be5700ec9942983ef903a2404e7e3879d82c189a5e95687916e8eb
size 310597

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5d05151967955c961ca47619de2f28f86d0350299b61fbef0371c1706439a844
size 421908
oid sha256:5b07c105d41caf87c34a506138ff5d43df853d9b7b7143e0099f222886ca217e
size 420316

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3a2056882e65afe8fc2d0dc60efc7912ca5674acb57e3690136a8704f9f063ed
size 407942
oid sha256:738ee59035ecbc006ccd43ff10ce847d6d741da780b3e189c221b55a1b6ec06c
size 405327

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6521d409bc1fc70fa422ffec400d9f20d0b7fe16adebb7f5b0a9e5f135328ffc
size 422213
oid sha256:1b3ba2db52bd7ee1713ba1b276b0e3a4a7edf406a8833027d548c9fdf4dbe838
size 419123

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d39cfdb0db12a6d14f3105aa168ea002a845d2083f07ecfabe0858739507018d
size 396789
oid sha256:4bd69417650f09e240405d839464ba716d55d01e6230a9aa8b923460b4c88443
size 393093

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3f76220968b3e28b2c1ddd91b4036bf037a98e97c859e8616314ed953862cc64
size 42525

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3321dd27c1fbe47df862dad9b591faf9279da1ea4eb66b7b900d8c962280a74e
size 31811

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ca4e1e3cd5d7cb9fd8416bc7074272b759bbc501993829b6edef6809cf519aee
size 27532

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8615ceb43c29becd4cfe6062d8fc844b15b717e8f2e5d7b151bdc4478de59381
size 20607