> = _filteredItems
+
+ fun filterText(input: String) {
+ // This filter returns the full items list when input is an empty string.
+ _filteredItems.value = items.filter { it.contains(input, ignoreCase = true) }
+ }
+}
+// [END android_compose_text_filtertextviewmodel]
+
+// [START android_compose_text_filtertextview]
+@Composable
+fun FilterTextView(modifier: Modifier = Modifier, viewModel: FilterTextViewModel = viewModel()) {
+ val filteredItems by viewModel.filteredItems.collectAsStateWithLifecycle()
+ var text by rememberSaveable { mutableStateOf("") }
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(all = 10.dp)
+ ) {
+ OutlinedTextField(
+ value = text,
+ onValueChange = {
+ text = it
+ viewModel.filterText(text)
+ },
+ label = { Text("Filter Text") },
+ modifier = Modifier.fillMaxWidth()
+ )
+
+ LazyColumn {
+ items(
+ count = filteredItems.size,
+ key = { index -> filteredItems[index] }
+ ) {
+ ListItem(
+ headlineContent = { Text(filteredItems[it]) },
+ modifier = Modifier
+ .fillParentMaxWidth()
+ .padding(10.dp)
+ )
+ }
+ }
+ }
+}
+// [END android_compose_text_filtertextview]
+
+@Preview(showBackground = true)
+@Composable
+private fun FilterTextViewPreview() {
+ FilterTextView()
+}
diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/text/HtmlStyling.kt b/compose/snippets/src/main/java/com/example/compose/snippets/text/HtmlStyling.kt
new file mode 100644
index 000000000..607fdf462
--- /dev/null
+++ b/compose/snippets/src/main/java/com/example/compose/snippets/text/HtmlStyling.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2024 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 com.example.compose.snippets.text
+
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.TextLinkStyles
+import androidx.compose.ui.text.font.FontStyle
+import androidx.compose.ui.text.fromHtml
+import androidx.compose.ui.text.style.TextDecoration
+import androidx.compose.ui.tooling.preview.Preview
+
+// [START android_compose_text_annotatedhtmlstringwithlink]
+@Composable
+fun AnnotatedHtmlStringWithLink(
+ modifier: Modifier = Modifier,
+ htmlText: String = """
+ Jetpack Compose
+
+ Build better apps faster with Jetpack Compose
+
+ """.trimIndent()
+) {
+ Text(
+ AnnotatedString.fromHtml(
+ htmlText,
+ linkStyles = TextLinkStyles(
+ style = SpanStyle(
+ textDecoration = TextDecoration.Underline,
+ fontStyle = FontStyle.Italic,
+ color = Color.Blue
+ )
+ )
+ ),
+ modifier
+ )
+}
+// [END android_compose_text_annotatedhtmlstringwithlink]
+
+@Preview(showBackground = true)
+@Composable
+private fun AnnotatedHtmlStringWithLinkPreview() {
+ AnnotatedHtmlStringWithLink()
+}
diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/focus/FocusSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/focus/FocusSnippets.kt
index 82b5b2d1e..2fce7f0aa 100644
--- a/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/focus/FocusSnippets.kt
+++ b/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/focus/FocusSnippets.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 The Android Open Source Project
+ * Copyright 2025 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.
@@ -14,20 +14,17 @@
* limitations under the License.
*/
-@file:Suppress("DEPRECATION_ERROR")
-
package com.example.compose.snippets.touchinput.focus
-import androidx.compose.foundation.Indication
-import androidx.compose.foundation.IndicationInstance
+import androidx.compose.foundation.IndicationNodeFactory
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.focusGroup
import androidx.compose.foundation.focusable
+import androidx.compose.foundation.interaction.FocusInteraction
import androidx.compose.foundation.interaction.InteractionSource
import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -42,7 +39,6 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -70,9 +66,13 @@ import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.key.type
+import androidx.compose.ui.node.DelegatableNode
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.invalidateDraw
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.launch
@Preview
@Composable
@@ -436,45 +436,64 @@ private fun ReactToFocus() {
}
// [START android_compose_touchinput_focus_advanced_cues]
-private class MyHighlightIndicationInstance(isEnabledState: State) :
- IndicationInstance {
- private val isEnabled by isEnabledState
- override fun ContentDrawScope.drawIndication() {
+private class MyHighlightIndicationNode(private val interactionSource: InteractionSource) :
+ Modifier.Node(), DrawModifierNode {
+ private var isFocused = false
+
+ override fun onAttach() {
+ coroutineScope.launch {
+ var focusCount = 0
+ interactionSource.interactions.collect { interaction ->
+ when (interaction) {
+ is FocusInteraction.Focus -> focusCount++
+ is FocusInteraction.Unfocus -> focusCount--
+ }
+ val focused = focusCount > 0
+ if (isFocused != focused) {
+ isFocused = focused
+ invalidateDraw()
+ }
+ }
+ }
+ }
+
+ override fun ContentDrawScope.draw() {
drawContent()
- if (isEnabled) {
+ if (isFocused) {
drawRect(size = size, color = Color.White, alpha = 0.2f)
}
}
}
+
// [END android_compose_touchinput_focus_advanced_cues]
// [START android_compose_touchinput_focus_indication]
-class MyHighlightIndication : Indication {
- @Composable
- override fun rememberUpdatedInstance(interactionSource: InteractionSource):
- IndicationInstance {
- val isFocusedState = interactionSource.collectIsFocusedAsState()
- return remember(interactionSource) {
- MyHighlightIndicationInstance(isEnabledState = isFocusedState)
- }
+object MyHighlightIndication : IndicationNodeFactory {
+ override fun create(interactionSource: InteractionSource): DelegatableNode {
+ return MyHighlightIndicationNode(interactionSource)
}
+
+ override fun hashCode(): Int = -1
+
+ override fun equals(other: Any?) = other === this
}
// [END android_compose_touchinput_focus_indication]
@Composable
private fun ApplyIndication() {
// [START android_compose_touchinput_focus_apply_indication]
- val highlightIndication = remember { MyHighlightIndication() }
var interactionSource = remember { MutableInteractionSource() }
Card(
modifier = Modifier
.clickable(
interactionSource = interactionSource,
- indication = highlightIndication,
+ indication = MyHighlightIndication,
enabled = true,
onClick = { }
)
- ) {}
+ ) {
+ Text("hello")
+ }
// [END android_compose_touchinput_focus_apply_indication]
}
diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/keyboardinput/KeyboardShortcutsHelper.kt b/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/keyboardinput/KeyboardShortcutsHelper.kt
new file mode 100644
index 000000000..610143cec
--- /dev/null
+++ b/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/keyboardinput/KeyboardShortcutsHelper.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2024 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 com.example.compose.snippets.touchinput.keyboardinput
+
+import android.os.Build
+import android.os.Bundle
+import android.view.KeyEvent
+import android.view.KeyboardShortcutGroup
+import android.view.KeyboardShortcutInfo
+import android.view.Menu
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.LocalActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.annotation.RequiresApi
+import androidx.compose.material3.Button
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+
+class MainActivity : ComponentActivity() {
+ // Activity codes such as overridden onStart method.
+
+ @RequiresApi(Build.VERSION_CODES.N)
+ // [START android_compose_keyboard_shortcuts_helper]
+ override fun onProvideKeyboardShortcuts(
+ data: MutableList?,
+ menu: Menu?,
+ deviceId: Int
+ ) {
+ val shortcutGroup =
+ KeyboardShortcutGroup(
+ "Cursor movement",
+ listOf(
+ KeyboardShortcutInfo("Up", KeyEvent.KEYCODE_P, KeyEvent.META_CTRL_ON),
+ KeyboardShortcutInfo("Down", KeyEvent.KEYCODE_N, KeyEvent.META_CTRL_ON),
+ KeyboardShortcutInfo("Forward", KeyEvent.KEYCODE_F, KeyEvent.META_CTRL_ON),
+ KeyboardShortcutInfo("Backward", KeyEvent.KEYCODE_B, KeyEvent.META_CTRL_ON),
+ )
+ )
+ data?.add(shortcutGroup)
+ }
+ // [END android_compose_keyboard_shortcuts_helper]
+}
+
+class AnotherActivity : ComponentActivity() {
+
+ @RequiresApi(Build.VERSION_CODES.N)
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
+ setContent {
+ MaterialTheme {
+ // [START android_compose_keyboard_shortcuts_helper_request]
+ val activity = LocalActivity.current
+
+ Button(
+ onClick = {
+ activity?.requestShowKeyboardShortcuts()
+ }
+ ) {
+ Text(text = "Show keyboard shortcuts")
+ }
+ // [END android_compose_keyboard_shortcuts_helper_request]
+ }
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.N)
+ // [START android_compose_keyboard_shortcuts_helper_with_groups]
+ override fun onProvideKeyboardShortcuts(
+ data: MutableList?,
+ menu: Menu?,
+ deviceId: Int
+ ) {
+ val cursorMovement = KeyboardShortcutGroup(
+ "Cursor movement",
+ listOf(
+ KeyboardShortcutInfo("Up", KeyEvent.KEYCODE_P, KeyEvent.META_CTRL_ON),
+ KeyboardShortcutInfo("Down", KeyEvent.KEYCODE_N, KeyEvent.META_CTRL_ON),
+ KeyboardShortcutInfo("Forward", KeyEvent.KEYCODE_F, KeyEvent.META_CTRL_ON),
+ KeyboardShortcutInfo("Backward", KeyEvent.KEYCODE_B, KeyEvent.META_CTRL_ON),
+ )
+ )
+
+ val messageEdit = KeyboardShortcutGroup(
+ "Message editing",
+ listOf(
+ KeyboardShortcutInfo("Select All", KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON),
+ KeyboardShortcutInfo(
+ "Send a message",
+ KeyEvent.KEYCODE_ENTER,
+ KeyEvent.META_SHIFT_ON
+ )
+ )
+ )
+
+ data?.add(cursorMovement)
+ data?.add(messageEdit)
+ }
+ // [END android_compose_keyboard_shortcuts_helper_with_groups]
+}
diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/keyboardinput/commands.kt b/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/keyboardinput/commands.kt
new file mode 100644
index 000000000..d0c08c544
--- /dev/null
+++ b/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/keyboardinput/commands.kt
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2024 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 com.example.compose.snippets.touchinput.keyboardinput
+
+import android.content.Context
+import android.widget.Toast
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.Card
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextField
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusDirection
+import androidx.compose.ui.focus.onFocusEvent
+import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.KeyEventType
+import androidx.compose.ui.input.key.isAltPressed
+import androidx.compose.ui.input.key.isCtrlPressed
+import androidx.compose.ui.input.key.isMetaPressed
+import androidx.compose.ui.input.key.isShiftPressed
+import androidx.compose.ui.input.key.key
+import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.input.key.onPreviewKeyEvent
+import androidx.compose.ui.input.key.type
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.unit.dp
+
+@Suppress("unused")
+@Composable
+fun CommandsScreen() {
+ val context = LocalContext.current
+ var playerState by rememberSaveable { mutableStateOf(false) }
+
+ val doSomething = {
+ showToast(context, "Doing something")
+ }
+
+ val doAnotherThing = {
+ showToast(context, "Doing another thing")
+ }
+
+ val togglePlayPause = {
+ playerState = !playerState
+ val message = if (playerState) {
+ "Playing"
+ } else {
+ "Paused"
+ }
+ showToast(context, message)
+ }
+
+ val actionC = {
+ showToast(context, "Action C")
+ }
+
+ Column(
+ verticalArrangement = Arrangement.spacedBy(16.dp),
+ modifier = Modifier
+ .verticalScroll(rememberScrollState())
+ .padding(8.dp)
+ ) {
+ KeyEvents(doSomething)
+ ModifierKeys(doSomething)
+ SpacebarAndEnterKeyTriggersClickEvents(togglePlayPause)
+ UnconsumedKeyEvents(doSomething, doAnotherThing, actionC)
+ PreviewKeyEvents()
+ InterceptKeyEvents(
+ doSomething,
+ { keyEvent ->
+ showToast(context, "onPreviewKeyEvent: ${keyEvent.key.keyCode}")
+ },
+ { keyEvent ->
+ showToast(context, "onKeyEvent: ${keyEvent.key.keyCode}")
+ }
+ )
+ }
+}
+
+fun showToast(context: Context, message: String) {
+ val toast = Toast.makeText(context, message, Toast.LENGTH_SHORT)
+ toast.show()
+}
+
+@Composable
+private fun BoxWithFocusIndication(
+ modifier: Modifier = Modifier,
+ content: @Composable BoxScope.() -> Unit
+) {
+ var isFocused by remember { mutableStateOf(false) }
+ val backgroundColor = if (isFocused) {
+ MaterialTheme.colorScheme.surfaceVariant
+ } else {
+ MaterialTheme.colorScheme.surface
+ }
+ Box(
+ modifier = modifier
+ .onFocusEvent {
+ isFocused = it.isFocused
+ }
+ .background(backgroundColor),
+ content = content
+ )
+}
+
+@Composable
+private fun KeyEvents(
+ doSomething: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ BoxWithFocusIndication(modifier) {
+ // [START android_compose_touchinput_keyboardinput_keyevents]
+ Box(
+ modifier = Modifier
+ .onKeyEvent {
+ if (
+ it.type == KeyEventType.KeyUp &&
+ it.key == Key.S
+ ) {
+ doSomething()
+ true
+ } else {
+ false
+ }
+ }
+ .focusable()
+ ) {
+ Text("Press S key")
+ }
+ // [END android_compose_touchinput_keyboardinput_keyevents]
+ }
+}
+
+@Composable
+private fun ModifierKeys(
+ doSomething: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ BoxWithFocusIndication(modifier = modifier) {
+ // [START android_compose_touchinput_keyboardinput_modifierkeys]
+ Box(
+ modifier = Modifier
+ .focusable()
+ .onKeyEvent {
+ if (
+ it.type == KeyEventType.KeyUp &&
+ it.key == Key.S &&
+ !it.isAltPressed &&
+ !it.isCtrlPressed &&
+ !it.isMetaPressed &&
+ !it.isShiftPressed
+ ) {
+ doSomething()
+ true
+ } else {
+ false
+ }
+ }
+ ) {
+ Text("Press S key with a modifier key")
+ }
+ // [END android_compose_touchinput_keyboardinput_modifierkeys]
+ }
+}
+
+@Composable
+private fun SpacebarAndEnterKeyTriggersClickEvents(
+ togglePausePlay: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ BoxWithFocusIndication(modifier = modifier) {
+ // [START android_compose_touchinput_keyboardinput_spacebar]
+ MediaPlayer(modifier = Modifier.clickable { togglePausePlay() })
+ // [END android_compose_touchinput_keyboardinput_spacebar]
+ }
+}
+
+@Composable
+private fun MediaPlayer(
+ modifier: Modifier = Modifier,
+) {
+ Box(
+ modifier = modifier
+ .size(200.dp)
+ .background(MaterialTheme.colorScheme.primaryContainer)
+ )
+}
+
+@Composable
+private fun UnconsumedKeyEvents(
+ actionA: () -> Unit,
+ actionB: () -> Unit,
+ actionC: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ BoxWithFocusIndication(modifier = modifier) {
+ // [START android_compose_touchinput_keyboardinput_unconsumedkeyevents]
+ OuterComponent(
+ modifier = Modifier.onKeyEvent {
+ when {
+ it.type == KeyEventType.KeyUp && it.key == Key.S -> {
+ actionB() // This function is never called.
+ true
+ }
+
+ it.type == KeyEventType.KeyUp && it.key == Key.D -> {
+ actionC()
+ true
+ }
+
+ else -> false
+ }
+ }
+ ) {
+ InnerComponent(
+ modifier = Modifier.onKeyEvent {
+ if (it.type == KeyEventType.KeyUp && it.key == Key.S) {
+ actionA()
+ true
+ } else {
+ false
+ }
+ }
+ )
+ }
+ // [END android_compose_touchinput_keyboardinput_unconsumedkeyevents]
+ }
+}
+
+@Composable
+private fun OuterComponent(
+ modifier: Modifier = Modifier,
+ content: @Composable BoxScope.() -> Unit,
+) =
+ Box(content = content, modifier = modifier.focusable())
+
+@Composable
+private fun InnerComponent(
+ modifier: Modifier = Modifier
+) {
+ Card(modifier = modifier.focusable()) {
+ Text("Press S key or D key", modifier = Modifier.padding(16.dp))
+ }
+}
+
+@Composable
+private fun PreviewKeyEvents() {
+ // [START android_compose_touchinput_keyboardinput_previewkeyevents]
+ val focusManager = LocalFocusManager.current
+ var textFieldValue by remember { mutableStateOf(TextFieldValue()) }
+
+ TextField(
+ textFieldValue,
+ onValueChange = {
+ textFieldValue = it
+ },
+ modifier = Modifier.onPreviewKeyEvent {
+ if (it.type == KeyEventType.KeyUp && it.key == Key.Tab) {
+ focusManager.moveFocus(FocusDirection.Next)
+ true
+ } else {
+ false
+ }
+ }
+ )
+ // [END android_compose_touchinput_keyboardinput_previewkeyevents]
+}
+
+@Composable
+private fun InterceptKeyEvents(
+ previewSKey: () -> Unit,
+ actionForPreview: (KeyEvent) -> Unit,
+ actionForKeyEvent: (KeyEvent) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ BoxWithFocusIndication(modifier = modifier) {
+ // [START android_compose_touchinput_keyboardinput_interceptevents]
+ Column(
+ modifier = Modifier.onPreviewKeyEvent {
+ if (it.key == Key.S) {
+ previewSKey()
+ true
+ } else {
+ false
+ }
+ }
+ ) {
+ Box(
+ modifier = Modifier
+ .focusable()
+ .onPreviewKeyEvent {
+ actionForPreview(it)
+ false
+ }
+ .onKeyEvent {
+ actionForKeyEvent(it)
+ true
+ }
+ ) {
+ Text("Press any key")
+ }
+ }
+ // [END android_compose_touchinput_keyboardinput_interceptevents]
+ }
+}
diff --git a/compose/snippets/src/main/res/drawable-nodpi/cheese_1.jpg b/compose/snippets/src/main/res/drawable-nodpi/cheese_1.jpg
new file mode 100644
index 000000000..65be51c6b
Binary files /dev/null and b/compose/snippets/src/main/res/drawable-nodpi/cheese_1.jpg differ
diff --git a/compose/snippets/src/main/res/drawable-nodpi/cheese_2.jpg b/compose/snippets/src/main/res/drawable-nodpi/cheese_2.jpg
new file mode 100644
index 000000000..ae83e1ac6
Binary files /dev/null and b/compose/snippets/src/main/res/drawable-nodpi/cheese_2.jpg differ
diff --git a/compose/snippets/src/main/res/drawable-nodpi/cheese_3.jpg b/compose/snippets/src/main/res/drawable-nodpi/cheese_3.jpg
new file mode 100644
index 000000000..362ef3874
Binary files /dev/null and b/compose/snippets/src/main/res/drawable-nodpi/cheese_3.jpg differ
diff --git a/compose/snippets/src/main/res/drawable-nodpi/cheese_4.jpg b/compose/snippets/src/main/res/drawable-nodpi/cheese_4.jpg
new file mode 100644
index 000000000..052ef4c72
Binary files /dev/null and b/compose/snippets/src/main/res/drawable-nodpi/cheese_4.jpg differ
diff --git a/compose/snippets/src/main/res/drawable-nodpi/cheese_5.jpg b/compose/snippets/src/main/res/drawable-nodpi/cheese_5.jpg
new file mode 100644
index 000000000..137a7f3a1
Binary files /dev/null and b/compose/snippets/src/main/res/drawable-nodpi/cheese_5.jpg differ
diff --git a/compose/snippets/src/main/res/values/strings.xml b/compose/snippets/src/main/res/values/strings.xml
index faf8fd472..02254e29a 100644
--- a/compose/snippets/src/main/res/values/strings.xml
+++ b/compose/snippets/src/main/res/values/strings.xml
@@ -53,4 +53,6 @@
Shopping
Profile
This is just a placeholder.
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index b3b72fb4e..fdc4663df 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,59 +1,64 @@
[versions]
accompanist = "0.36.0"
-androidGradlePlugin = "8.6.1"
-androidx-activity-compose = "1.9.2"
+androidGradlePlugin = "8.8.0"
+androidx-activity-compose = "1.10.0"
androidx-appcompat = "1.7.0"
-androidx-compose-bom = "2024.09.02"
+androidx-compose-bom = "2025.01.01"
androidx-compose-ui-test = "1.7.0-alpha08"
-androidx-constraintlayout = "2.1.4"
-androidx-constraintlayout-compose = "1.0.1"
+androidx-constraintlayout = "2.2.0"
+androidx-constraintlayout-compose = "1.1.0"
androidx-coordinator-layout = "1.2.0"
-androidx-corektx = "1.13.1"
+androidx-corektx = "1.15.0"
androidx-emoji2-views = "1.5.0"
-androidx-fragment-ktx = "1.8.3"
-androidx-glance-appwidget = "1.1.0"
-androidx-lifecycle-compose = "2.8.6"
-androidx-lifecycle-runtime-compose = "2.8.6"
-androidx-navigation = "2.8.1"
-androidx-paging = "3.3.2"
+androidx-fragment-ktx = "1.8.5"
+androidx-glance-appwidget = "1.1.1"
+androidx-lifecycle-compose = "2.8.7"
+androidx-lifecycle-runtime-compose = "2.8.7"
+androidx-navigation = "2.8.6"
+androidx-paging = "3.3.5"
androidx-test = "1.6.1"
androidx-test-espresso = "3.6.1"
-androidx-window = "1.3.0"
+androidx-window = "1.4.0-beta01"
androidxHiltNavigationCompose = "1.2.0"
coil = "2.7.0"
# @keep
-compileSdk = "34"
-compose-latest = "1.7.2"
+compileSdk = "35"
+compose-latest = "1.7.7"
composeUiTooling = "1.4.0"
coreSplashscreen = "1.0.1"
-coroutines = "1.7.3"
+coroutines = "1.10.1"
glide = "1.0.0-beta01"
google-maps = "19.0.0"
-gradle-versions = "0.51.0"
-hilt = "2.52"
-horologist = "0.6.19"
+gradle-versions = "0.52.0"
+guava = "33.4.0-jre"
+hilt = "2.55"
+horologist = "0.6.22"
junit = "4.13.2"
-kotlin = "2.0.20"
-kotlinxSerializationJson = "1.7.3"
-ksp = "2.0.20-1.0.25"
-maps-compose = "6.1.2"
-material = "1.13.0-alpha06"
+kotlin = "2.1.10"
+kotlinxSerializationJson = "1.8.0"
+ksp = "2.1.10-1.0.29"
+maps-compose = "6.4.2"
+material = "1.13.0-alpha10"
material3-adaptive = "1.0.0"
-material3-adaptive-navigation-suite = "1.3.0"
-media3 = "1.4.1"
+material3-adaptive-navigation-suite = "1.3.1"
+media3 = "1.5.1"
# @keep
minSdk = "21"
-playServicesWearable = "18.2.0"
-recyclerview = "1.3.2"
+playServicesWearable = "19.0.0"
+protolayout = "1.2.1"
+recyclerview = "1.4.0"
# @keep
targetSdk = "34"
-version-catalog-update = "0.8.4"
+tiles = "1.4.1"
+version-catalog-update = "0.8.5"
+wear = "1.3.0"
wearComposeFoundation = "1.4.0"
wearComposeMaterial = "1.4.0"
+wearToolingPreview = "1.0.0"
[libraries]
accompanist-adaptive = { module = "com.google.accompanist:accompanist-adaptive", version.ref = "accompanist" }
-accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" }
+accompanist-permissions = "com.google.accompanist:accompanist-permissions:0.37.0"
accompanist-theme-adapter-appcompat = { module = "com.google.accompanist:accompanist-themeadapter-appcompat", version.ref = "accompanist" }
accompanist-theme-adapter-material = { module = "com.google.accompanist:accompanist-themeadapter-material", version.ref = "accompanist" }
accompanist-theme-adapter-material3 = { module = "com.google.accompanist:accompanist-themeadapter-material3", version.ref = "accompanist" }
@@ -94,7 +99,7 @@ androidx-glance-appwidget = { module = "androidx.glance:glance-appwidget", versi
androidx-glance-material3 = { module = "androidx.glance:glance-material3", version.ref = "androidx-glance-appwidget" }
androidx-graphics-shapes = "androidx.graphics:graphics-shapes:1.0.1"
androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" }
-androidx-lifecycle-runtime = "androidx.lifecycle:lifecycle-runtime-ktx:2.8.5"
+androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidx-lifecycle-runtime-compose" }
androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle-runtime-compose" }
androidx-lifecycle-viewModelCompose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle-compose" }
androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle-compose" }
@@ -103,12 +108,22 @@ androidx-media3-common = { module = "androidx.media3:media3-common", version.ref
androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" }
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "androidx-navigation" }
androidx-paging-compose = { module = "androidx.paging:paging-compose", version.ref = "androidx-paging" }
+androidx-protolayout = { module = "androidx.wear.protolayout:protolayout", version.ref = "protolayout" }
+androidx-protolayout-expression = { module = "androidx.wear.protolayout:protolayout-expression", version.ref = "protolayout" }
+androidx-protolayout-material = { module = "androidx.wear.protolayout:protolayout-material", version.ref = "protolayout" }
androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }
androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test" }
androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-test-espresso" }
androidx-test-runner = "androidx.test:runner:1.6.2"
+androidx-tiles = { module = "androidx.wear.tiles:tiles", version.ref = "tiles" }
+androidx-tiles-renderer = { module = "androidx.wear.tiles:tiles-renderer", version.ref = "tiles" }
+androidx-tiles-testing = { module = "androidx.wear.tiles:tiles-testing", version.ref = "tiles" }
+androidx-tiles-tooling = { module = "androidx.wear.tiles:tiles-tooling", version.ref = "tiles" }
+androidx-tiles-tooling-preview = { module = "androidx.wear.tiles:tiles-tooling-preview", version.ref = "tiles" }
+androidx-wear = { module = "androidx.wear:wear", version.ref = "wear" }
+androidx-wear-tooling-preview = { module = "androidx.wear:wear-tooling-preview", version.ref = "wearToolingPreview" }
androidx-window-core = { module = "androidx.window:window-core", version.ref = "androidx-window" }
-androidx-work-runtime-ktx = "androidx.work:work-runtime-ktx:2.9.1"
+androidx-work-runtime-ktx = "androidx.work:work-runtime-ktx:2.10.0"
coil-kt-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
compose-foundation = { module = "androidx.wear.compose:compose-foundation", version.ref = "wearComposeFoundation" }
compose-material = { module = "androidx.wear.compose:compose-material", version.ref = "wearComposeMaterial" }
@@ -117,6 +132,7 @@ glide-compose = { module = "com.github.bumptech.glide:compose", version.ref = "g
google-android-material = { module = "com.google.android.material:material", version.ref = "material" }
googlemaps-compose = { module = "com.google.maps.android:maps-compose", version.ref = "maps-compose" }
googlemaps-maps = { module = "com.google.android.gms:play-services-maps", version.ref = "google-maps" }
+guava = { module = "com.google.guava:guava", version.ref = "guava" }
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
hilt-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" }
horologist-compose-layout = { module = "com.google.android.horologist:horologist-compose-layout", version.ref = "horologist" }
@@ -124,7 +140,7 @@ horologist-compose-material = { module = "com.google.android.horologist:horologi
junit = { module = "junit:junit", version.ref = "junit" }
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
-kotlinx-coroutines-test = "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0"
+kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
play-services-wearable = { module = "com.google.android.gms:play-services-wearable", version.ref = "playServicesWearable" }
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 0aaefbcaf..df97d72b8 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/misc/.gitignore b/misc/.gitignore
new file mode 100644
index 000000000..42afabfd2
--- /dev/null
+++ b/misc/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/misc/build.gradle.kts b/misc/build.gradle.kts
new file mode 100644
index 000000000..de867bb88
--- /dev/null
+++ b/misc/build.gradle.kts
@@ -0,0 +1,72 @@
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.ksp)
+ alias(libs.plugins.hilt)
+ alias(libs.plugins.compose.compiler)
+}
+
+android {
+ compileSdk = libs.versions.compileSdk.get().toInt()
+ namespace = "com.example.snippets"
+
+ defaultConfig {
+ applicationId = "com.example.snippets"
+ minSdk = libs.versions.minSdk.get().toInt()
+ targetSdk = libs.versions.targetSdk.get().toInt()
+ versionCode = 1
+ versionName = "1.0"
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ kotlin {
+ jvmToolchain(17)
+ }
+
+ buildTypes {
+ getByName("debug") {
+ signingConfig = signingConfigs.getByName("debug")
+ }
+
+ getByName("release") {
+ isMinifyEnabled = false
+ proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro")
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+ buildFeatures {
+ compose = true
+ // Disable unused AGP features
+ viewBinding = true
+ }
+
+}
+dependencies {
+ val composeBom = platform(libs.androidx.compose.bom)
+ implementation(composeBom)
+ androidTestImplementation(composeBom)
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.activity.compose)
+ implementation(libs.androidx.compose.runtime)
+ implementation(libs.androidx.compose.foundation)
+ implementation(libs.androidx.compose.foundation.layout)
+ implementation(libs.androidx.compose.ui.util)
+ implementation(libs.androidx.compose.ui.tooling.preview)
+ implementation(libs.androidx.compose.material3)
+
+ implementation(libs.hilt.android)
+ implementation(libs.androidx.hilt.navigation.compose)
+ implementation(libs.kotlinx.serialization.json)
+ ksp(libs.hilt.compiler)
+
+ implementation(libs.androidx.lifecycle.runtime)
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.test.core)
+ androidTestImplementation(libs.androidx.test.runner)
+ androidTestImplementation(libs.androidx.test.espresso.core)
+}
diff --git a/shared/proguard-rules.pro b/misc/proguard-rules.pro
similarity index 100%
rename from shared/proguard-rules.pro
rename to misc/proguard-rules.pro
diff --git a/misc/src/main/AndroidManifest.xml b/misc/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..0a7a5c6f0
--- /dev/null
+++ b/misc/src/main/AndroidManifest.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/misc/src/main/java/com/example/snippets/BroadcastReceiverJavaSnippets.java b/misc/src/main/java/com/example/snippets/BroadcastReceiverJavaSnippets.java
new file mode 100644
index 000000000..083639954
--- /dev/null
+++ b/misc/src/main/java/com/example/snippets/BroadcastReceiverJavaSnippets.java
@@ -0,0 +1,120 @@
+package com.example.snippets;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+
+import androidx.activity.ComponentActivity;
+import androidx.core.content.ContextCompat;
+
+import java.util.Objects;
+
+import javax.inject.Inject;
+
+import dagger.hilt.android.qualifiers.ApplicationContext;
+
+// Warning for reader: This file has the Java code snippets for completeness of the corresponding
+// documentation page, but these snippets are not part of the actual sample. Refer to the Kotlin
+// code for the actual sample.
+public class BroadcastReceiverJavaSnippets {
+
+ // [START android_broadcast_receiver_2_class_java]
+ public static class MyBroadcastReceiver extends BroadcastReceiver {
+
+ @Inject
+ DataRepository dataRepository;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Objects.equals(intent.getAction(), "com.example.snippets.ACTION_UPDATE_DATA")) {
+ String data = intent.getStringExtra("com.example.snippets.DATA");
+ // Do something with the data, for example send it to a data repository:
+ if (data != null) { dataRepository.updateData(data); }
+ }
+ }
+ }
+ // [END android_broadcast_receiver_2_class_java]
+
+ /** @noinspection ConstantValue, unused */
+ public static class BroadcastReceiverViewModel {
+ Context context;
+
+ public BroadcastReceiverViewModel(@ApplicationContext Context context) {
+ this.context = context;
+ }
+
+ // [START android_broadcast_receiver_3_definition_java]
+ MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
+ // [END android_broadcast_receiver_3_definition_java]
+
+ public void registerBroadcastReceiver() {
+ // [START android_broadcast_receiver_4_intent_filter_java]
+ IntentFilter filter = new IntentFilter("com.example.snippets.ACTION_UPDATE_DATA");
+ // [END android_broadcast_receiver_4_intent_filter_java]
+ // [START android_broadcast_receiver_5_exported_java]
+ boolean listenToBroadcastsFromOtherApps = false;
+ int receiverFlags = listenToBroadcastsFromOtherApps
+ ? ContextCompat.RECEIVER_EXPORTED
+ : ContextCompat.RECEIVER_NOT_EXPORTED;
+ // [END android_broadcast_receiver_5_exported_java]
+
+ // [START android_broadcast_receiver_6_register_java]
+ ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags);
+ // [END android_broadcast_receiver_6_register_java]
+
+ // [START android_broadcast_receiver_12_register_with_permission_java]
+ ContextCompat.registerReceiver(
+ context, myBroadcastReceiver, filter,
+ android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ null, // scheduler that defines thread, null means run on main thread
+ receiverFlags
+ );
+ // [END android_broadcast_receiver_12_register_with_permission_java]
+ }
+
+ public void unregisterBroadcastReceiver() {
+ context.unregisterReceiver(myBroadcastReceiver);
+ }
+
+ public void sendBroadcast(String newData, boolean usePermission) {
+ if(usePermission) {
+ // [START android_broadcast_receiver_8_send_java]
+ Intent intent = new Intent("com.example.snippets.ACTION_UPDATE_DATA");
+ intent.putExtra("com.example.snippets.DATA", newData);
+ intent.setPackage("com.example.snippets");
+ context.sendBroadcast(intent);
+ // [END android_broadcast_receiver_8_send_java]
+ } else {
+ Intent intent = new Intent("com.example.snippets.ACTION_UPDATE_DATA");
+ intent.putExtra("com.example.snippets.DATA", newData);
+ intent.setPackage("com.example.snippets");
+ // [START android_broadcast_receiver_9_send_with_permission_java]
+ context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION);
+ // [END android_broadcast_receiver_9_send_with_permission_java]
+ }
+ }
+ }
+
+ /** @noinspection InnerClassMayBeStatic*/
+ // [START android_broadcast_receiver_13_activity_java]
+ class MyActivity extends ComponentActivity {
+ MyBroadcastReceiver myBroadcastReceiver;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // [START_EXCLUDE]
+ IntentFilter filter = new IntentFilter("com.example.snippets.ACTION_UPDATE_DATA");
+ boolean listenToBroadcastsFromOtherApps = false;
+ int receiverFlags = listenToBroadcastsFromOtherApps
+ ? ContextCompat.RECEIVER_EXPORTED
+ : ContextCompat.RECEIVER_NOT_EXPORTED;
+ // [END_EXCLUDE]
+ ContextCompat.registerReceiver(this, myBroadcastReceiver, filter, receiverFlags);
+ // Set content
+ }
+ }
+ // [END android_broadcast_receiver_13_activity_java]
+}
diff --git a/misc/src/main/java/com/example/snippets/BroadcastReceiverSnippets.kt b/misc/src/main/java/com/example/snippets/BroadcastReceiverSnippets.kt
new file mode 100644
index 000000000..dda35cbe0
--- /dev/null
+++ b/misc/src/main/java/com/example/snippets/BroadcastReceiverSnippets.kt
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2024 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 com.example.snippets
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.Button
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextField
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
+import androidx.core.content.ContextCompat
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.compose.LifecycleStartEffect
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import dagger.hilt.android.AndroidEntryPoint
+import dagger.hilt.android.lifecycle.HiltViewModel
+import dagger.hilt.android.qualifiers.ApplicationContext
+import javax.inject.Inject
+
+// Warning for reader: This file contains both the code snippets for apps _sending_ broadcasts, as
+// those that are _receiving_ broadcasts. Do not consider this a reference implementation.
+//
+// The actual sample demonstrates how data can be passed from a broadcast receiver back to the UI,
+// through an intermediary data repository.
+
+@AndroidEntryPoint
+// [START android_broadcast_receiver_2_class]
+class MyBroadcastReceiver : BroadcastReceiver() {
+
+ @Inject
+ lateinit var dataRepository: DataRepository
+
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action == "com.example.snippets.ACTION_UPDATE_DATA") {
+ val data = intent.getStringExtra("com.example.snippets.DATA") ?: "No data"
+ // Do something with the data, for example send it to a data repository:
+ dataRepository.updateData(data)
+ }
+ }
+}
+// [END android_broadcast_receiver_2_class]
+
+@HiltViewModel
+class BroadcastReceiverViewModel @Inject constructor(
+ @ApplicationContext private val context: Context,
+ dataRepository: DataRepository
+) : ViewModel() {
+ val data = dataRepository.data
+
+ @Suppress("MemberVisibilityCanBePrivate")
+ // [START android_broadcast_receiver_3_definition]
+ val myBroadcastReceiver = MyBroadcastReceiver()
+ // [END android_broadcast_receiver_3_definition]
+
+ fun registerBroadcastReceiver() {
+ // [START android_broadcast_receiver_4_intent_filter]
+ val filter = IntentFilter("com.example.snippets.ACTION_UPDATE_DATA")
+ // [END android_broadcast_receiver_4_intent_filter]
+ // [START android_broadcast_receiver_5_exported]
+ val listenToBroadcastsFromOtherApps = false
+ val receiverFlags = if (listenToBroadcastsFromOtherApps) {
+ ContextCompat.RECEIVER_EXPORTED
+ } else {
+ ContextCompat.RECEIVER_NOT_EXPORTED
+ }
+ // [END android_broadcast_receiver_5_exported]
+
+ // [START android_broadcast_receiver_6_register]
+ ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags)
+ // [END android_broadcast_receiver_6_register]
+
+ // [START android_broadcast_receiver_12_register_with_permission]
+ ContextCompat.registerReceiver(
+ context, myBroadcastReceiver, filter,
+ android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ null, // scheduler that defines thread, null means run on main thread
+ receiverFlags
+ )
+ // [END android_broadcast_receiver_12_register_with_permission]
+ }
+
+ fun unregisterBroadcastReceiver() {
+ context.unregisterReceiver(myBroadcastReceiver)
+ }
+
+ fun sendBroadcast(newData: String, usePermission: Boolean = false) {
+ if (!usePermission) {
+ // [START android_broadcast_receiver_8_send]
+ val intent = Intent("com.example.snippets.ACTION_UPDATE_DATA").apply {
+ putExtra("com.example.snippets.DATA", newData)
+ setPackage("com.example.snippets")
+ }
+ context.sendBroadcast(intent)
+ // [END android_broadcast_receiver_8_send]
+ } else {
+ val intent = Intent("com.example.snippets.ACTION_UPDATE_DATA").apply {
+ putExtra("com.example.snippets.DATA", newData)
+ setPackage("com.example.snippets")
+ }
+ // [START android_broadcast_receiver_9_send_with_permission]
+ context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ // [END android_broadcast_receiver_9_send_with_permission]
+ }
+ }
+}
+
+@Suppress("NAME_SHADOWING")
+@Composable
+fun LifecycleScopedBroadcastReceiver(
+ registerReceiver: () -> Unit,
+ unregisterReceiver: () -> Unit
+) {
+ val registerReceiver by rememberUpdatedState(registerReceiver)
+ val unregisterReceiver by rememberUpdatedState(unregisterReceiver)
+ // [START android_broadcast_receiver_7_lifecycle_scoped]
+ LifecycleStartEffect(true) {
+ registerReceiver()
+ onStopOrDispose { unregisterReceiver() }
+ }
+ // [END android_broadcast_receiver_7_lifecycle_scoped]
+}
+
+@Composable
+fun BroadcastReceiverSample(
+ modifier: Modifier = Modifier,
+ viewModel: BroadcastReceiverViewModel = hiltViewModel()
+) {
+ val data by viewModel.data.collectAsStateWithLifecycle()
+ BroadcastReceiverSample(
+ modifier = modifier,
+ data = data,
+ registerBroadcastReceiver = viewModel::registerBroadcastReceiver,
+ unregisterBroadcastReceiver = viewModel::unregisterBroadcastReceiver,
+ sendBroadcast = viewModel::sendBroadcast
+ )
+}
+
+@Composable
+fun BroadcastReceiverSample(
+ modifier: Modifier = Modifier,
+ data: String,
+ registerBroadcastReceiver: () -> Unit,
+ unregisterBroadcastReceiver: () -> Unit,
+ sendBroadcast: (newData: String) -> Unit
+) {
+ var newData by remember { mutableStateOf("") }
+ Scaffold { innerPadding ->
+ Column(
+ modifier
+ .padding(innerPadding)
+ .padding(16.dp)
+ ) {
+ Text("Fill in a word, send broadcast, and see it added to the bottom")
+ Spacer(Modifier.height(16.dp))
+ TextField(newData, onValueChange = { newData = it }, Modifier.widthIn(min = 160.dp))
+ Spacer(Modifier.height(16.dp))
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ Button(onClick = { sendBroadcast(newData) }) {
+ Text("Send broadcast")
+ }
+ }
+ Spacer(Modifier.height(16.dp))
+ Text(data, Modifier.verticalScroll(rememberScrollState()))
+ }
+ }
+ LifecycleScopedBroadcastReceiver(registerBroadcastReceiver, unregisterBroadcastReceiver)
+}
+
+class MyBroadcastReceiverWithPermission : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ // no-op, only used to demonstrate manifest registration of receiver with permission
+ }
+}
+
+// Ignore following code - it's only used to demonstrate best practices, not part of the sample
+@Suppress("unused")
+// [START android_broadcast_receiver_13_activity]
+class MyActivity : ComponentActivity() {
+ private val myBroadcastReceiver = MyBroadcastReceiver()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // [START_EXCLUDE]
+ val filter = IntentFilter("com.example.snippets.ACTION_UPDATE_DATA")
+ val listenToBroadcastsFromOtherApps = false
+ val receiverFlags = if (listenToBroadcastsFromOtherApps) {
+ ContextCompat.RECEIVER_EXPORTED
+ } else {
+ ContextCompat.RECEIVER_NOT_EXPORTED
+ }
+
+ // [END_EXCLUDE]
+ ContextCompat.registerReceiver(this, myBroadcastReceiver, filter, receiverFlags)
+ setContent { MyApp() }
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ // When you forget to unregister your receiver here, you're causing a leak!
+ this.unregisterReceiver(myBroadcastReceiver)
+ }
+}
+// [END android_broadcast_receiver_13_activity]
+
+@Composable
+fun MyApp() {}
+
+@Suppress("unused")
+// [START android_broadcast_receiver_14_stateless]
+@Composable
+fun MyStatefulScreen() {
+ val myBroadcastReceiver = remember { MyBroadcastReceiver() }
+ val context = LocalContext.current
+ LifecycleStartEffect(true) {
+ // [START_EXCLUDE]
+ val filter = IntentFilter("com.example.snippets.ACTION_UPDATE_DATA")
+ val listenToBroadcastsFromOtherApps = false
+ val flags = if (listenToBroadcastsFromOtherApps) {
+ ContextCompat.RECEIVER_EXPORTED
+ } else {
+ ContextCompat.RECEIVER_NOT_EXPORTED
+ }
+ // [END_EXCLUDE]
+ ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, flags)
+ onStopOrDispose { context.unregisterReceiver(myBroadcastReceiver) }
+ }
+ MyStatelessScreen()
+}
+
+@Composable
+fun MyStatelessScreen() {
+ // Implement your screen
+}
+// [END android_broadcast_receiver_14_stateless]
diff --git a/misc/src/main/java/com/example/snippets/DataRepository.kt b/misc/src/main/java/com/example/snippets/DataRepository.kt
new file mode 100644
index 000000000..7e626e5a8
--- /dev/null
+++ b/misc/src/main/java/com/example/snippets/DataRepository.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2024 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 com.example.snippets
+
+import javax.inject.Inject
+import javax.inject.Singleton
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+@Singleton
+class DataRepository @Inject constructor() {
+ // You would normally save this data in a database or other persistent storage
+ private val _data: MutableStateFlow = MutableStateFlow("This text will be updated")
+ val data: StateFlow = _data
+
+ fun updateData(data: String) {
+ _data.value += "\n$data"
+ }
+}
diff --git a/misc/src/main/java/com/example/snippets/MainActivity.kt b/misc/src/main/java/com/example/snippets/MainActivity.kt
new file mode 100644
index 000000000..a687939e9
--- /dev/null
+++ b/misc/src/main/java/com/example/snippets/MainActivity.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2024 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 com.example.snippets
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.ui.Modifier
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import com.example.snippets.navigation.Destination
+import com.example.snippets.navigation.LandingScreen
+import com.example.snippets.ui.theme.SnippetsTheme
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
+ setContent {
+ SnippetsTheme {
+ val navController = rememberNavController()
+ // A surface container using the 'background' color from the theme
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MaterialTheme.colorScheme.background
+ ) {
+ NavHost(navController, startDestination = "LandingScreen") {
+ composable("LandingScreen") {
+ LandingScreen { navController.navigate(it.route) }
+ }
+ Destination.entries.forEach { destination ->
+ composable(destination.route) {
+ when (destination) {
+ Destination.BroadcastReceiverExamples -> BroadcastReceiverSample()
+ // Add your destination here
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/misc/src/main/java/com/example/snippets/MyApplication.kt b/misc/src/main/java/com/example/snippets/MyApplication.kt
new file mode 100644
index 000000000..4e4a187c6
--- /dev/null
+++ b/misc/src/main/java/com/example/snippets/MyApplication.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2024 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 com.example.snippets
+
+import android.app.Application
+import dagger.hilt.android.HiltAndroidApp
+
+@HiltAndroidApp
+class MyApplication : Application()
diff --git a/misc/src/main/java/com/example/snippets/location/LocationPermissionsActivity.kt b/misc/src/main/java/com/example/snippets/location/LocationPermissionsActivity.kt
new file mode 100644
index 000000000..674f8a42e
--- /dev/null
+++ b/misc/src/main/java/com/example/snippets/location/LocationPermissionsActivity.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2024 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 com.example.snippets.location
+
+import android.Manifest
+import android.os.Build
+import androidx.activity.ComponentActivity
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.annotation.RequiresApi
+
+// Assuming this function is inside a ComponentActivity or a subclass of it
+class LocationPermissionsActivity : ComponentActivity() {
+
+ // [START android_location_requestpermissions_kotlin]
+ @RequiresApi(Build.VERSION_CODES.N)
+ fun requestPermissions() {
+ val locationPermissionRequest = registerForActivityResult(
+ ActivityResultContracts.RequestMultiplePermissions()
+ ) { permissions ->
+ when {
+ permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) -> {
+ // Precise location access granted.
+ }
+ permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> {
+ // Only approximate location access granted.
+ }
+ else -> {
+ // No location access granted.
+ }
+ }
+ }
+
+ // Before you perform the actual permission request, check whether your app
+ // already has the permissions, and whether your app needs to show a permission
+ // rationale dialog. For more details, see Request permissions:
+ // https://developer.android.com/training/permissions/requesting#request-permission
+ locationPermissionRequest.launch(
+ arrayOf(
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ )
+ )
+ }
+ // [END android_location_requestpermissions_kotlin]
+}
diff --git a/misc/src/main/java/com/example/snippets/location/LocationPermissionsActivityJava.java b/misc/src/main/java/com/example/snippets/location/LocationPermissionsActivityJava.java
new file mode 100644
index 000000000..8d20ad5d3
--- /dev/null
+++ b/misc/src/main/java/com/example/snippets/location/LocationPermissionsActivityJava.java
@@ -0,0 +1,51 @@
+package com.example.snippets.location;
+
+import android.Manifest;
+import android.os.Build;
+import android.os.Bundle;
+import androidx.activity.ComponentActivity;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import java.util.Map;
+
+public class LocationPermissionsActivityJava extends ComponentActivity {
+
+ // [START android_location_requestpermissions_java]
+ private void requestPermissions() {
+
+ ActivityResultLauncher locationPermissionRequest =
+ registerForActivityResult(new ActivityResultContracts
+ .RequestMultiplePermissions(), result -> {
+
+ Boolean fineLocationGranted = null;
+ Boolean coarseLocationGranted = null;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ fineLocationGranted = result.getOrDefault(
+ Manifest.permission.ACCESS_FINE_LOCATION, false);
+ coarseLocationGranted = result.getOrDefault(
+ Manifest.permission.ACCESS_COARSE_LOCATION,false);
+ }
+
+ if (fineLocationGranted != null && fineLocationGranted) {
+ // Precise location access granted.
+ } else if (coarseLocationGranted != null && coarseLocationGranted) {
+ // Only approximate location access granted.
+ } else {
+ // No location access granted.
+ }
+ }
+ );
+
+ // ...
+
+ // Before you perform the actual permission request, check whether your app
+ // already has the permissions, and whether your app needs to show a permission
+ // rationale dialog. For more details, see Request permissions.
+ locationPermissionRequest.launch(new String[] {
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ });
+ }
+ // [END android_location_requestpermissions_java]
+}
\ No newline at end of file
diff --git a/misc/src/main/java/com/example/snippets/navigation/Destination.kt b/misc/src/main/java/com/example/snippets/navigation/Destination.kt
new file mode 100644
index 000000000..59f424a7b
--- /dev/null
+++ b/misc/src/main/java/com/example/snippets/navigation/Destination.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2024 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 com.example.snippets.navigation
+
+enum class Destination(val route: String, val title: String) {
+ BroadcastReceiverExamples("broadcastReceiverExamples", "Broadcast Receiver Examples"),
+ // Add your example here
+}
diff --git a/misc/src/main/java/com/example/snippets/navigation/LandingScreen.kt b/misc/src/main/java/com/example/snippets/navigation/LandingScreen.kt
new file mode 100644
index 000000000..f096906dd
--- /dev/null
+++ b/misc/src/main/java/com/example/snippets/navigation/LandingScreen.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2024 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 com.example.snippets.navigation
+
+/*
+ * Copyright 2023 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.
+ */
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ListItem
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun LandingScreen(
+ navigate: (Destination) -> Unit
+) {
+ Scaffold(
+ topBar = {
+ TopAppBar(title = {
+ Text(text = "Android snippets",)
+ })
+ }
+ ) { padding ->
+ NavigationItems(modifier = Modifier.padding(padding)) { navigate(it) }
+ }
+}
+
+@Composable
+fun NavigationItems(
+ modifier: Modifier = Modifier,
+ navigate: (Destination) -> Unit
+) {
+ LazyColumn(
+ modifier = modifier
+ .fillMaxSize(),
+ verticalArrangement = Arrangement.spacedBy(8.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ items(Destination.entries) { destination ->
+ NavigationItem(destination) {
+ navigate(
+ destination
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun NavigationItem(destination: Destination, onClick: () -> Unit) {
+ ListItem(
+ headlineContent = {
+ Text(destination.title)
+ },
+ modifier = Modifier
+ .heightIn(min = 48.dp)
+ .clickable {
+ onClick()
+ }
+ )
+}
diff --git a/misc/src/main/java/com/example/snippets/ui/theme/Color.kt b/misc/src/main/java/com/example/snippets/ui/theme/Color.kt
new file mode 100644
index 000000000..662ef48ff
--- /dev/null
+++ b/misc/src/main/java/com/example/snippets/ui/theme/Color.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2024 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 com.example.snippets.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple80 = Color(0xFFD0BCFF)
+val PurpleGrey80 = Color(0xFFCCC2DC)
+val Pink80 = Color(0xFFEFB8C8)
+
+val Purple40 = Color(0xFF6650a4)
+val PurpleGrey40 = Color(0xFF625b71)
+val Pink40 = Color(0xFF7D5260)
diff --git a/misc/src/main/java/com/example/snippets/ui/theme/Theme.kt b/misc/src/main/java/com/example/snippets/ui/theme/Theme.kt
new file mode 100644
index 000000000..4dcf62e85
--- /dev/null
+++ b/misc/src/main/java/com/example/snippets/ui/theme/Theme.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2024 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 com.example.snippets.ui.theme
+
+import android.os.Build
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+
+private val DarkColorScheme = darkColorScheme(
+ primary = Purple80,
+ secondary = PurpleGrey80,
+ tertiary = Pink80
+)
+
+private val LightColorScheme = lightColorScheme(
+ primary = Purple40,
+ secondary = PurpleGrey40,
+ tertiary = Pink40
+
+ /* Other default colors to override
+ background = Color(0xFFFFFBFE),
+ surface = Color(0xFFFFFBFE),
+ onPrimary = Color.White,
+ onSecondary = Color.White,
+ onTertiary = Color.White,
+ onBackground = Color(0xFF1C1B1F),
+ onSurface = Color(0xFF1C1B1F),
+ */
+)
+
+@Composable
+fun SnippetsTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ // Dynamic color is available on Android 12+
+ dynamicColor: Boolean = true,
+ content: @Composable () -> Unit
+) {
+ val colorScheme = when {
+ dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
+ val context = LocalContext.current
+ if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
+ }
+
+ darkTheme -> DarkColorScheme
+ else -> LightColorScheme
+ }
+
+ MaterialTheme(
+ colorScheme = colorScheme,
+ typography = Typography,
+ content = content
+ )
+}
diff --git a/misc/src/main/java/com/example/snippets/ui/theme/Type.kt b/misc/src/main/java/com/example/snippets/ui/theme/Type.kt
new file mode 100644
index 000000000..f383a07ba
--- /dev/null
+++ b/misc/src/main/java/com/example/snippets/ui/theme/Type.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2024 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 com.example.snippets.ui.theme
+
+import androidx.compose.material3.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ bodyLarge = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.5.sp
+ )
+ /* Other default text styles to override
+ titleLarge = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 22.sp,
+ lineHeight = 28.sp,
+ letterSpacing = 0.sp
+ ),
+ labelSmall = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Medium,
+ fontSize = 11.sp,
+ lineHeight = 16.sp,
+ letterSpacing = 0.5.sp
+ )
+ */
+)
diff --git a/shared/src/main/res/drawable/ic_launcher_background.xml b/misc/src/main/res/drawable/ic_launcher_background.xml
similarity index 54%
rename from shared/src/main/res/drawable/ic_launcher_background.xml
rename to misc/src/main/res/drawable/ic_launcher_background.xml
index 336b6dca0..07d5da9cb 100644
--- a/shared/src/main/res/drawable/ic_launcher_background.xml
+++ b/misc/src/main/res/drawable/ic_launcher_background.xml
@@ -1,171 +1,170 @@
-
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
diff --git a/misc/src/main/res/drawable/ic_launcher_foreground.xml b/misc/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 000000000..2b068d114
--- /dev/null
+++ b/misc/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shared/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/misc/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
similarity index 79%
rename from shared/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
rename to misc/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
index eca70cfe5..6f3b755bf 100644
--- a/shared/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/misc/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -2,4 +2,5 @@
+
\ No newline at end of file
diff --git a/shared/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/misc/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
similarity index 79%
rename from shared/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
rename to misc/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
index eca70cfe5..6f3b755bf 100644
--- a/shared/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ b/misc/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -2,4 +2,5 @@
+
\ No newline at end of file
diff --git a/misc/src/main/res/mipmap-hdpi/ic_launcher.webp b/misc/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 000000000..c209e78ec
Binary files /dev/null and b/misc/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/misc/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/misc/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..b2dfe3d1b
Binary files /dev/null and b/misc/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/misc/src/main/res/mipmap-mdpi/ic_launcher.webp b/misc/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 000000000..4f0f1d64e
Binary files /dev/null and b/misc/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/misc/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/misc/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..62b611da0
Binary files /dev/null and b/misc/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/misc/src/main/res/mipmap-xhdpi/ic_launcher.webp b/misc/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 000000000..948a3070f
Binary files /dev/null and b/misc/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/misc/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/misc/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..1b9a6956b
Binary files /dev/null and b/misc/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/misc/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/misc/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 000000000..28d4b77f9
Binary files /dev/null and b/misc/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/misc/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/misc/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..9287f5083
Binary files /dev/null and b/misc/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/misc/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/misc/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 000000000..aa7d6427e
Binary files /dev/null and b/misc/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/misc/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/misc/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 000000000..9126ae37c
Binary files /dev/null and b/misc/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/misc/src/main/res/values/colors.xml b/misc/src/main/res/values/colors.xml
new file mode 100644
index 000000000..f8c6127d3
--- /dev/null
+++ b/misc/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/misc/src/main/res/values/strings.xml b/misc/src/main/res/values/strings.xml
new file mode 100644
index 000000000..870fc4736
--- /dev/null
+++ b/misc/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Background Snippets
+
\ No newline at end of file
diff --git a/misc/src/main/res/values/themes.xml b/misc/src/main/res/values/themes.xml
new file mode 100644
index 000000000..65078ebe0
--- /dev/null
+++ b/misc/src/main/res/values/themes.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index f9ba4f22a..65b99c5b1 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -21,10 +21,11 @@ dependencyResolutionManagement {
}
rootProject.name = "snippets"
include(
- ":shared",
- ":bluetoothle",
+ ":bluetoothle",
":compose:recomposehighlighter",
":kotlin",
":compose:snippets",
":wear",
+ ":views",
+ ":misc"
)
diff --git a/shared/.gitignore b/shared/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/shared/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts
deleted file mode 100644
index 78f920b3c..000000000
--- a/shared/build.gradle.kts
+++ /dev/null
@@ -1,37 +0,0 @@
-plugins {
- alias(libs.plugins.android.library)
- alias(libs.plugins.kotlin.android)
-}
-android {
- compileSdk = libs.versions.compileSdk.get().toInt()
- namespace = "com.example.shared"
-
- defaultConfig {
- minSdk = libs.versions.minSdk.get().toInt()
- testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
- }
- kotlin {
- jvmToolchain(17)
- }
- buildTypes {
- getByName("debug") {
- signingConfig = signingConfigs.getByName("debug")
- }
-
- getByName("release") {
- isMinifyEnabled = false
- proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"),
- "proguard-rules.pro")
- }
- }
-}
-dependencies {
- implementation(libs.kotlin.stdlib)
- implementation(libs.androidx.appcompat)
- implementation(libs.androidx.constraintlayout)
- testImplementation(libs.junit)
- androidTestImplementation(libs.junit)
- androidTestImplementation(libs.androidx.test.core)
- androidTestImplementation(libs.androidx.test.runner)
- androidTestImplementation(libs.androidx.test.espresso.core)
-}
diff --git a/shared/src/main/AndroidManifest.xml b/shared/src/main/AndroidManifest.xml
deleted file mode 100644
index 74b7379f7..000000000
--- a/shared/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/shared/src/main/res/drawable-v24/ic_launcher_foreground.xml b/shared/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index eadaa7ca4..000000000
--- a/shared/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/shared/src/main/res/mipmap-hdpi/ic_launcher.png b/shared/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 898f3ed59..000000000
Binary files a/shared/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/shared/src/main/res/mipmap-hdpi/ic_launcher_round.png b/shared/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index dffca3601..000000000
Binary files a/shared/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/shared/src/main/res/mipmap-mdpi/ic_launcher.png b/shared/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 64ba76f75..000000000
Binary files a/shared/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/shared/src/main/res/mipmap-mdpi/ic_launcher_round.png b/shared/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index dae5e0823..000000000
Binary files a/shared/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/shared/src/main/res/mipmap-xhdpi/ic_launcher.png b/shared/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index e5ed46597..000000000
Binary files a/shared/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/shared/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/shared/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 14ed0af35..000000000
Binary files a/shared/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/shared/src/main/res/mipmap-xxhdpi/ic_launcher.png b/shared/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index b0907cac3..000000000
Binary files a/shared/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/shared/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/shared/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index d8ae03154..000000000
Binary files a/shared/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/shared/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/shared/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 2c18de9e6..000000000
Binary files a/shared/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/shared/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/shared/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index beed3cdd2..000000000
Binary files a/shared/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/shared/src/main/res/values/colors.xml b/shared/src/main/res/values/colors.xml
deleted file mode 100644
index 69b22338c..000000000
--- a/shared/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
- #008577
- #00574B
- #D81B60
-
diff --git a/shared/src/main/res/values/strings.xml b/shared/src/main/res/values/strings.xml
deleted file mode 100644
index 51ebc2b9c..000000000
--- a/shared/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- snippets
-
diff --git a/shared/src/main/res/values/styles.xml b/shared/src/main/res/values/styles.xml
deleted file mode 100644
index 5885930df..000000000
--- a/shared/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
diff --git a/views/.gitignore b/views/.gitignore
new file mode 100644
index 000000000..42afabfd2
--- /dev/null
+++ b/views/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/views/README.md b/views/README.md
new file mode 100644
index 000000000..a9256a204
--- /dev/null
+++ b/views/README.md
@@ -0,0 +1 @@
+This is a sample project that contains the code snippets seen on https://android.devsite.corp.google.com/develop/ui/views
diff --git a/views/build.gradle.kts b/views/build.gradle.kts
new file mode 100644
index 000000000..80ee4edc8
--- /dev/null
+++ b/views/build.gradle.kts
@@ -0,0 +1,56 @@
+//Copyright 2024 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.
+
+plugins {
+ alias(libs.plugins.android.library)
+ alias(libs.plugins.kotlin.android)
+}
+
+android {
+ namespace = "com.example.example.snippet.views"
+ compileSdk = 35
+
+ defaultConfig {
+ minSdk = 35
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles("consumer-rules.pro")
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+}
+
+dependencies {
+
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.appcompat)
+ implementation(libs.google.android.material)
+ implementation(libs.androidx.glance.appwidget)
+
+}
\ No newline at end of file
diff --git a/views/consumer-rules.pro b/views/consumer-rules.pro
new file mode 100644
index 000000000..e69de29bb
diff --git a/views/proguard-rules.pro b/views/proguard-rules.pro
new file mode 100644
index 000000000..481bb4348
--- /dev/null
+++ b/views/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/views/src/main/AndroidManifest.xml b/views/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..65ba8e1e0
--- /dev/null
+++ b/views/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/views/src/main/java/com/example/example/snippet/views/appwidget/AppWidgetSnippets.kt b/views/src/main/java/com/example/example/snippet/views/appwidget/AppWidgetSnippets.kt
new file mode 100644
index 000000000..cd6ac4855
--- /dev/null
+++ b/views/src/main/java/com/example/example/snippet/views/appwidget/AppWidgetSnippets.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2024 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 com.example.example.snippet.views.appwidget
+
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProvider
+import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
+import android.content.Context
+import android.widget.RemoteViews
+import androidx.glance.GlanceId
+import androidx.glance.appwidget.GlanceAppWidget
+import androidx.glance.appwidget.compose
+import com.example.example.snippet.views.R
+
+class ExampleAppWidget:GlanceAppWidget() {
+ override suspend fun provideGlance(context: Context, id: GlanceId) {
+ TODO("Not yet implemented")
+ }
+
+}
+
+private object GeneratedPreviewWithoutGlance {
+
+ lateinit var appContext: Context
+
+ fun MyWidgetPreview() {
+ // [START android_view_appwidget_generatedpreview_with_remoteview]
+ AppWidgetManager.getInstance(appContext).setWidgetPreview(
+ ComponentName(
+ appContext,
+ ExampleAppWidgetReceiver::class.java
+ ),
+ AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
+ RemoteViews("com.example", R.layout.widget_preview)
+ )
+ // [END android_view_appwidget_generatedpreview_with_remoteview]
+ }
+
+ suspend fun MyGlanceWidgetPreview() {
+ // [START android_view_appwidget_generatedpreview_with_glance]
+ AppWidgetManager.getInstance(appContext).setWidgetPreview(
+ ComponentName(
+ appContext,
+ ExampleAppWidgetReceiver::class.java
+ ),
+ AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
+ ExampleAppWidget().compose(
+ context = appContext
+ ),
+ )
+
+ // [END android_view_appwidget_generatedpreview_with_glance]
+ }
+}
+
+class ExampleAppWidgetReceiver: AppWidgetProvider() {
+
+}
diff --git a/views/src/main/res/layout/widget_preview.xml b/views/src/main/res/layout/widget_preview.xml
new file mode 100644
index 000000000..c9f449bb1
--- /dev/null
+++ b/views/src/main/res/layout/widget_preview.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/wear/build.gradle.kts b/wear/build.gradle.kts
index 9725e5396..154e7d37e 100644
--- a/wear/build.gradle.kts
+++ b/wear/build.gradle.kts
@@ -6,7 +6,7 @@ plugins {
android {
namespace = "com.example.wear"
- compileSdk = 34
+ compileSdk = 35
defaultConfig {
applicationId = "com.example.wear"
@@ -55,6 +55,17 @@ dependencies {
implementation(libs.compose.ui.tooling)
implementation(libs.play.services.wearable)
+ implementation(libs.androidx.tiles)
+ implementation(libs.androidx.wear)
+ implementation(libs.androidx.protolayout)
+ implementation(libs.androidx.protolayout.material)
+ implementation(libs.androidx.protolayout.expression)
+ debugImplementation(libs.androidx.tiles.renderer)
+ testImplementation(libs.androidx.tiles.testing)
+ implementation(libs.androidx.wear.tooling.preview)
+ implementation(libs.androidx.tiles.tooling.preview)
+ debugImplementation(libs.androidx.tiles.tooling)
+ implementation(libs.guava)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.tooling.preview)
diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml
index f0d2328e2..84c4785a2 100644
--- a/wear/src/main/AndroidManifest.xml
+++ b/wear/src/main/AndroidManifest.xml
@@ -34,6 +34,24 @@