Skip to content

Commit 54aad13

Browse files
Merge branch 'main' into jv/broadcast_receiver
2 parents 759e5e6 + f595a0d commit 54aad13

File tree

8 files changed

+293
-28
lines changed

8 files changed

+293
-28
lines changed

.github/workflows/apply_spotless.yml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,13 @@ jobs:
4444
- name: Run spotlessApply
4545
run: ./gradlew :compose:spotlessApply --init-script gradle/init.gradle.kts --no-configuration-cache --stacktrace
4646

47+
- name: Run spotlessApply for Wear
48+
run: ./gradlew :wear:spotlessApply --init-script gradle/init.gradle.kts --no-configuration-cache --stacktrace
49+
50+
- name: Run spotlessApply for Misc
51+
run: ./gradlew :misc:spotlessApply --init-script gradle/init.gradle.kts --no-configuration-cache --stacktrace
52+
4753
- name: Auto-commit if spotlessApply has changes
4854
uses: stefanzweifel/git-auto-commit-action@v5
4955
with:
50-
commit_message: Apply Spotless
51-
52-
- name: Run spotlessApply for Wear
53-
run: ./gradlew :wear:spotlessApply --init-script gradle/init.gradle.kts --no-configuration-cache --stacktrace
56+
commit_message: Apply Spotless
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright 2024 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.compose.snippets.images
18+
19+
import androidx.compose.foundation.Image
20+
import androidx.compose.foundation.layout.Box
21+
import androidx.compose.foundation.layout.fillMaxWidth
22+
import androidx.compose.foundation.layout.offset
23+
import androidx.compose.foundation.layout.padding
24+
import androidx.compose.foundation.layout.size
25+
import androidx.compose.foundation.lazy.LazyColumn
26+
import androidx.compose.material3.MaterialTheme
27+
import androidx.compose.material3.Text
28+
import androidx.compose.runtime.Composable
29+
import androidx.compose.runtime.getValue
30+
import androidx.compose.runtime.mutableFloatStateOf
31+
import androidx.compose.runtime.mutableStateOf
32+
import androidx.compose.runtime.remember
33+
import androidx.compose.runtime.setValue
34+
import androidx.compose.ui.Alignment
35+
import androidx.compose.ui.Modifier
36+
import androidx.compose.ui.geometry.Offset
37+
import androidx.compose.ui.graphics.Color
38+
import androidx.compose.ui.graphics.graphicsLayer
39+
import androidx.compose.ui.graphics.painter.ColorPainter
40+
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
41+
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
42+
import androidx.compose.ui.input.nestedscroll.nestedScroll
43+
import androidx.compose.ui.tooling.preview.Preview
44+
import androidx.compose.ui.unit.Dp
45+
import androidx.compose.ui.unit.IntOffset
46+
import androidx.compose.ui.unit.dp
47+
48+
// [START android_compose_images_imageresizeonscrollexample]
49+
@Composable
50+
fun ImageResizeOnScrollExample(
51+
modifier: Modifier = Modifier,
52+
maxImageSize: Dp = 300.dp,
53+
minImageSize: Dp = 100.dp
54+
) {
55+
var currentImageSize by remember { mutableStateOf(maxImageSize) }
56+
var imageScale by remember { mutableFloatStateOf(1f) }
57+
58+
val nestedScrollConnection = remember {
59+
object : NestedScrollConnection {
60+
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
61+
// Calculate the change in image size based on scroll delta
62+
val delta = available.y
63+
val newImageSize = currentImageSize + delta.dp
64+
val previousImageSize = currentImageSize
65+
66+
// Constrain the image size within the allowed bounds
67+
currentImageSize = newImageSize.coerceIn(minImageSize, maxImageSize)
68+
val consumed = currentImageSize - previousImageSize
69+
70+
// Calculate the scale for the image
71+
imageScale = currentImageSize / maxImageSize
72+
73+
// Return the consumed scroll amount
74+
return Offset(0f, consumed.value)
75+
}
76+
}
77+
}
78+
79+
Box(Modifier.nestedScroll(nestedScrollConnection)) {
80+
LazyColumn(
81+
Modifier
82+
.fillMaxWidth()
83+
.padding(15.dp)
84+
.offset {
85+
IntOffset(0, currentImageSize.roundToPx())
86+
}
87+
) {
88+
// Placeholder list items
89+
items(100, key = { it }) {
90+
Text(
91+
text = "Item: $it",
92+
style = MaterialTheme.typography.bodyLarge
93+
)
94+
}
95+
}
96+
97+
Image(
98+
painter = ColorPainter(Color.Red),
99+
contentDescription = "Red color image",
100+
Modifier
101+
.size(maxImageSize)
102+
.align(Alignment.TopCenter)
103+
.graphicsLayer {
104+
scaleX = imageScale
105+
scaleY = imageScale
106+
// Center the image vertically as it scales
107+
translationY = -(maxImageSize.toPx() - currentImageSize.toPx()) / 2f
108+
}
109+
)
110+
}
111+
}
112+
// [END android_compose_images_imageresizeonscrollexample]
113+
114+
@Preview(showBackground = true)
115+
@Composable
116+
private fun ImageSizeOnScrollScreenPreview() {
117+
ImageResizeOnScrollExample()
118+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2024 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.compose.snippets.text
18+
19+
import androidx.compose.material3.Text
20+
import androidx.compose.runtime.Composable
21+
import androidx.compose.ui.Modifier
22+
import androidx.compose.ui.graphics.Color
23+
import androidx.compose.ui.text.AnnotatedString
24+
import androidx.compose.ui.text.SpanStyle
25+
import androidx.compose.ui.text.TextLinkStyles
26+
import androidx.compose.ui.text.font.FontStyle
27+
import androidx.compose.ui.text.fromHtml
28+
import androidx.compose.ui.text.style.TextDecoration
29+
import androidx.compose.ui.tooling.preview.Preview
30+
31+
// [START android_compose_text_annotatedhtmlstringwithlink]
32+
@Composable
33+
fun AnnotatedHtmlStringWithLink(
34+
modifier: Modifier = Modifier,
35+
htmlText: String = """
36+
<h1>Jetpack Compose</h1>
37+
<p>
38+
Build <b>better apps</b> faster with <a href="https://www.android.com">Jetpack Compose</a>
39+
</p>
40+
""".trimIndent()
41+
) {
42+
Text(
43+
AnnotatedString.fromHtml(
44+
htmlText,
45+
linkStyles = TextLinkStyles(
46+
style = SpanStyle(
47+
textDecoration = TextDecoration.Underline,
48+
fontStyle = FontStyle.Italic,
49+
color = Color.Blue
50+
)
51+
)
52+
),
53+
modifier
54+
)
55+
}
56+
// [END android_compose_text_annotatedhtmlstringwithlink]
57+
58+
@Preview(showBackground = true)
59+
@Composable
60+
private fun AnnotatedHtmlStringWithLinkPreview() {
61+
AnnotatedHtmlStringWithLink()
62+
}

gradle/libs.versions.toml

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
[versions]
22
accompanist = "0.36.0"
3-
androidGradlePlugin = "8.7.0"
4-
androidx-activity-compose = "1.9.2"
3+
androidGradlePlugin = "8.7.2"
4+
androidx-activity-compose = "1.9.3"
55
androidx-appcompat = "1.7.0"
6-
androidx-compose-bom = "2024.09.03"
6+
androidx-compose-bom = "2024.10.01"
77
androidx-compose-ui-test = "1.7.0-alpha08"
8-
androidx-constraintlayout = "2.1.4"
9-
androidx-constraintlayout-compose = "1.0.1"
8+
androidx-constraintlayout = "2.2.0"
9+
androidx-constraintlayout-compose = "1.1.0"
1010
androidx-coordinator-layout = "1.2.0"
11-
androidx-corektx = "1.13.1"
11+
androidx-corektx = "1.15.0"
1212
androidx-emoji2-views = "1.5.0"
13-
androidx-fragment-ktx = "1.8.4"
13+
androidx-fragment-ktx = "1.8.5"
1414
androidx-glance-appwidget = "1.1.1"
15-
androidx-lifecycle-compose = "2.8.6"
16-
androidx-lifecycle-runtime-compose = "2.8.6"
17-
androidx-navigation = "2.8.2"
15+
androidx-lifecycle-compose = "2.8.7"
16+
androidx-lifecycle-runtime-compose = "2.8.7"
17+
androidx-navigation = "2.8.3"
1818
androidx-paging = "3.3.2"
1919
androidx-test = "1.6.1"
2020
androidx-test-espresso = "3.6.1"
2121
androidx-window = "1.3.0"
2222
androidxHiltNavigationCompose = "1.2.0"
2323
coil = "2.7.0"
2424
# @keep
25-
compileSdk = "34"
26-
compose-latest = "1.7.3"
25+
compileSdk = "35"
26+
compose-latest = "1.7.5"
2727
composeUiTooling = "1.4.0"
2828
coreSplashscreen = "1.0.1"
2929
coroutines = "1.9.0"
@@ -33,21 +33,21 @@ gradle-versions = "0.51.0"
3333
hilt = "2.52"
3434
horologist = "0.6.20"
3535
junit = "4.13.2"
36-
kotlin = "2.0.20"
36+
kotlin = "2.0.21"
3737
kotlinxSerializationJson = "1.7.3"
38-
ksp = "2.0.20-1.0.25"
39-
maps-compose = "6.1.2"
40-
material = "1.13.0-alpha06"
38+
ksp = "2.0.21-1.0.26"
39+
maps-compose = "6.2.0"
40+
material = "1.13.0-alpha07"
4141
material3-adaptive = "1.0.0"
42-
material3-adaptive-navigation-suite = "1.3.0"
42+
material3-adaptive-navigation-suite = "1.3.1"
4343
media3 = "1.4.1"
4444
# @keep
4545
minSdk = "21"
4646
playServicesWearable = "18.2.0"
4747
recyclerview = "1.3.2"
4848
# @keep
4949
targetSdk = "34"
50-
version-catalog-update = "0.8.4"
50+
version-catalog-update = "0.8.5"
5151
wearComposeFoundation = "1.4.0"
5252
wearComposeMaterial = "1.4.0"
5353

@@ -108,7 +108,7 @@ androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-te
108108
androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-test-espresso" }
109109
androidx-test-runner = "androidx.test:runner:1.6.2"
110110
androidx-window-core = { module = "androidx.window:window-core", version.ref = "androidx-window" }
111-
androidx-work-runtime-ktx = "androidx.work:work-runtime-ktx:2.9.1"
111+
androidx-work-runtime-ktx = "androidx.work:work-runtime-ktx:2.10.0"
112112
coil-kt-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
113113
compose-foundation = { module = "androidx.wear.compose:compose-foundation", version.ref = "wearComposeFoundation" }
114114
compose-material = { module = "androidx.wear.compose:compose-material", version.ref = "wearComposeMaterial" }

misc/src/main/java/com/example/snippets/BroadcastReceiverJavaSnippets.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import android.content.Context;
55
import android.content.Intent;
66
import android.content.IntentFilter;
7+
import android.os.Bundle;
78

9+
import androidx.activity.ComponentActivity;
810
import androidx.core.content.ContextCompat;
911

1012
import java.util.Objects;
@@ -94,5 +96,25 @@ public void sendBroadcast(String newData, boolean usePermission) {
9496
}
9597
}
9698
}
97-
}
9899

100+
/** @noinspection InnerClassMayBeStatic*/
101+
// [START android_broadcast_receiver_13_activity_java]
102+
class MyActivity extends ComponentActivity {
103+
MyBroadcastReceiver myBroadcastReceiver;
104+
105+
@Override
106+
protected void onCreate(Bundle savedInstanceState) {
107+
super.onCreate(savedInstanceState);
108+
// [START_EXCLUDE]
109+
IntentFilter filter = new IntentFilter("com.example.snippets.ACTION_UPDATE_DATA");
110+
boolean listenToBroadcastsFromOtherApps = false;
111+
int receiverFlags = listenToBroadcastsFromOtherApps
112+
? ContextCompat.RECEIVER_EXPORTED
113+
: ContextCompat.RECEIVER_NOT_EXPORTED;
114+
// [END_EXCLUDE]
115+
ContextCompat.registerReceiver(this, myBroadcastReceiver, filter, receiverFlags);
116+
// Set content
117+
}
118+
}
119+
// [END android_broadcast_receiver_13_activity_java]
120+
}

misc/src/main/java/com/example/snippets/BroadcastReceiverSnippets.kt

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import android.content.BroadcastReceiver
44
import android.content.Context
55
import android.content.Intent
66
import android.content.IntentFilter
7+
import android.os.Bundle
8+
import androidx.activity.ComponentActivity
9+
import androidx.activity.compose.setContent
710
import androidx.compose.foundation.layout.Arrangement
811
import androidx.compose.foundation.layout.Column
912
import androidx.compose.foundation.layout.Row
@@ -24,6 +27,7 @@ import androidx.compose.runtime.remember
2427
import androidx.compose.runtime.rememberUpdatedState
2528
import androidx.compose.runtime.setValue
2629
import androidx.compose.ui.Modifier
30+
import androidx.compose.ui.platform.LocalContext
2731
import androidx.compose.ui.unit.dp
2832
import androidx.core.content.ContextCompat
2933
import androidx.hilt.navigation.compose.hiltViewModel
@@ -190,3 +194,62 @@ class MyBroadcastReceiverWithPermission : BroadcastReceiver() {
190194
// no-op, only used to demonstrate manifest registration of receiver with permission
191195
}
192196
}
197+
198+
// Ignore following code - it's only used to demonstrate best practices, not part of the sample
199+
@Suppress("unused")
200+
// [START android_broadcast_receiver_13_activity]
201+
class MyActivity : ComponentActivity() {
202+
private val myBroadcastReceiver = MyBroadcastReceiver()
203+
204+
override fun onCreate(savedInstanceState: Bundle?) {
205+
super.onCreate(savedInstanceState)
206+
// [START_EXCLUDE]
207+
val filter = IntentFilter("com.example.snippets.ACTION_UPDATE_DATA")
208+
val listenToBroadcastsFromOtherApps = false
209+
val receiverFlags = if (listenToBroadcastsFromOtherApps) {
210+
ContextCompat.RECEIVER_EXPORTED
211+
} else {
212+
ContextCompat.RECEIVER_NOT_EXPORTED
213+
}
214+
// [END_EXCLUDE]
215+
ContextCompat.registerReceiver(this, myBroadcastReceiver, filter, receiverFlags)
216+
setContent { MyApp() }
217+
}
218+
219+
override fun onDestroy() {
220+
super.onDestroy()
221+
// When you forget to unregister your receiver here, you're causing a leak!
222+
this.unregisterReceiver(myBroadcastReceiver)
223+
}
224+
}
225+
// [END android_broadcast_receiver_13_activity]
226+
227+
@Composable
228+
fun MyApp() {}
229+
230+
@Suppress("unused")
231+
// [START android_broadcast_receiver_14_stateless]
232+
@Composable
233+
fun MyStatefulScreen() {
234+
val myBroadcastReceiver = remember { MyBroadcastReceiver()}
235+
val context = LocalContext.current
236+
LifecycleStartEffect(true) {
237+
// [START_EXCLUDE]
238+
val filter = IntentFilter("com.example.snippets.ACTION_UPDATE_DATA")
239+
val listenToBroadcastsFromOtherApps = false
240+
val flags = if (listenToBroadcastsFromOtherApps) {
241+
ContextCompat.RECEIVER_EXPORTED
242+
} else {
243+
ContextCompat.RECEIVER_NOT_EXPORTED
244+
}
245+
// [END_EXCLUDE]
246+
ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, flags)
247+
onStopOrDispose { context.unregisterReceiver(myBroadcastReceiver) }
248+
}
249+
MyStatelessScreen()
250+
}
251+
252+
@Composable
253+
fun MyStatelessScreen() {
254+
// Implement your screen
255+
}

0 commit comments

Comments
 (0)