Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3ad4428
Migrate Compose Glance & Navigation snippets
kkuan2011 Aug 4, 2026
d79afbb
Existing code compiled. but was not enough elegant for a snippet on DAC.
hamen Aug 12, 2026
b2e92f3
To keep the snippet short, similarly to the original one, I added an …
hamen Aug 12, 2026
27b6a2c
This is not a code snippet. This is an API reference, we can find [he…
hamen Aug 12, 2026
ac2042c
Cleaned up and made it a bit more Kotlin idiomatic.
hamen Aug 12, 2026
e0607f6
Remove the Fragment snippet as we only want to focus on Compose.
hamen Aug 12, 2026
2f2160c
Optimized imports and replace a deprecated call with the suggested one.
hamen Aug 12, 2026
8dba068
Removed the MyAlertDialogFragment as we decided we don't export old V…
hamen Aug 12, 2026
3fa4407
Remove unnecessary file
hamen Aug 14, 2026
0a402e9
Add missing copyright
hamen Aug 14, 2026
f42b3c1
Remove log entry
hamen Aug 14, 2026
57f5fb4
Optimize imports
hamen Aug 14, 2026
ac28a97
Optimize imports in EdgeToEdgeSnippets.kt
hamen Aug 14, 2026
5f98093
Optimize imports
hamen Aug 14, 2026
ba22e49
Add missing throwable parameter
hamen Aug 14, 2026
c9adfd8
Merge pull request #1 from StellarElements/katherinekuan/migrate-dac-…
hamen Aug 14, 2026
141d872
Merge branch 'android:main' into main
hamen Aug 17, 2026
51d6430
Add silent to the START_EXCLUDE tag
hamen Aug 18, 2026
072db4d
Merge branch 'main' into main
hamen Aug 18, 2026
341e9bd
Merge branch 'main' into main
hamen Aug 18, 2026
4e4e374
End the snippet comments with a period
hamen Aug 20, 2026
2183a02
Merge branch 'main' into main
hamen Aug 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,18 @@
package com.example.compose.snippets.glance

import android.annotation.SuppressLint
import androidx.annotation.RequiresApi
import android.app.Activity
import android.app.Application
import android.app.PendingIntent
import android.app.Service
import android.appwidget.AppWidgetManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.widget.RemoteViews
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.material3.ColorScheme
Expand All @@ -41,6 +44,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.glance.Button
import androidx.glance.ColorFilter
import androidx.glance.GlanceId
Expand Down Expand Up @@ -86,13 +90,15 @@ import androidx.glance.layout.Row
import androidx.glance.layout.RowScope
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
import androidx.glance.layout.width
import androidx.glance.layout.height
import androidx.glance.layout.padding
import androidx.glance.material3.ColorProviders
import androidx.glance.preview.ExperimentalGlancePreviewApi
import androidx.glance.preview.Preview
import androidx.glance.text.FontFamily
import androidx.glance.text.FontWeight
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.example.compose.snippets.MyActivity
Expand All @@ -114,7 +120,7 @@ private object GlanceCreateAppWidgetSnippet01 {
private object GlanceCreateAppWidgetSnippet02 {
// [START android_compose_glance_receiver02]
class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {

// Let MyAppWidgetReceiver know which GlanceAppWidget to use
override val glanceAppWidget: GlanceAppWidget = MyAppWidget()
}
Expand Down Expand Up @@ -518,7 +524,9 @@ object BuildUIWithGlance {

LazyColumn {
// [START android_compose_glance_buildUI06]
items(items = peopleList, itemId = { person -> person.id.hashCode().toLong() }) { person ->
items(
items = peopleList,
itemId = { person -> person.id.hashCode().toLong() }) { person ->
Text(person.name)
}
// [END android_compose_glance_buildUI06]
Expand Down Expand Up @@ -684,6 +692,7 @@ object CompoundButton {
)
// [END android_compose_glance_buildUI12]
}

// [START android_compose_glance_buildUI13]
class MyAppWidget : GlanceAppWidget() {

Expand Down Expand Up @@ -749,7 +758,7 @@ object CompoundButton {
uncheckedColor = ColorProvider(day = Color.Red, night = Color.Blue)
),

)
)
// [END android_compose_glance_buildUI14]
}

Expand Down Expand Up @@ -791,7 +800,7 @@ object GlanceTheming {
// [START android_compose_glance_glancetheming06]
// Remember, use the Glance imports
// import androidx.glance.material3.ColorProviders

// Example Imports from your own app
// import com.example.myapp.ui.theme.DarkColors
// import com.example.myapp.ui.theme.LightColors
Expand All @@ -803,6 +812,7 @@ object GlanceTheming {
dark = DarkColors
)
}

// [END android_compose_glance_glancetheming06]
// [START android_compose_glance_glancetheming02]
override suspend fun provideGlance(context: Context, id: GlanceId) {
Expand Down Expand Up @@ -1082,3 +1092,113 @@ class SyncService : Service() {
fun SnapScrollLayoutPreview() {
SnapScrollingSnippet.SnapScrollLayout()
}

private object GlanceHandleTextSnippet {
@Composable
fun TextExample() {
// [START android_compose_glance_handle_text]
Text(
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
fontFamily = FontFamily.Monospace
),
text = "Example Text"
)
// [END android_compose_glance_handle_text]
}
}

private object GlanceErrorHandlingSnippets {

class ErrorHandlingTryCatchWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {
// [START android_compose_glance_error_try_catch]
provideContent {
var isError = false
var data = listOf<String>()
try {
val repository = (context.applicationContext as MyApplication).myRepository
data = repository.loadData()
} catch (e: Exception) {
isError = true
// TODO: handle error
}

if (isError) {
ErrorView()
} else {
Content(data)
}
}
// [END android_compose_glance_error_try_catch]
}
}

// [START android_compose_glance_error_layout_param]
class UpgradeWidget : GlanceAppWidget(errorUiLayout = R.layout.error_layout) {
// [START_EXCLUDE]
override suspend fun provideGlance(context: Context, id: GlanceId) {}
// [END_EXCLUDE]
}
// [END android_compose_glance_error_layout_param]

class CustomErrorHandlingWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {}

// [START android_compose_glance_on_composition_error_override]
override fun onCompositionError(
context: Context, glanceId: GlanceId, appWidgetId: Int, throwable: Throwable
) {
val rv = RemoteViews(context.packageName, R.layout.error_layout)
rv.setTextViewText(
R.id.error_text_view,
"Error was thrown. \nThis is a custom view \nError Message: `${throwable.message}`"
)
rv.setOnClickPendingIntent(R.id.error_icon, getErrorIntent(context, throwable))
AppWidgetManager.getInstance(context).updateAppWidget(appWidgetId, rv)
}
// [END android_compose_glance_on_composition_error_override]

// [START android_compose_glance_error_get_intent]
private fun getErrorIntent(context: Context, throwable: Throwable): PendingIntent {
val intent = Intent(context, UpgradeToHelloWorldPro::class.java)
intent.action = "widgetError"
// [START_EXCLUDE silent]
intent.putExtra("error_message", throwable.message)
// [END_EXCLUDE]
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
}
// [END android_compose_glance_error_get_intent]
}

class ErrorHandlingWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = CustomErrorHandlingWidget()

// [START android_compose_glance_error_on_receive]
override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
Log.e("ErrorOnClick", "Button was clicked.")
}
// [END android_compose_glance_error_on_receive]
}

private class MyRepo {
fun loadData(): List<String> = listOf("item1", "item2")
}

private class MyApplication : Application() {
val myRepository = MyRepo()
}

@Composable
private fun ErrorView() {
}

@Composable
private fun Content(data: List<String>) {
}

private class UpgradeToHelloWorldPro
}

Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@ import androidx.activity.BackEventCompat
import androidx.activity.compose.PredictiveBackHandler
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.animate
import androidx.compose.animation.scaleOut
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
Expand All @@ -33,21 +36,26 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.util.VelocityTracker
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import kotlin.coroutines.cancellation.CancellationException
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
import kotlin.coroutines.cancellation.CancellationException

@Serializable data object Home
@Serializable data object Settings
Expand Down Expand Up @@ -200,3 +208,58 @@ private enum class DrawerState {
}

private val DrawerWidth = 300.dp

@Composable
private fun PredictiveBackHandlerFlowExample(isBackHandlerEnabled: Boolean) {
// [START android_predictive_back_handler_progress]
PredictiveBackHandler(enabled = isBackHandlerEnabled) { progress: Flow<BackEventCompat> ->
try {
progress.collect { backEvent ->
// Update your UI or animation based on backEvent.progress
}
// Handle the final back action (e.g., navigate back)
} catch (e: CancellationException) {
// Back gesture was cancelled, reset your UI
}
}
// [END android_predictive_back_handler_progress]
}

// [START android_predictive_back_handler_custom_transition]
@Composable
fun DetailScreen(onBack: () -> Unit) {
var scale by remember { mutableFloatStateOf(1f) }
var xOffset by remember { mutableFloatStateOf(0f) }
val scope = rememberCoroutineScope()

PredictiveBackHandler { progressFlow ->
try {
progressFlow.collectLatest { backEvent ->
scale = 1f - backEvent.progress
xOffset = backEvent.progress * 100f
}
// User completed gesture
onBack()
} catch (e: CancellationException) {
// User cancelled gesture
// Animate scale and xOffset back to 1f and 0f respectively
scope.launch {
animate(scale, 1f) { value, _ -> scale = value }
}
scope.launch {
animate(xOffset, 0f) { value, _ -> xOffset = value }
}
}
}
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Surface(
modifier = Modifier
.size(200.dp)
.scale(scale)
.offset { IntOffset(x = xOffset.dp.roundToPx(), y = 0) },
Comment thread
kkuan2011 marked this conversation as resolved.
color = Color.Blue
) {}
}
}
// [END android_predictive_back_handler_custom_transition]

Loading
Loading