Compose 1.5.0 (#3584)
https://android-developers.googleblog.com/2023/08/whats-new-in-jetpack-compose-august-23-release.html https://google.github.io/accompanist/navigation-animation https://google.github.io/accompanist/pager https://google.github.io/accompanist/placeholder * Update dependency androidx.compose:compose-bom to v2023.08.00 * Update dependency androidx.navigation:navigation-compose to v2.7.0 * Migrate rememberPagerState & HorizontalPager * material-motion-compose-core:1.0.4 * Migrate AnimatedNavHost * Remove accompanist-insets-ui * Add Placeholder sources from the original repo * Migrate accompanist-insets-ui * Minor Compose cleanups * io.github.fornewid:material-motion-compose-core:1.0.6 * Remove accompanist-pager * Remove ExperimentalAnimationApi imports * Migrate ModalBottomSheetState * Revert changes about insets-ui * Cleanup --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This commit is contained in:
+4
-6
@@ -326,8 +326,9 @@ dependencies {
|
||||
implementation 'com.github.ChickenHook:RestrictionBypass:2.2'
|
||||
implementation 'dev.rikka.tools.refine:runtime:4.3.0'
|
||||
|
||||
implementation platform("androidx.compose:compose-bom:2023.06.01")
|
||||
implementation platform("androidx.compose:compose-bom:2023.08.00")
|
||||
implementation "androidx.compose.ui:ui"
|
||||
implementation "androidx.compose.ui:ui-util"
|
||||
debugImplementation "androidx.compose.ui:ui-tooling"
|
||||
implementation "androidx.compose.ui:ui-text-google-fonts"
|
||||
implementation "androidx.compose.foundation:foundation"
|
||||
@@ -338,18 +339,15 @@ dependencies {
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
||||
implementation "androidx.activity:activity-compose:1.7.2"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1"
|
||||
implementation "androidx.navigation:navigation-compose:2.6.0"
|
||||
implementation "androidx.navigation:navigation-compose:2.7.0"
|
||||
implementation "androidx.palette:palette-ktx:1.0.0"
|
||||
implementation "androidx.slice:slice-core:1.1.0-alpha02"
|
||||
implementation "com.google.accompanist:accompanist-drawablepainter:$accompanist_version"
|
||||
implementation "com.google.accompanist:accompanist-insets-ui:$accompanist_version"
|
||||
implementation "com.google.accompanist:accompanist-navigation-animation:$accompanist_version"
|
||||
implementation "com.google.accompanist:accompanist-permissions:$accompanist_version"
|
||||
implementation "com.google.accompanist:accompanist-pager:$accompanist_version"
|
||||
implementation "com.google.accompanist:accompanist-placeholder-material:$accompanist_version"
|
||||
implementation "com.google.accompanist:accompanist-systemuicontroller:$accompanist_version"
|
||||
implementation "com.google.android.material:material:1.9.0"
|
||||
implementation "io.github.fornewid:material-motion-compose-core:0.12.3"
|
||||
implementation "io.github.fornewid:material-motion-compose-core:1.0.6"
|
||||
implementation 'dev.kdrag0n:colorkt:1.0.5'
|
||||
implementation 'io.coil-kt:coil-compose:2.4.0'
|
||||
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
|
||||
|
||||
@@ -9,7 +9,6 @@ import android.widget.Toast
|
||||
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
@@ -30,6 +29,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.navArgument
|
||||
import app.lawnchair.backup.LawnchairBackup
|
||||
import app.lawnchair.ui.preferences.LocalNavController
|
||||
@@ -40,11 +40,9 @@ import app.lawnchair.util.BackHandler
|
||||
import app.lawnchair.util.hasFlag
|
||||
import app.lawnchair.util.restartLauncher
|
||||
import com.android.launcher3.R
|
||||
import com.google.accompanist.navigation.animation.composable
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.Base64
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
fun NavGraphBuilder.restoreBackupGraph(route: String) {
|
||||
preferenceGraph(route, {}) { subRoute ->
|
||||
composable(
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright 2021 The Android Open Source Project
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 app.lawnchair.ui.placeholder
|
||||
|
||||
import androidx.compose.animation.core.FiniteAnimationSpec
|
||||
import androidx.compose.animation.core.InfiniteRepeatableSpec
|
||||
import androidx.compose.animation.core.MutableTransitionState
|
||||
import androidx.compose.animation.core.RepeatMode
|
||||
import androidx.compose.animation.core.Transition
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.infiniteRepeatable
|
||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.geometry.toRect
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Outline
|
||||
import androidx.compose.ui.graphics.Paint
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.drawOutline
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
|
||||
import androidx.compose.ui.node.Ref
|
||||
import androidx.compose.ui.platform.debugInspectorInfo
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
|
||||
/**
|
||||
* Contains default values used by [Modifier.placeholder] and [PlaceholderHighlight].
|
||||
*/
|
||||
object PlaceholderDefaults {
|
||||
/**
|
||||
* The default [InfiniteRepeatableSpec] to use for [fade].
|
||||
*/
|
||||
val fadeAnimationSpec: InfiniteRepeatableSpec<Float> by lazy {
|
||||
infiniteRepeatable(
|
||||
animation = tween(delayMillis = 200, durationMillis = 600),
|
||||
repeatMode = RepeatMode.Reverse,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The default [InfiniteRepeatableSpec] to use for [shimmer].
|
||||
*/
|
||||
val shimmerAnimationSpec: InfiniteRepeatableSpec<Float> by lazy {
|
||||
infiniteRepeatable(
|
||||
animation = tween(durationMillis = 1700, delayMillis = 200),
|
||||
repeatMode = RepeatMode.Restart,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws some skeleton UI which is typically used whilst content is 'loading'.
|
||||
*
|
||||
* A version of this modifier which uses appropriate values for Material themed apps is available
|
||||
* in the 'Placeholder Material' library.
|
||||
*
|
||||
* You can provide a [PlaceholderHighlight] which runs an highlight animation on the placeholder.
|
||||
* The [shimmer] and [fade] implementations are provided for easy usage.
|
||||
*
|
||||
* A cross-fade transition will be applied to the content and placeholder UI when the [visible]
|
||||
* value changes. The transition can be customized via the [contentFadeTransitionSpec] and
|
||||
* [placeholderFadeTransitionSpec] parameters.
|
||||
*
|
||||
* You can find more information on the pattern at the Material Theming
|
||||
* [Placeholder UI](https://material.io/design/communication/launch-screen.html#placeholder-ui)
|
||||
* guidelines.
|
||||
*
|
||||
* @param visible whether the placeholder should be visible or not.
|
||||
* @param color the color used to draw the placeholder UI.
|
||||
* @param shape desired shape of the placeholder. Defaults to [RectangleShape].
|
||||
* @param highlight optional highlight animation.
|
||||
* @param placeholderFadeTransitionSpec The transition spec to use when fading the placeholder
|
||||
* on/off screen. The boolean parameter defined for the transition is [visible].
|
||||
* @param contentFadeTransitionSpec The transition spec to use when fading the content
|
||||
* on/off screen. The boolean parameter defined for the transition is [visible].
|
||||
*/
|
||||
fun Modifier.placeholder(
|
||||
visible: Boolean,
|
||||
color: Color,
|
||||
shape: Shape = RectangleShape,
|
||||
highlight: PlaceholderHighlight? = null,
|
||||
placeholderFadeTransitionSpec: @Composable Transition.Segment<Boolean>.() -> FiniteAnimationSpec<Float> = { spring() },
|
||||
contentFadeTransitionSpec: @Composable Transition.Segment<Boolean>.() -> FiniteAnimationSpec<Float> = { spring() },
|
||||
): Modifier = composed(
|
||||
inspectorInfo = debugInspectorInfo {
|
||||
name = "placeholder"
|
||||
value = visible
|
||||
properties["visible"] = visible
|
||||
properties["color"] = color
|
||||
properties["highlight"] = highlight
|
||||
properties["shape"] = shape
|
||||
},
|
||||
) {
|
||||
// Values used for caching purposes
|
||||
val lastSize = remember { Ref<Size>() }
|
||||
val lastLayoutDirection = remember { Ref<LayoutDirection>() }
|
||||
val lastOutline = remember { Ref<Outline>() }
|
||||
|
||||
// The current highlight animation progress
|
||||
var highlightProgress: Float by remember { mutableFloatStateOf(0f) }
|
||||
|
||||
// This is our crossfade transition
|
||||
val transitionState = remember { MutableTransitionState(visible) }.apply {
|
||||
targetState = visible
|
||||
}
|
||||
val transition = updateTransition(transitionState, "placeholder_crossfade")
|
||||
|
||||
val placeholderAlpha by transition.animateFloat(
|
||||
transitionSpec = placeholderFadeTransitionSpec,
|
||||
label = "placeholder_fade",
|
||||
targetValueByState = { placeholderVisible -> if (placeholderVisible) 1f else 0f },
|
||||
)
|
||||
val contentAlpha by transition.animateFloat(
|
||||
transitionSpec = contentFadeTransitionSpec,
|
||||
label = "content_fade",
|
||||
targetValueByState = { placeholderVisible -> if (placeholderVisible) 0f else 1f },
|
||||
)
|
||||
|
||||
// Run the optional animation spec and update the progress if the placeholder is visible
|
||||
val animationSpec = highlight?.animationSpec
|
||||
if (animationSpec != null && (visible || placeholderAlpha >= 0.01f)) {
|
||||
val infiniteTransition = rememberInfiniteTransition(label = "")
|
||||
highlightProgress = infiniteTransition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = 1f,
|
||||
animationSpec = animationSpec,
|
||||
label = "",
|
||||
).value
|
||||
}
|
||||
|
||||
val paint = remember { Paint() }
|
||||
remember(color, shape, highlight) {
|
||||
drawWithContent {
|
||||
// Draw the composable content first
|
||||
if (contentAlpha in 0.01f..0.99f) {
|
||||
// If the content alpha is between 1% and 99%, draw it in a layer with
|
||||
// the alpha applied
|
||||
paint.alpha = contentAlpha
|
||||
withLayer(paint) {
|
||||
with(this@drawWithContent) {
|
||||
drawContent()
|
||||
}
|
||||
}
|
||||
} else if (contentAlpha >= 0.99f) {
|
||||
// If the content alpha is > 99%, draw it with no alpha
|
||||
drawContent()
|
||||
}
|
||||
|
||||
if (placeholderAlpha in 0.01f..0.99f) {
|
||||
// If the placeholder alpha is between 1% and 99%, draw it in a layer with
|
||||
// the alpha applied
|
||||
paint.alpha = placeholderAlpha
|
||||
withLayer(paint) {
|
||||
lastOutline.value = drawPlaceholder(
|
||||
shape = shape,
|
||||
color = color,
|
||||
highlight = highlight,
|
||||
progress = highlightProgress,
|
||||
lastOutline = lastOutline.value,
|
||||
lastLayoutDirection = lastLayoutDirection.value,
|
||||
lastSize = lastSize.value,
|
||||
)
|
||||
}
|
||||
} else if (placeholderAlpha >= 0.99f) {
|
||||
// If the placeholder alpha is > 99%, draw it with no alpha
|
||||
lastOutline.value = drawPlaceholder(
|
||||
shape = shape,
|
||||
color = color,
|
||||
highlight = highlight,
|
||||
progress = highlightProgress,
|
||||
lastOutline = lastOutline.value,
|
||||
lastLayoutDirection = lastLayoutDirection.value,
|
||||
lastSize = lastSize.value,
|
||||
)
|
||||
}
|
||||
|
||||
// Keep track of the last size & layout direction
|
||||
lastSize.value = size
|
||||
lastLayoutDirection.value = layoutDirection
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun DrawScope.drawPlaceholder(
|
||||
shape: Shape,
|
||||
color: Color,
|
||||
highlight: PlaceholderHighlight?,
|
||||
progress: Float,
|
||||
lastOutline: Outline?,
|
||||
lastLayoutDirection: LayoutDirection?,
|
||||
lastSize: Size?,
|
||||
): Outline? {
|
||||
// shortcut to avoid Outline calculation and allocation
|
||||
if (shape === RectangleShape) {
|
||||
// Draw the initial background color
|
||||
drawRect(color = color)
|
||||
|
||||
if (highlight != null) {
|
||||
drawRect(
|
||||
brush = highlight.brush(progress, size),
|
||||
alpha = highlight.alpha(progress),
|
||||
)
|
||||
}
|
||||
// We didn't create an outline so return null
|
||||
return null
|
||||
}
|
||||
|
||||
// Otherwise we need to create an outline from the shape
|
||||
val outline = lastOutline.takeIf {
|
||||
size == lastSize && layoutDirection == lastLayoutDirection
|
||||
} ?: shape.createOutline(size, layoutDirection, this)
|
||||
|
||||
// Draw the placeholder color
|
||||
drawOutline(outline = outline, color = color)
|
||||
|
||||
if (highlight != null) {
|
||||
drawOutline(
|
||||
outline = outline,
|
||||
brush = highlight.brush(progress, size),
|
||||
alpha = highlight.alpha(progress),
|
||||
)
|
||||
}
|
||||
|
||||
// Return the outline we used
|
||||
return outline
|
||||
}
|
||||
|
||||
private inline fun DrawScope.withLayer(
|
||||
paint: Paint,
|
||||
drawBlock: DrawScope.() -> Unit,
|
||||
) = drawIntoCanvas { canvas ->
|
||||
canvas.saveLayer(size.toRect(), paint)
|
||||
drawBlock()
|
||||
canvas.restore()
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2021 The Android Open Source Project
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 app.lawnchair.ui.placeholder
|
||||
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.compose.animation.core.AnimationSpec
|
||||
import androidx.compose.animation.core.InfiniteRepeatableSpec
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.util.lerp
|
||||
import kotlin.math.max
|
||||
|
||||
/**
|
||||
* A class which provides a brush to paint placeholder based on progress.
|
||||
*/
|
||||
@Stable
|
||||
interface PlaceholderHighlight {
|
||||
/**
|
||||
* The optional [AnimationSpec] to use when running the animation for this highlight.
|
||||
*/
|
||||
val animationSpec: InfiniteRepeatableSpec<Float>?
|
||||
|
||||
/**
|
||||
* Return a [Brush] to draw for the given [progress] and [size].
|
||||
*
|
||||
* @param progress the current animated progress in the range of 0f..1f.
|
||||
* @param size The size of the current layout to draw in.
|
||||
*/
|
||||
fun brush(
|
||||
@FloatRange(from = 0.0, to = 1.0) progress: Float,
|
||||
size: Size,
|
||||
): Brush
|
||||
|
||||
/**
|
||||
* Return the desired alpha value used for drawing the [Brush] returned from [brush].
|
||||
*
|
||||
* @param progress the current animated progress in the range of 0f..1f.
|
||||
*/
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
fun alpha(progress: Float): Float
|
||||
|
||||
companion object
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [Fade] brush with the given initial and target colors.
|
||||
*
|
||||
* @param highlightColor the color of the highlight which is faded in/out.
|
||||
* @param animationSpec the [AnimationSpec] to configure the animation.
|
||||
*/
|
||||
fun PlaceholderHighlight.Companion.fade(
|
||||
highlightColor: Color,
|
||||
animationSpec: InfiniteRepeatableSpec<Float> = PlaceholderDefaults.fadeAnimationSpec,
|
||||
): PlaceholderHighlight = Fade(
|
||||
highlightColor = highlightColor,
|
||||
animationSpec = animationSpec,
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [PlaceholderHighlight] which 'shimmers', using the given [highlightColor].
|
||||
*
|
||||
* The highlight starts at the top-start, and then grows to the bottom-end during the animation.
|
||||
* During that time it is also faded in, from 0f..progressForMaxAlpha, and then faded out from
|
||||
* progressForMaxAlpha..1f.
|
||||
*
|
||||
* @param highlightColor the color of the highlight 'shimmer'.
|
||||
* @param animationSpec the [AnimationSpec] to configure the animation.
|
||||
* @param progressForMaxAlpha The progress where the shimmer should be at it's peak opacity.
|
||||
* Defaults to 0.6f.
|
||||
*/
|
||||
fun PlaceholderHighlight.Companion.shimmer(
|
||||
highlightColor: Color,
|
||||
animationSpec: InfiniteRepeatableSpec<Float> = PlaceholderDefaults.shimmerAnimationSpec,
|
||||
@FloatRange(from = 0.0, to = 1.0) progressForMaxAlpha: Float = 0.6f,
|
||||
): PlaceholderHighlight = Shimmer(
|
||||
highlightColor = highlightColor,
|
||||
animationSpec = animationSpec,
|
||||
progressForMaxAlpha = progressForMaxAlpha,
|
||||
)
|
||||
|
||||
private data class Fade(
|
||||
private val highlightColor: Color,
|
||||
override val animationSpec: InfiniteRepeatableSpec<Float>,
|
||||
) : PlaceholderHighlight {
|
||||
private val brush = SolidColor(highlightColor)
|
||||
|
||||
override fun brush(progress: Float, size: Size): Brush = brush
|
||||
override fun alpha(progress: Float): Float = progress
|
||||
}
|
||||
|
||||
private data class Shimmer(
|
||||
private val highlightColor: Color,
|
||||
override val animationSpec: InfiniteRepeatableSpec<Float>,
|
||||
private val progressForMaxAlpha: Float = 0.6f,
|
||||
) : PlaceholderHighlight {
|
||||
override fun brush(
|
||||
progress: Float,
|
||||
size: Size,
|
||||
): Brush = Brush.radialGradient(
|
||||
colors = listOf(
|
||||
highlightColor.copy(alpha = 0f),
|
||||
highlightColor,
|
||||
highlightColor.copy(alpha = 0f),
|
||||
),
|
||||
center = Offset(x = 0f, y = 0f),
|
||||
radius = (max(size.width, size.height) * progress * 2).coerceAtLeast(0.01f),
|
||||
)
|
||||
|
||||
override fun alpha(progress: Float): Float = when {
|
||||
// From 0f...ProgressForOpaqueAlpha we animate from 0..1
|
||||
progress <= progressForMaxAlpha -> {
|
||||
lerp(
|
||||
start = 0f,
|
||||
stop = 1f,
|
||||
fraction = progress / progressForMaxAlpha,
|
||||
)
|
||||
}
|
||||
// From ProgressForOpaqueAlpha..1f we animate from 1..0
|
||||
else -> {
|
||||
lerp(
|
||||
start = 1f,
|
||||
stop = 0f,
|
||||
fraction = (progress - progressForMaxAlpha) / (1f - progressForMaxAlpha),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2021 The Android Open Source Project
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 app.lawnchair.ui.placeholder.material
|
||||
|
||||
import androidx.compose.animation.core.FiniteAnimationSpec
|
||||
import androidx.compose.animation.core.Transition
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.contentColorFor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.compositeOver
|
||||
import androidx.compose.ui.graphics.isSpecified
|
||||
import app.lawnchair.ui.placeholder.PlaceholderDefaults
|
||||
import app.lawnchair.ui.placeholder.PlaceholderHighlight
|
||||
import app.lawnchair.ui.placeholder.placeholder
|
||||
|
||||
/**
|
||||
* Returns the value used as the the `color` parameter value on [Modifier.placeholder].
|
||||
*
|
||||
* @param backgroundColor The current background color of the layout. Defaults to
|
||||
* `MaterialTheme.colors.surface`.
|
||||
* @param contentColor The content color to be used on top of [backgroundColor].
|
||||
* @param contentAlpha The alpha component to set on [contentColor] when compositing the color
|
||||
* on top of [backgroundColor]. Defaults to `0.1f`.
|
||||
*/
|
||||
@Composable
|
||||
fun PlaceholderDefaults.color(
|
||||
backgroundColor: Color = MaterialTheme.colors.surface,
|
||||
contentColor: Color = contentColorFor(backgroundColor),
|
||||
contentAlpha: Float = 0.1f,
|
||||
): Color = contentColor.copy(contentAlpha).compositeOver(backgroundColor)
|
||||
|
||||
/**
|
||||
* Returns the value used as the the `highlightColor` parameter value of
|
||||
* [PlaceholderHighlight.Companion.fade].
|
||||
*
|
||||
* @param backgroundColor The current background color of the layout. Defaults to
|
||||
* `MaterialTheme.colors.surface`.
|
||||
* @param alpha The alpha component to set on [backgroundColor]. Defaults to `0.3f`.
|
||||
*/
|
||||
@Composable
|
||||
fun PlaceholderDefaults.fadeHighlightColor(
|
||||
backgroundColor: Color = MaterialTheme.colors.surface,
|
||||
alpha: Float = 0.3f,
|
||||
): Color = backgroundColor.copy(alpha = alpha)
|
||||
|
||||
/**
|
||||
* Returns the value used as the the `highlightColor` parameter value of
|
||||
* [PlaceholderHighlight.Companion.shimmer].
|
||||
*
|
||||
* @param backgroundColor The current background color of the layout. Defaults to
|
||||
* `MaterialTheme.colors.surface`.
|
||||
* @param alpha The alpha component to set on [backgroundColor]. Defaults to `0.75f`.
|
||||
*/
|
||||
@Composable
|
||||
fun PlaceholderDefaults.shimmerHighlightColor(
|
||||
backgroundColor: Color = MaterialTheme.colors.surface,
|
||||
alpha: Float = 0.75f,
|
||||
): Color {
|
||||
return backgroundColor.copy(alpha = alpha)
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws some skeleton UI which is typically used whilst content is 'loading'.
|
||||
*
|
||||
* To customize the color and shape of the placeholder, you can use the foundation version of
|
||||
* [Modifier.placeholder], along with the values provided by [PlaceholderDefaults].
|
||||
*
|
||||
* A cross-fade transition will be applied to the content and placeholder UI when the [visible]
|
||||
* value changes. The transition can be customized via the [contentFadeTransitionSpec] and
|
||||
* [placeholderFadeTransitionSpec] parameters.
|
||||
*
|
||||
* You can provide a [PlaceholderHighlight] which runs an highlight animation on the placeholder.
|
||||
* The [shimmer] and [fade] implementations are provided for easy usage.
|
||||
*
|
||||
* You can find more information on the pattern at the Material Theming
|
||||
* [Placeholder UI](https://material.io/design/communication/launch-screen.html#placeholder-ui)
|
||||
* guidelines.
|
||||
*
|
||||
* @param visible whether the placeholder should be visible or not.
|
||||
* @param color the color used to draw the placeholder UI. If [Color.Unspecified] is provided,
|
||||
* the placeholder will use [PlaceholderDefaults.color].
|
||||
* @param shape desired shape of the placeholder. If null is provided the placeholder
|
||||
* will use the small shape set in [MaterialTheme.shapes].
|
||||
* @param highlight optional highlight animation.
|
||||
* @param placeholderFadeTransitionSpec The transition spec to use when fading the placeholder
|
||||
* on/off screen. The boolean parameter defined for the transition is [visible].
|
||||
* @param contentFadeTransitionSpec The transition spec to use when fading the content
|
||||
* on/off screen. The boolean parameter defined for the transition is [visible].
|
||||
*/
|
||||
fun Modifier.placeholder(
|
||||
visible: Boolean,
|
||||
color: Color = Color.Unspecified,
|
||||
shape: Shape? = null,
|
||||
highlight: PlaceholderHighlight? = null,
|
||||
placeholderFadeTransitionSpec: @Composable Transition.Segment<Boolean>.() -> FiniteAnimationSpec<Float> = { spring() },
|
||||
contentFadeTransitionSpec: @Composable Transition.Segment<Boolean>.() -> FiniteAnimationSpec<Float> = { spring() },
|
||||
): Modifier = composed {
|
||||
Modifier.placeholder(
|
||||
visible = visible,
|
||||
color = if (color.isSpecified) color else PlaceholderDefaults.color(),
|
||||
shape = shape ?: MaterialTheme.shapes.small,
|
||||
highlight = highlight,
|
||||
placeholderFadeTransitionSpec = placeholderFadeTransitionSpec,
|
||||
contentFadeTransitionSpec = contentFadeTransitionSpec,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2021 The Android Open Source Project
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 app.lawnchair.ui.placeholder.material
|
||||
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.compose.animation.core.AnimationSpec
|
||||
import androidx.compose.animation.core.InfiniteRepeatableSpec
|
||||
import androidx.compose.runtime.Composable
|
||||
import app.lawnchair.ui.placeholder.PlaceholderDefaults
|
||||
import app.lawnchair.ui.placeholder.PlaceholderHighlight
|
||||
import app.lawnchair.ui.placeholder.fade
|
||||
import app.lawnchair.ui.placeholder.shimmer
|
||||
|
||||
/**
|
||||
* Creates a [PlaceholderHighlight] which fades in an appropriate color, using the
|
||||
* given [animationSpec].
|
||||
*
|
||||
* @param animationSpec the [AnimationSpec] to configure the animation.
|
||||
*/
|
||||
@Composable
|
||||
fun PlaceholderHighlight.Companion.fade(
|
||||
animationSpec: InfiniteRepeatableSpec<Float> = PlaceholderDefaults.fadeAnimationSpec,
|
||||
): PlaceholderHighlight = PlaceholderHighlight.fade(
|
||||
highlightColor = PlaceholderDefaults.fadeHighlightColor(),
|
||||
animationSpec = animationSpec,
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a [PlaceholderHighlight] which 'shimmers', using a default color.
|
||||
*
|
||||
* The highlight starts at the top-start, and then grows to the bottom-end during the animation.
|
||||
* During that time it is also faded in, from 0f..progressForMaxAlpha, and then faded out from
|
||||
* progressForMaxAlpha..1f.
|
||||
*
|
||||
* @param animationSpec the [AnimationSpec] to configure the animation.
|
||||
* @param progressForMaxAlpha The progress where the shimmer should be at it's peak opacity.
|
||||
* Defaults to 0.6f.
|
||||
*/
|
||||
@Composable
|
||||
fun PlaceholderHighlight.Companion.shimmer(
|
||||
animationSpec: InfiniteRepeatableSpec<Float> = PlaceholderDefaults.shimmerAnimationSpec,
|
||||
@FloatRange(from = 0.0, to = 1.0) progressForMaxAlpha: Float = 0.6f,
|
||||
): PlaceholderHighlight = PlaceholderHighlight.shimmer(
|
||||
highlightColor = PlaceholderDefaults.shimmerHighlightColor(),
|
||||
animationSpec = animationSpec,
|
||||
progressForMaxAlpha = progressForMaxAlpha,
|
||||
)
|
||||
@@ -73,8 +73,10 @@ private fun CustomIconShapePreference() {
|
||||
|
||||
val appliedIconShape = customIconShapeAdapter.state.value
|
||||
val selectedIconShape = remember { mutableStateOf(appliedIconShape ?: IconShape.Circle) }
|
||||
val selectedIconShapeApplied = derivedStateOf {
|
||||
appliedIconShape.toString() == selectedIconShape.value.toString()
|
||||
val selectedIconShapeApplied = remember {
|
||||
derivedStateOf {
|
||||
appliedIconShape.toString() == selectedIconShape.value.toString()
|
||||
}
|
||||
}
|
||||
|
||||
PreferenceLayout(
|
||||
|
||||
@@ -5,7 +5,6 @@ import android.content.Intent
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
@@ -26,6 +25,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.navArgument
|
||||
import app.lawnchair.font.FontCache
|
||||
import app.lawnchair.font.googlefonts.GoogleFontsListing
|
||||
@@ -37,10 +37,7 @@ import app.lawnchair.ui.AndroidText
|
||||
import app.lawnchair.ui.OverflowMenu
|
||||
import app.lawnchair.ui.preferences.components.*
|
||||
import com.android.launcher3.R
|
||||
import com.google.accompanist.navigation.animation.composable
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
fun NavGraphBuilder.fontSelectionGraph(route: String) {
|
||||
preferenceGraph(route, {}) { subRoute ->
|
||||
composable(
|
||||
@@ -81,16 +78,18 @@ fun FontSelection(fontPref: BasePreferenceManager.FontPref) {
|
||||
}
|
||||
value = list
|
||||
}
|
||||
val allItems by derivedStateOf { customFonts + items }
|
||||
val allItems by remember { derivedStateOf { items + customFonts } }
|
||||
val adapter = fontPref.getAdapter()
|
||||
var searchQuery by remember { mutableStateOf("") }
|
||||
|
||||
val hasFilter by derivedStateOf { searchQuery.isNotEmpty() }
|
||||
val filteredItems by derivedStateOf {
|
||||
if (hasFilter) {
|
||||
val lowerCaseQuery = searchQuery.lowercase()
|
||||
allItems.filter { it.displayName.lowercase().contains(lowerCaseQuery) }
|
||||
} else items
|
||||
val hasFilter by remember { derivedStateOf { searchQuery.isNotEmpty() } }
|
||||
val filteredItems by remember {
|
||||
derivedStateOf {
|
||||
if (hasFilter) {
|
||||
val lowerCaseQuery = searchQuery.lowercase()
|
||||
allItems.filter { it.displayName.lowercase().contains(lowerCaseQuery) }
|
||||
} else items
|
||||
}
|
||||
}
|
||||
|
||||
val request = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -49,8 +49,8 @@ fun HomeScreenGridPreferences() {
|
||||
|
||||
val originalColumns = remember { columnsAdapter.state.value }
|
||||
val originalRows = remember { rowsAdapter.state.value }
|
||||
val columns = rememberSaveable { mutableStateOf(originalColumns) }
|
||||
val rows = rememberSaveable { mutableStateOf(originalRows) }
|
||||
val columns = rememberSaveable { mutableIntStateOf(originalColumns) }
|
||||
val rows = rememberSaveable { mutableIntStateOf(originalRows) }
|
||||
|
||||
if (isPortrait) {
|
||||
GridOverridesPreview(
|
||||
@@ -60,7 +60,7 @@ fun HomeScreenGridPreferences() {
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.clip(MaterialTheme.shapes.large)
|
||||
) {
|
||||
copy(numColumns = columns.value, numRows = rows.value)
|
||||
copy(numColumns = columns.intValue, numRows = rows.intValue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,8 +85,8 @@ fun HomeScreenGridPreferences() {
|
||||
val context = LocalContext.current
|
||||
val applyOverrides = {
|
||||
prefs.batchEdit {
|
||||
columnsAdapter.onChange(columns.value)
|
||||
rowsAdapter.onChange(rows.value)
|
||||
columnsAdapter.onChange(columns.intValue)
|
||||
rowsAdapter.onChange(rows.intValue)
|
||||
}
|
||||
LauncherAppState.getIDP(context).onPreferencesChanged(context)
|
||||
navController.popBackStack()
|
||||
@@ -103,7 +103,7 @@ fun HomeScreenGridPreferences() {
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterEnd)
|
||||
.fillMaxWidth(),
|
||||
enabled = columns.value != originalColumns || rows.value != originalRows
|
||||
enabled = columns.intValue != originalColumns || rows.intValue != originalRows
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.apply_grid))
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import android.os.Process
|
||||
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
@@ -27,6 +26,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.navArgument
|
||||
import app.lawnchair.icons.*
|
||||
import app.lawnchair.ui.OverflowMenu
|
||||
@@ -37,13 +37,11 @@ import app.lawnchair.util.requireSystemService
|
||||
import com.android.launcher3.R
|
||||
import com.google.accompanist.drawablepainter.rememberDrawablePainter
|
||||
import com.google.accompanist.insets.ui.LocalScaffoldPadding
|
||||
import com.google.accompanist.navigation.animation.composable
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.launch
|
||||
import androidx.compose.material.MaterialTheme as Material2Theme
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
fun NavGraphBuilder.iconPickerGraph(route: String) {
|
||||
preferenceGraph(route, {
|
||||
IconPickerPreference(packageName = "")
|
||||
@@ -162,11 +160,13 @@ fun IconPickerGrid(
|
||||
.catch { loadFailed = true }
|
||||
}
|
||||
val categories by categoriesFlow.collectAsState(emptyList())
|
||||
val filteredCategories by derivedStateOf {
|
||||
categories.asSequence()
|
||||
.map { it.filter(searchQuery) }
|
||||
.filter { it.items.isNotEmpty() }
|
||||
.toList()
|
||||
val filteredCategories by remember {
|
||||
derivedStateOf {
|
||||
categories.asSequence()
|
||||
.map { it.filter(searchQuery) }
|
||||
.filter { it.items.isNotEmpty() }
|
||||
.toList()
|
||||
}
|
||||
}
|
||||
|
||||
val density = LocalDensity.current
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package app.lawnchair.ui.preferences
|
||||
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
@@ -25,6 +24,8 @@ import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import app.lawnchair.backup.ui.createBackupGraph
|
||||
import app.lawnchair.backup.ui.restoreBackupGraph
|
||||
import app.lawnchair.ui.preferences.about.aboutGraph
|
||||
@@ -32,8 +33,6 @@ import app.lawnchair.ui.preferences.components.SystemUi
|
||||
import app.lawnchair.ui.preferences.components.colorpreference.colorSelectionGraph
|
||||
import app.lawnchair.ui.util.ProvideBottomSheetHandler
|
||||
import app.lawnchair.util.ProvideLifecycleState
|
||||
import com.google.accompanist.navigation.animation.AnimatedNavHost
|
||||
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
|
||||
import soup.compose.material.motion.animation.materialSharedAxisXIn
|
||||
import soup.compose.material.motion.animation.materialSharedAxisXOut
|
||||
import soup.compose.material.motion.animation.rememberSlideDistance
|
||||
@@ -68,10 +67,9 @@ val LocalPreferenceInteractor = staticCompositionLocalOf<PreferenceInteractor> {
|
||||
error("CompositionLocal LocalPreferenceInteractor not present")
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
fun Preferences(interactor: PreferenceInteractor = viewModel<PreferenceViewModel>()) {
|
||||
val navController = rememberAnimatedNavController()
|
||||
val navController = rememberNavController()
|
||||
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
|
||||
val slideDistance = rememberSlideDistance()
|
||||
|
||||
@@ -82,7 +80,7 @@ fun Preferences(interactor: PreferenceInteractor = viewModel<PreferenceViewModel
|
||||
LocalNavController provides navController,
|
||||
LocalPreferenceInteractor provides interactor,
|
||||
) {
|
||||
AnimatedNavHost(
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = "/",
|
||||
enterTransition = { materialSharedAxisXIn(!isRtl, slideDistance) },
|
||||
|
||||
@@ -3,13 +3,13 @@ package app.lawnchair.ui.preferences
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.pm.LauncherApps
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.navArgument
|
||||
import app.lawnchair.data.iconoverride.IconOverrideRepository
|
||||
import app.lawnchair.icons.IconPickerItem
|
||||
@@ -21,10 +21,8 @@ import app.lawnchair.ui.util.OnResult
|
||||
import app.lawnchair.util.requireSystemService
|
||||
import com.android.launcher3.R
|
||||
import com.android.launcher3.util.ComponentKey
|
||||
import com.google.accompanist.navigation.animation.composable
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
fun NavGraphBuilder.selectIconGraph(route: String) {
|
||||
preferenceGraph(route, { }) { subRoute ->
|
||||
composable(
|
||||
|
||||
@@ -34,9 +34,9 @@ import androidx.compose.ui.unit.dp
|
||||
import app.lawnchair.theme.surfaceColorAtElevation
|
||||
import app.lawnchair.ui.preferences.components.PreferenceTemplate
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import com.google.accompanist.placeholder.PlaceholderHighlight
|
||||
import com.google.accompanist.placeholder.material.fade
|
||||
import com.google.accompanist.placeholder.material.placeholder
|
||||
import app.lawnchair.ui.placeholder.PlaceholderHighlight
|
||||
import app.lawnchair.ui.placeholder.material.fade
|
||||
import app.lawnchair.ui.placeholder.material.placeholder
|
||||
|
||||
@Composable
|
||||
fun ContributorRow(name: String, description: String, photoUrl: String, url: String) {
|
||||
|
||||
+1
-3
@@ -17,7 +17,6 @@
|
||||
package app.lawnchair.ui.preferences.about.acknowledgements
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -35,6 +34,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.navArgument
|
||||
import app.lawnchair.ui.preferences.LocalNavController
|
||||
import app.lawnchair.ui.preferences.LocalPreferenceInteractor
|
||||
@@ -42,9 +42,7 @@ import app.lawnchair.ui.preferences.components.*
|
||||
import app.lawnchair.ui.preferences.preferenceGraph
|
||||
import app.lawnchair.ui.preferences.subRoute
|
||||
import com.android.launcher3.R
|
||||
import com.google.accompanist.navigation.animation.composable
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
fun NavGraphBuilder.licensesGraph(route: String) {
|
||||
preferenceGraph(route, { Acknowledgements() }) { subRoute ->
|
||||
composable(
|
||||
|
||||
@@ -24,12 +24,11 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.lawnchair.ui.placeholder.PlaceholderHighlight
|
||||
import app.lawnchair.ui.placeholder.material.fade
|
||||
import app.lawnchair.ui.placeholder.material.placeholder
|
||||
import app.lawnchair.util.App
|
||||
import com.google.accompanist.placeholder.PlaceholderHighlight
|
||||
import com.google.accompanist.placeholder.material.fade
|
||||
import com.google.accompanist.placeholder.material.placeholder
|
||||
|
||||
@Composable
|
||||
fun AppItem(
|
||||
|
||||
@@ -28,7 +28,6 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.lawnchair.ui.AlertBottomSheetContent
|
||||
import app.lawnchair.ui.util.bottomSheetHandler
|
||||
import com.android.launcher3.R
|
||||
|
||||
@Composable
|
||||
fun ClickablePreference(
|
||||
|
||||
@@ -3,6 +3,7 @@ package app.lawnchair.ui.preferences.components
|
||||
import android.content.Context
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -20,8 +21,8 @@ import app.lawnchair.ui.StretchEdgeEffect
|
||||
|
||||
@Composable
|
||||
fun NestedScrollStretch(content: @Composable () -> Unit) {
|
||||
val invalidateTick = remember { mutableStateOf(0) }
|
||||
val invalidate = Runnable { invalidateTick.value++ }
|
||||
val invalidateTick = remember { mutableIntStateOf(0) }
|
||||
val invalidate = Runnable { invalidateTick.intValue++ }
|
||||
|
||||
val context = LocalContext.current
|
||||
val connection = remember { NestedScrollStretchConnection(context, invalidate) }
|
||||
@@ -38,7 +39,7 @@ fun NestedScrollStretch(content: @Composable () -> Unit) {
|
||||
}
|
||||
.drawWithContent {
|
||||
// Redraw when this value changes
|
||||
invalidateTick.value
|
||||
invalidateTick.intValue
|
||||
|
||||
connection.topEdgeEffect.draw(tmpOut, StretchEdgeEffect.POSITION_TOP, this) {
|
||||
connection.bottomEdgeEffect.draw(tmpOut, StretchEdgeEffect.POSITION_BOTTOM, this) {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package app.lawnchair.ui.preferences.components
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.ContentAlpha
|
||||
import androidx.compose.material.MaterialTheme
|
||||
|
||||
@@ -25,9 +25,6 @@ import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@@ -19,9 +19,6 @@ package app.lawnchair.ui.preferences.components
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
|
||||
@@ -74,7 +74,7 @@ fun PreferenceSearchScaffold(
|
||||
}
|
||||
},
|
||||
bottomBar = { BottomSpacer() },
|
||||
contentPadding = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal).asPaddingValues()
|
||||
contentPadding = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal).asPaddingValues(),
|
||||
) {
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
innerPadding.left = it.calculateLeftPadding(layoutDirection)
|
||||
|
||||
@@ -31,6 +31,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
@@ -76,7 +77,7 @@ fun SliderPreference(
|
||||
showAsPercentage: Boolean = false,
|
||||
) {
|
||||
var adapterValue by adapter
|
||||
var sliderValue by remember { mutableStateOf(adapterValue) }
|
||||
var sliderValue by remember { mutableFloatStateOf(adapterValue) }
|
||||
|
||||
DisposableEffect(adapterValue) {
|
||||
sliderValue = adapterValue
|
||||
|
||||
+11
-11
@@ -2,7 +2,6 @@ package app.lawnchair.ui.preferences.components.colorpreference
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.layout.*
|
||||
@@ -27,13 +26,12 @@ import app.lawnchair.ui.preferences.components.colorpreference.pickers.PresetsLi
|
||||
import app.lawnchair.ui.preferences.components.colorpreference.pickers.SwatchGrid
|
||||
import app.lawnchair.ui.preferences.preferenceGraph
|
||||
import com.android.launcher3.R
|
||||
import com.google.accompanist.navigation.animation.composable
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.navigation.compose.composable
|
||||
import com.patrykmichalik.opto.domain.Preference
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
fun NavGraphBuilder.colorSelectionGraph(route: String) {
|
||||
preferenceGraph(route, {}) { subRoute ->
|
||||
composable(
|
||||
@@ -67,10 +65,10 @@ fun ColorSelection(
|
||||
val adapter = preference.getAdapter()
|
||||
val appliedColor = adapter.state.value
|
||||
val context = LocalContext.current
|
||||
val selectedColor = remember { mutableStateOf(appliedColor.forCustomPicker(context)) }
|
||||
val selectedColor = remember { mutableIntStateOf(appliedColor.forCustomPicker(context)) }
|
||||
val selectedColorApplied = remember {
|
||||
derivedStateOf {
|
||||
appliedColor is ColorOption.CustomColor && appliedColor.color == selectedColor.value
|
||||
appliedColor is ColorOption.CustomColor && appliedColor.color == selectedColor.intValue
|
||||
}
|
||||
}
|
||||
val defaultTabIndex = when {
|
||||
@@ -80,11 +78,14 @@ fun ColorSelection(
|
||||
}
|
||||
|
||||
val onPresetClick = { option: ColorOption ->
|
||||
selectedColor.value = option.forCustomPicker(context)
|
||||
selectedColor.intValue = option.forCustomPicker(context)
|
||||
adapter.onChange(newValue = option)
|
||||
}
|
||||
|
||||
val pagerState = rememberPagerState(initialPage = defaultTabIndex)
|
||||
val pagerState = rememberPagerState(
|
||||
initialPage = defaultTabIndex,
|
||||
pageCount = { 2 },
|
||||
)
|
||||
PreferenceLayout(
|
||||
label = label,
|
||||
bottomBar = {
|
||||
@@ -98,7 +99,7 @@ fun ColorSelection(
|
||||
) {
|
||||
Button(
|
||||
enabled = !selectedColorApplied.value,
|
||||
onClick = { adapter.onChange(newValue = ColorOption.CustomColor(selectedColor.value)) },
|
||||
onClick = { adapter.onChange(newValue = ColorOption.CustomColor(selectedColor.intValue)) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(all = 16.dp),
|
||||
@@ -132,7 +133,6 @@ fun ColorSelection(
|
||||
)
|
||||
}
|
||||
HorizontalPager(
|
||||
pageCount = 2,
|
||||
state = pagerState,
|
||||
verticalAlignment = Alignment.Top,
|
||||
modifier = Modifier.animateContentSize(),
|
||||
@@ -161,8 +161,8 @@ fun ColorSelection(
|
||||
}
|
||||
1 -> {
|
||||
CustomColorPicker(
|
||||
selectedColor = selectedColor.value,
|
||||
onSelect = { selectedColor.value = it },
|
||||
selectedColor = selectedColor.intValue,
|
||||
onSelect = { selectedColor.intValue = it },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+10
-8
@@ -108,7 +108,10 @@ fun CustomColorPicker(
|
||||
}
|
||||
}
|
||||
|
||||
val pagerState = rememberPagerState(0)
|
||||
val pagerState = rememberPagerState(
|
||||
initialPage = 0,
|
||||
pageCount = { 2 }
|
||||
)
|
||||
val scope = rememberCoroutineScope()
|
||||
val scrollToPage =
|
||||
{ page: Int -> scope.launch { pagerState.animateScrollToPage(page) } }
|
||||
@@ -142,7 +145,6 @@ fun CustomColorPicker(
|
||||
|
||||
HorizontalPager(
|
||||
modifier = Modifier.animateContentSize(),
|
||||
pageCount = 2,
|
||||
state = pagerState,
|
||||
verticalAlignment = Alignment.Top,
|
||||
) { page ->
|
||||
@@ -274,9 +276,9 @@ private fun HsvColorPicker(
|
||||
onSliderValuesChange: (Int) -> Unit,
|
||||
) {
|
||||
val hsv = remember { intColorToHsvColorArray(selectedColor) }
|
||||
var hue by remember { mutableStateOf(hsv[0]) }
|
||||
var saturation by remember { mutableStateOf(hsv[1]) }
|
||||
var brightness by remember { mutableStateOf(hsv[2]) }
|
||||
var hue by remember { mutableFloatStateOf(hsv[0]) }
|
||||
var saturation by remember { mutableFloatStateOf(hsv[1]) }
|
||||
var brightness by remember { mutableFloatStateOf(hsv[2]) }
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
fun updateColor(
|
||||
@@ -343,9 +345,9 @@ private fun RgbColorPicker(
|
||||
onSliderValuesChange: (Int) -> Unit,
|
||||
) {
|
||||
|
||||
var red by remember { mutableStateOf(selectedColor.red) }
|
||||
var green by remember { mutableStateOf(selectedColor.green) }
|
||||
var blue by remember { mutableStateOf(selectedColor.blue) }
|
||||
var red by remember { mutableIntStateOf(selectedColor.red) }
|
||||
var green by remember { mutableIntStateOf(selectedColor.green) }
|
||||
var blue by remember { mutableIntStateOf(selectedColor.blue) }
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
fun updateColor(
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package app.lawnchair.ui.preferences
|
||||
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import com.google.accompanist.navigation.animation.composable
|
||||
import androidx.navigation.compose.composable
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
inline fun NavGraphBuilder.preferenceGraph(
|
||||
route: String,
|
||||
crossinline root: @Composable () -> Unit,
|
||||
|
||||
@@ -2,7 +2,7 @@ package app.lawnchair.ui.util
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.unit.Density
|
||||
@@ -13,7 +13,7 @@ class LazyGridLayout(
|
||||
private val gapWidth: Dp,
|
||||
private val density: Density
|
||||
) {
|
||||
private val _numColumns = mutableStateOf(0)
|
||||
private val _numColumns = mutableIntStateOf(0)
|
||||
val numColumns: State<Int> = _numColumns
|
||||
|
||||
@Composable
|
||||
@@ -23,7 +23,7 @@ class LazyGridLayout(
|
||||
val gapWidth = gapWidth.roundToPx()
|
||||
val availableWidth = (it.width - minWidth).coerceAtLeast(0)
|
||||
val additionalCols = availableWidth / (minWidth + gapWidth)
|
||||
_numColumns.value = 1 + additionalCols
|
||||
_numColumns.intValue = 1 + additionalCols
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,9 +52,11 @@ fun ProvideBottomSheetHandler(
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
var onDismiss by remember { mutableStateOf({}) }
|
||||
val density = LocalDensity.current
|
||||
val bottomSheetState = remember {
|
||||
ModalBottomSheetState(
|
||||
initialValue = ModalBottomSheetValue.Hidden,
|
||||
density = density,
|
||||
confirmValueChange = {
|
||||
if (it == ModalBottomSheetValue.Hidden) onDismiss()
|
||||
true
|
||||
|
||||
@@ -2,7 +2,9 @@ package app.lawnchair.ui.util
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ProvidableCompositionLocal
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@@ -16,6 +16,7 @@ import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
@@ -41,8 +42,8 @@ class ComposeBottomSheet<T>(context: Context)
|
||||
|
||||
private val container = ComposeView(context)
|
||||
private var imeShift = 0f
|
||||
private var _hintCloseProgress = mutableStateOf(0f)
|
||||
private val hintCloseProgress get() = _hintCloseProgress.value
|
||||
private var _hintCloseProgress = mutableFloatStateOf(0f)
|
||||
private val hintCloseProgress get() = _hintCloseProgress.floatValue
|
||||
private var hintCloseDistance = 0f
|
||||
|
||||
init {
|
||||
@@ -224,10 +225,10 @@ class ComposeBottomSheet<T>(context: Context)
|
||||
private val HINT_CLOSE_PROGRESS = object : FloatProperty<ComposeBottomSheet<*>>("hintCloseProgress") {
|
||||
|
||||
override fun setValue(view: ComposeBottomSheet<*>, value: Float) {
|
||||
view._hintCloseProgress.value = value
|
||||
view._hintCloseProgress.floatValue = value
|
||||
}
|
||||
|
||||
override fun get(view: ComposeBottomSheet<*>) = view._hintCloseProgress.value
|
||||
override fun get(view: ComposeBottomSheet<*>) = view._hintCloseProgress.floatValue
|
||||
}
|
||||
|
||||
fun <T> show(
|
||||
|
||||
Reference in New Issue
Block a user