Skip to content

Commit

Permalink
Update project
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelbel committed Nov 9, 2024
1 parent 3d478b8 commit 79886ff
Show file tree
Hide file tree
Showing 12 changed files with 595 additions and 18 deletions.
7 changes: 3 additions & 4 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import org.apache.commons.io.output.ByteArrayOutputStream
import java.io.FileInputStream
import java.nio.charset.Charset
Expand All @@ -20,9 +19,7 @@ private val gitCommitsCount: Int by lazy {
}

kotlin {
compilerOptions {
jvmToolchain(libs.versions.jdk.get().toInt())
}
jvmToolchain(libs.versions.jdk.get().toInt())
}

android {
Expand Down Expand Up @@ -75,12 +72,14 @@ android {
release {
isDebuggable = false
isMinifyEnabled = true
isShrinkResources = true
signingConfig = if (signingConfigs.findByName("release") != null) signingConfigs.getByName("release") else null
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
debug {
isDebuggable = true
isMinifyEnabled = false
isShrinkResources = false
applicationIdSuffix = ".debug"
}
}
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
</intent-filter>
</activity>

<profileable
android:shell="true"
android:enabled="true" />

</application>

</manifest>
3 changes: 0 additions & 3 deletions app/src/main/kotlin/org/michaelbel/template/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package org.michaelbel.template

import android.os.Bundle
import android.util.Log
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import org.michaelbel.core.ktx.versionCode

class MainActivity: AppCompatActivity() {

Expand All @@ -17,6 +15,5 @@ class MainActivity: AppCompatActivity() {
setContent {
MainActivityContent()
}
Log.e("2", "versionCode=$versionCode")
}
}
10 changes: 9 additions & 1 deletion core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,24 @@ kotlin {

android {
namespace = "org.michaelbel.template.core" // Replace with your own namespace
compileSdk = libs.versions.compile.sdk.get().toInt()

defaultConfig {
compileSdk = libs.versions.compile.sdk.get().toInt()
minSdk = libs.versions.min.sdk.get().toInt()
}

buildFeatures {
resValues = true
shaders = false
aidl = false
renderScript = false
buildConfig = true
compose = true
}

kotlinOptions {
allWarningsAsErrors = false
}
}

dependencies {
Expand Down
245 changes: 245 additions & 0 deletions core/src/main/kotlin/org/michaelbel/core/placeholder/Placeholder.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
package org.michaelbel.core.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].
*/
internal 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()
}
Loading

0 comments on commit 79886ff

Please sign in to comment.