Skip to content

Commit 79886ff

Browse files
committed
Update project
1 parent 3d478b8 commit 79886ff

File tree

12 files changed

+595
-18
lines changed

12 files changed

+595
-18
lines changed

app/build.gradle.kts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
import org.apache.commons.io.output.ByteArrayOutputStream
32
import java.io.FileInputStream
43
import java.nio.charset.Charset
@@ -20,9 +19,7 @@ private val gitCommitsCount: Int by lazy {
2019
}
2120

2221
kotlin {
23-
compilerOptions {
24-
jvmToolchain(libs.versions.jdk.get().toInt())
25-
}
22+
jvmToolchain(libs.versions.jdk.get().toInt())
2623
}
2724

2825
android {
@@ -75,12 +72,14 @@ android {
7572
release {
7673
isDebuggable = false
7774
isMinifyEnabled = true
75+
isShrinkResources = true
7876
signingConfig = if (signingConfigs.findByName("release") != null) signingConfigs.getByName("release") else null
7977
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
8078
}
8179
debug {
8280
isDebuggable = true
8381
isMinifyEnabled = false
82+
isShrinkResources = false
8483
applicationIdSuffix = ".debug"
8584
}
8685
}

app/src/main/AndroidManifest.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
</intent-filter>
2424
</activity>
2525

26+
<profileable
27+
android:shell="true"
28+
android:enabled="true" />
29+
2630
</application>
2731

2832
</manifest>
Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
package org.michaelbel.template
22

33
import android.os.Bundle
4-
import android.util.Log
54
import androidx.activity.compose.setContent
65
import androidx.activity.enableEdgeToEdge
76
import androidx.appcompat.app.AppCompatActivity
87
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
9-
import org.michaelbel.core.ktx.versionCode
108

119
class MainActivity: AppCompatActivity() {
1210

@@ -17,6 +15,5 @@ class MainActivity: AppCompatActivity() {
1715
setContent {
1816
MainActivityContent()
1917
}
20-
Log.e("2", "versionCode=$versionCode")
2118
}
2219
}

core/build.gradle.kts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,24 @@ kotlin {
1515

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

1920
defaultConfig {
20-
compileSdk = libs.versions.compile.sdk.get().toInt()
2121
minSdk = libs.versions.min.sdk.get().toInt()
2222
}
2323

2424
buildFeatures {
25+
resValues = true
26+
shaders = false
27+
aidl = false
28+
renderScript = false
2529
buildConfig = true
2630
compose = true
2731
}
32+
33+
kotlinOptions {
34+
allWarningsAsErrors = false
35+
}
2836
}
2937

3038
dependencies {
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
package org.michaelbel.core.placeholder
2+
3+
import androidx.compose.animation.core.FiniteAnimationSpec
4+
import androidx.compose.animation.core.InfiniteRepeatableSpec
5+
import androidx.compose.animation.core.MutableTransitionState
6+
import androidx.compose.animation.core.RepeatMode
7+
import androidx.compose.animation.core.Transition
8+
import androidx.compose.animation.core.animateFloat
9+
import androidx.compose.animation.core.infiniteRepeatable
10+
import androidx.compose.animation.core.rememberInfiniteTransition
11+
import androidx.compose.animation.core.spring
12+
import androidx.compose.animation.core.tween
13+
import androidx.compose.animation.core.updateTransition
14+
import androidx.compose.runtime.Composable
15+
import androidx.compose.runtime.getValue
16+
import androidx.compose.runtime.mutableFloatStateOf
17+
import androidx.compose.runtime.remember
18+
import androidx.compose.runtime.setValue
19+
import androidx.compose.ui.Modifier
20+
import androidx.compose.ui.composed
21+
import androidx.compose.ui.draw.drawWithContent
22+
import androidx.compose.ui.geometry.Size
23+
import androidx.compose.ui.geometry.toRect
24+
import androidx.compose.ui.graphics.Color
25+
import androidx.compose.ui.graphics.Outline
26+
import androidx.compose.ui.graphics.Paint
27+
import androidx.compose.ui.graphics.RectangleShape
28+
import androidx.compose.ui.graphics.Shape
29+
import androidx.compose.ui.graphics.drawOutline
30+
import androidx.compose.ui.graphics.drawscope.DrawScope
31+
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
32+
import androidx.compose.ui.node.Ref
33+
import androidx.compose.ui.platform.debugInspectorInfo
34+
import androidx.compose.ui.unit.LayoutDirection
35+
36+
/**
37+
* Contains default values used by [Modifier.placeholder] and [PlaceholderHighlight].
38+
*/
39+
internal object PlaceholderDefaults {
40+
/**
41+
* The default [InfiniteRepeatableSpec] to use for [fade].
42+
*/
43+
val fadeAnimationSpec: InfiniteRepeatableSpec<Float> by lazy {
44+
infiniteRepeatable(
45+
animation = tween(delayMillis = 200, durationMillis = 600),
46+
repeatMode = RepeatMode.Reverse,
47+
)
48+
}
49+
50+
/**
51+
* The default [InfiniteRepeatableSpec] to use for [shimmer].
52+
*/
53+
val shimmerAnimationSpec: InfiniteRepeatableSpec<Float> by lazy {
54+
infiniteRepeatable(
55+
animation = tween(durationMillis = 1700, delayMillis = 200),
56+
repeatMode = RepeatMode.Restart
57+
)
58+
}
59+
}
60+
61+
/**
62+
* Draws some skeleton UI which is typically used whilst content is 'loading'.
63+
*
64+
* A version of this modifier which uses appropriate values for Material themed apps is available
65+
* in the 'Placeholder Material' library.
66+
*
67+
* You can provide a [PlaceholderHighlight] which runs an highlight animation on the placeholder.
68+
* The [shimmer] and [fade] implementations are provided for easy usage.
69+
*
70+
* A cross-fade transition will be applied to the content and placeholder UI when the [visible]
71+
* value changes. The transition can be customized via the [contentFadeTransitionSpec] and
72+
* [placeholderFadeTransitionSpec] parameters.
73+
*
74+
* You can find more information on the pattern at the Material Theming
75+
* [Placeholder UI](https://material.io/design/communication/launch-screen.html#placeholder-ui)
76+
* guidelines.
77+
*
78+
* @param visible whether the placeholder should be visible or not.
79+
* @param color the color used to draw the placeholder UI.
80+
* @param shape desired shape of the placeholder. Defaults to [RectangleShape].
81+
* @param highlight optional highlight animation.
82+
* @param placeholderFadeTransitionSpec The transition spec to use when fading the placeholder
83+
* on/off screen. The boolean parameter defined for the transition is [visible].
84+
* @param contentFadeTransitionSpec The transition spec to use when fading the content
85+
* on/off screen. The boolean parameter defined for the transition is [visible].
86+
*/
87+
fun Modifier.placeholder(
88+
visible: Boolean,
89+
color: Color,
90+
shape: Shape = RectangleShape,
91+
highlight: PlaceholderHighlight? = null,
92+
placeholderFadeTransitionSpec: @Composable Transition.Segment<Boolean>.() -> FiniteAnimationSpec<Float> = { spring() },
93+
contentFadeTransitionSpec: @Composable Transition.Segment<Boolean>.() -> FiniteAnimationSpec<Float> = { spring() }
94+
): Modifier = composed(
95+
inspectorInfo = debugInspectorInfo {
96+
name = "placeholder"
97+
value = visible
98+
properties["visible"] = visible
99+
properties["color"] = color
100+
properties["highlight"] = highlight
101+
properties["shape"] = shape
102+
}
103+
) {
104+
// Values used for caching purposes
105+
val lastSize = remember { Ref<Size>() }
106+
val lastLayoutDirection = remember { Ref<LayoutDirection>() }
107+
val lastOutline = remember { Ref<Outline>() }
108+
109+
// The current highlight animation progress
110+
var highlightProgress: Float by remember { mutableFloatStateOf(0F) }
111+
112+
// This is our crossfade transition
113+
val transitionState = remember { MutableTransitionState(visible) }.apply {
114+
targetState = visible
115+
}
116+
val transition = updateTransition(transitionState, "placeholder_crossfade")
117+
118+
val placeholderAlpha by transition.animateFloat(
119+
transitionSpec = placeholderFadeTransitionSpec,
120+
label = "placeholder_fade",
121+
targetValueByState = { placeholderVisible -> if (placeholderVisible) 1F else 0F }
122+
)
123+
val contentAlpha by transition.animateFloat(
124+
transitionSpec = contentFadeTransitionSpec,
125+
label = "content_fade",
126+
targetValueByState = { placeholderVisible -> if (placeholderVisible) 0F else 1F }
127+
)
128+
129+
// Run the optional animation spec and update the progress if the placeholder is visible
130+
val animationSpec = highlight?.animationSpec
131+
if (animationSpec != null && (visible || placeholderAlpha >= 0.01F)) {
132+
val infiniteTransition = rememberInfiniteTransition(label = "")
133+
highlightProgress = infiniteTransition.animateFloat(
134+
initialValue = 0F,
135+
targetValue = 1F,
136+
animationSpec = animationSpec,
137+
label = ""
138+
).value
139+
}
140+
141+
val paint = remember { Paint() }
142+
remember(color, shape, highlight) {
143+
drawWithContent {
144+
// Draw the composable content first
145+
if (contentAlpha in 0.01F..0.99F) {
146+
// If the content alpha is between 1% and 99%, draw it in a layer with
147+
// the alpha applied
148+
paint.alpha = contentAlpha
149+
withLayer(paint) {
150+
with(this@drawWithContent) {
151+
drawContent()
152+
}
153+
}
154+
} else if (contentAlpha >= 0.99F) {
155+
// If the content alpha is > 99%, draw it with no alpha
156+
drawContent()
157+
}
158+
159+
if (placeholderAlpha in 0.01F..0.99F) {
160+
// If the placeholder alpha is between 1% and 99%, draw it in a layer with
161+
// the alpha applied
162+
paint.alpha = placeholderAlpha
163+
withLayer(paint) {
164+
lastOutline.value = drawPlaceholder(
165+
shape = shape,
166+
color = color,
167+
highlight = highlight,
168+
progress = highlightProgress,
169+
lastOutline = lastOutline.value,
170+
lastLayoutDirection = lastLayoutDirection.value,
171+
lastSize = lastSize.value,
172+
)
173+
}
174+
} else if (placeholderAlpha >= 0.99F) {
175+
// If the placeholder alpha is > 99%, draw it with no alpha
176+
lastOutline.value = drawPlaceholder(
177+
shape = shape,
178+
color = color,
179+
highlight = highlight,
180+
progress = highlightProgress,
181+
lastOutline = lastOutline.value,
182+
lastLayoutDirection = lastLayoutDirection.value,
183+
lastSize = lastSize.value,
184+
)
185+
}
186+
187+
// Keep track of the last size & layout direction
188+
lastSize.value = size
189+
lastLayoutDirection.value = layoutDirection
190+
}
191+
}
192+
}
193+
194+
private fun DrawScope.drawPlaceholder(
195+
shape: Shape,
196+
color: Color,
197+
highlight: PlaceholderHighlight?,
198+
progress: Float,
199+
lastOutline: Outline?,
200+
lastLayoutDirection: LayoutDirection?,
201+
lastSize: Size?,
202+
): Outline? {
203+
// shortcut to avoid Outline calculation and allocation
204+
if (shape === RectangleShape) {
205+
// Draw the initial background color
206+
drawRect(color = color)
207+
208+
if (highlight != null) {
209+
drawRect(
210+
brush = highlight.brush(progress, size),
211+
alpha = highlight.alpha(progress),
212+
)
213+
}
214+
// We didn't create an outline so return null
215+
return null
216+
}
217+
218+
// Otherwise we need to create an outline from the shape
219+
val outline = lastOutline.takeIf {
220+
size == lastSize && layoutDirection == lastLayoutDirection
221+
} ?: shape.createOutline(size, layoutDirection, this)
222+
223+
// Draw the placeholder color
224+
drawOutline(outline = outline, color = color)
225+
226+
if (highlight != null) {
227+
drawOutline(
228+
outline = outline,
229+
brush = highlight.brush(progress, size),
230+
alpha = highlight.alpha(progress),
231+
)
232+
}
233+
234+
// Return the outline we used
235+
return outline
236+
}
237+
238+
private inline fun DrawScope.withLayer(
239+
paint: Paint,
240+
drawBlock: DrawScope.() -> Unit,
241+
) = drawIntoCanvas { canvas ->
242+
canvas.saveLayer(size.toRect(), paint)
243+
drawBlock()
244+
canvas.restore()
245+
}

0 commit comments

Comments
 (0)