Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: keelim/compose-samples
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: android/compose-samples
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Able to merge. These branches can be automatically merged.
  • 16 commits
  • 24 files changed
  • 5 contributors

Commits on Mar 7, 2025

  1. Fix Wear navigation startDestination

    Also add a unit test covering activity startup and navigation
    yschimke committed Mar 7, 2025
    Copy the full SHA
    38733f6 View commit details
  2. fix formatting

    yschimke committed Mar 7, 2025
    Copy the full SHA
    07f4d2b View commit details
  3. Fix Wear navigation startDestination (android#1538)

    Also add a unit test covering activity startup and navigation
    yschimke authored Mar 7, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    3e509ff View commit details

Commits on Mar 17, 2025

  1. enable predictive back

    trambui09 committed Mar 17, 2025
    Copy the full SHA
    1265bf8 View commit details

Commits on Mar 20, 2025

  1. Adding Glance to Jetcaster Readme

    Summers Pittman committed Mar 20, 2025
    Copy the full SHA
    0d08da5 View commit details
  2. cleanup minor size issues

    Summers Pittman committed Mar 20, 2025
    Copy the full SHA
    a209fe0 View commit details
  3. Tweaking small widget and landscape layouts

    Summers Pittman committed Mar 20, 2025
    Copy the full SHA
    12ff20e View commit details

Commits on Mar 21, 2025

  1. Adding Dynamic color, removing unused field to disable

    Summers Pittman committed Mar 21, 2025
    Copy the full SHA
    86eae5e View commit details
  2. Generated Preview and Preview Layout

    Summers Pittman committed Mar 21, 2025
    Copy the full SHA
    1dda6e9 View commit details
  3. SpotlessApply

    Summers Pittman committed Mar 21, 2025
    Copy the full SHA
    d30ebbc View commit details
  4. fix tint issue

    Summers Pittman committed Mar 21, 2025
    Copy the full SHA
    1698f4d View commit details
  5. Add comment to widget_preview

    Summers Pittman committed Mar 21, 2025
    Copy the full SHA
    5349056 View commit details
  6. removing runBlocking

    Summers Pittman committed Mar 21, 2025
    Copy the full SHA
    2c6678f View commit details
  7. Moving updateWidgetPreview coroutine out of MainActivity

    Summers Pittman committed Mar 21, 2025
    Copy the full SHA
    8cf60ea View commit details

Commits on Mar 25, 2025

  1. [Reply] Enable predictive back (android#1540)

    Enable predictive back in the Reply sample
    trambui09 authored Mar 25, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    85e61a2 View commit details

Commits on Mar 27, 2025

  1. Widget preview updates (android#1541)

    This PR
     * Adds a widget preview layout
     *  Add a widget preview Image
     * Adds a generated widget preview
    * Cleans up some widget layout issues on denser homescreens and in
    landscape
    secondsun authored Mar 27, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    57913c5 View commit details
Showing with 477 additions and 48 deletions.
  1. +1 −0 Jetcaster/README.md
  2. +1 −0 Jetcaster/glancewidget/build.gradle.kts
  3. +76 −38 Jetcaster/glancewidget/src/main/java/com/example/jetcaster/glancewidget/JetcasterAppWidget.kt
  4. +116 −0 Jetcaster/glancewidget/src/main/java/com/example/jetcaster/glancewidget/JetcasterAppWidgetPreview.kt
  5. +1 −1 Jetcaster/glancewidget/src/main/res/drawable/outline_play_arrow_24.xml
  6. BIN Jetcaster/glancewidget/src/main/res/drawable/widget_preview.png
  7. +20 −0 Jetcaster/glancewidget/src/main/res/drawable/widget_preview_image_shape.xml
  8. BIN Jetcaster/glancewidget/src/main/res/drawable/widget_preview_thumbnail.png
  9. +55 −0 Jetcaster/glancewidget/src/main/res/layout/widget_preview.xml
  10. +4 −0 Jetcaster/glancewidget/src/main/res/values-h48dp/sizes.xml
  11. +19 −0 Jetcaster/glancewidget/src/main/res/values-night-v31/colors.xml
  12. +19 −0 Jetcaster/glancewidget/src/main/res/values-night/colors.xml
  13. +19 −0 Jetcaster/glancewidget/src/main/res/values-v31/colors.xml
  14. +22 −0 Jetcaster/glancewidget/src/main/res/values-v31/styles.xml
  15. +23 −0 Jetcaster/glancewidget/src/main/res/values/colors.xml
  16. +4 −0 Jetcaster/glancewidget/src/main/res/values/sizes.xml
  17. +22 −0 Jetcaster/glancewidget/src/main/res/values/styles.xml
  18. +8 −2 Jetcaster/glancewidget/src/main/res/xml/jetcaster_info.xml
  19. +2 −0 Jetcaster/gradle/libs.versions.toml
  20. +2 −1 Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/MainActivity.kt
  21. +7 −1 Jetcaster/wear/src/main/java/com/example/jetcaster/MainActivity.kt
  22. +3 −5 Jetcaster/wear/src/main/java/com/example/jetcaster/WearApp.kt
  23. +52 −0 Jetcaster/wear/src/test/java/com/example/jetcaster/NavigationTest.kt
  24. +1 −0 Reply/app/src/main/AndroidManifest.xml
1 change: 1 addition & 0 deletions Jetcaster/README.md
Original file line number Diff line number Diff line change
@@ -134,6 +134,7 @@ limitations under the License.
[epstore]: mobile/src/main/java/com/example/jetcaster/data/EpisodeStore.kt
[catstore]: mobile/src/main/java/com/example/jetcaster/data/CategoryStore.kt
[db]: mobile/src/main/java/com/example/jetcaster/data/room/JetcasterDatabase.kt
[glance]: https://developer.android.com/develop/ui/compose/glance
[homevm]: mobile/src/main/java/com/example/jetcaster/ui/home/HomeViewModel.kt
[homeui]: mobile/src/main/java/com/example/jetcaster/ui/home/Home.kt
[compose]: https://developer.android.com/jetpack/compose
1 change: 1 addition & 0 deletions Jetcaster/glancewidget/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -48,6 +48,7 @@ dependencies {
implementation(libs.coil.kt.compose)

implementation(libs.androidx.core.ktx)
implementation(libs.android.material3)
implementation(libs.androidx.lifecycle.runtime)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
Original file line number Diff line number Diff line change
@@ -22,8 +22,6 @@ import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.net.Uri
import android.util.Log
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -32,6 +30,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
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
@@ -59,7 +58,6 @@ import androidx.glance.layout.Spacer
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.padding
import androidx.glance.layout.size
import androidx.glance.material3.ColorProviders
import androidx.glance.text.FontWeight
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
@@ -70,7 +68,7 @@ import coil.request.ImageRequest
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

internal val TAG = "JetcasterAppWidegt"
internal val TAG = "JetcasterAppWidget"

/**
* Implementation of App Widget functionality.
@@ -85,28 +83,32 @@ data class JetcasterAppWidgetViewState(
val podcastTitle: String,
val isPlaying: Boolean,
val albumArtUri: String,
val useDynamicColor: Boolean
)

private object Sizes {
val short = 72.dp
val minWidth = 140.dp
val smallBucketCutoffWidth = 250.dp // anything from minWidth to this will have no title

val imageNormal = 80.dp
val imageCondensed = 60.dp
val normal = 80.dp
val medium = 56.dp
val condensed = 48.dp
}

private enum class SizeBucket { Invalid, Narrow, Normal }
private enum class SizeBucket { Invalid, Narrow, Normal, NarrowShort, NormalShort }

@Composable
private fun calculateSizeBucket(): SizeBucket {
val size: DpSize = LocalSize.current
val width = size.width
val height = size.height

return when {
width < Sizes.minWidth -> SizeBucket.Invalid
width <= Sizes.smallBucketCutoffWidth -> SizeBucket.Narrow
else -> SizeBucket.Normal
width <= Sizes.smallBucketCutoffWidth ->
if (height >= Sizes.short) SizeBucket.Narrow else SizeBucket.NarrowShort
else ->
if (height >= Sizes.short) SizeBucket.Normal else SizeBucket.NormalShort
}
}

@@ -122,29 +124,39 @@ class JetcasterAppWidget : GlanceAppWidget() {
podcastTitle = "Now in Android",
isPlaying = false,
albumArtUri = "https://static.libsyn.com/p/assets/9/f/f/3/" +
"9ff3cb5dc6cfb3e2e5bbc093207a2619/NIA000_PodcastThumbnail.png",
useDynamicColor = false
"9ff3cb5dc6cfb3e2e5bbc093207a2619/NIA000_PodcastThumbnail.png"
)

provideContent {
val sizeBucket = calculateSizeBucket()
val playPauseIcon = if (testState.isPlaying) PlayPauseIcon.Pause else PlayPauseIcon.Play
val artUri = Uri.parse(testState.albumArtUri)

GlanceTheme(
colors = ColorProviders(
light = lightColorScheme(),
dark = darkColorScheme()
)
) {
GlanceTheme {
when (sizeBucket) {
SizeBucket.Invalid -> WidgetUiInvalidSize()
SizeBucket.Narrow -> WidgetUiNarrow(
SizeBucket.Narrow -> Widget(
iconSize = Sizes.medium,
imageUri = artUri,
playPauseIcon = playPauseIcon
)

SizeBucket.Normal -> WidgetUiNormal(
iconSize = Sizes.normal,
title = testState.episodeTitle,
subtitle = testState.podcastTitle,
imageUri = artUri,
playPauseIcon = playPauseIcon
)

SizeBucket.NarrowShort -> Widget(
iconSize = Sizes.condensed,
imageUri = artUri,
playPauseIcon = playPauseIcon
)

SizeBucket.NormalShort -> WidgetUiNormal(
iconSize = Sizes.condensed,
title = testState.episodeTitle,
subtitle = testState.podcastTitle,
imageUri = artUri,
@@ -162,20 +174,23 @@ private fun WidgetUiNormal(
subtitle: String,
imageUri: Uri,
playPauseIcon: PlayPauseIcon,
iconSize: Dp,
) {
Scaffold(titleBar = {} /* title bar will be optional starting in glance 1.1.0-beta3*/) {

Scaffold {
Row(
GlanceModifier.fillMaxSize(), verticalAlignment = Alignment.Vertical.CenterVertically
) {
AlbumArt(imageUri, GlanceModifier.size(Sizes.imageNormal))
AlbumArt(imageUri, GlanceModifier.size(iconSize))
PodcastText(title, subtitle, modifier = GlanceModifier.padding(16.dp).defaultWeight())
PlayPauseButton(playPauseIcon, {})
PlayPauseButton(GlanceModifier.size(iconSize), playPauseIcon, {})
}
}
}

@Composable
private fun WidgetUiNarrow(
private fun Widget(
iconSize: Dp,
imageUri: Uri,
playPauseIcon: PlayPauseIcon,
) {
@@ -184,9 +199,9 @@ private fun WidgetUiNarrow(
modifier = GlanceModifier.fillMaxSize(),
verticalAlignment = Alignment.Vertical.CenterVertically
) {
AlbumArt(imageUri, GlanceModifier.size(Sizes.imageCondensed))
AlbumArt(imageUri, GlanceModifier.size(iconSize))
Spacer(GlanceModifier.defaultWeight())
PlayPauseButton(playPauseIcon, {})
PlayPauseButton(GlanceModifier.size(iconSize), playPauseIcon, {})
}
}
}
@@ -209,22 +224,44 @@ private fun AlbumArt(
@Composable
fun PodcastText(title: String, subtitle: String, modifier: GlanceModifier = GlanceModifier) {
val fgColor = GlanceTheme.colors.onPrimaryContainer
Column(modifier) {
Text(
text = title,
style = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.Medium, color = fgColor),
maxLines = 2,
)
Text(
text = subtitle,
style = TextStyle(fontSize = 14.sp, color = fgColor),
maxLines = 2,
)
val size = LocalSize.current
when {
size.height >= Sizes.short -> Column(modifier) {
Text(
text = title,
style = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
color = fgColor
),
maxLines = 2,
)
Text(
text = subtitle,
style = TextStyle(fontSize = 14.sp, color = fgColor),
maxLines = 2,
)
}
else -> Column(modifier) {
Text(
text = title,
style = TextStyle(
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
color = fgColor
),
maxLines = 1,
)
}
}
}

@Composable
private fun PlayPauseButton(state: PlayPauseIcon, onClick: () -> Unit) {
private fun PlayPauseButton(
modifier: GlanceModifier = GlanceModifier.size(Sizes.normal),
state: PlayPauseIcon,
onClick: () -> Unit
) {
val (iconRes: Int, description: Int) = when (state) {
PlayPauseIcon.Play -> R.drawable.outline_play_arrow_24 to R.string.content_description_play
PlayPauseIcon.Pause -> R.drawable.outline_pause_24 to R.string.content_description_pause
@@ -234,7 +271,8 @@ private fun PlayPauseButton(state: PlayPauseIcon, onClick: () -> Unit) {
val contentDescription = LocalContext.current.getString(description)

SquareIconButton(
provider,
modifier = modifier,
imageProvider = provider,
contentDescription = contentDescription,
onClick = onClick
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* 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.jetcaster.glancewidget

import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProviderInfo
import android.content.ComponentName
import android.content.Context
import android.os.Build
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.Image
import androidx.glance.ImageProvider
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.SizeMode
import androidx.glance.appwidget.components.Scaffold
import androidx.glance.appwidget.components.SquareIconButton
import androidx.glance.appwidget.compose
import androidx.glance.appwidget.provideContent
import androidx.glance.layout.Alignment
import androidx.glance.layout.Row
import androidx.glance.layout.Spacer
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.size
import androidx.glance.layout.wrapContentSize
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

private object SizesPreview {
val medium = 56.dp
}

/**
* This is a convenience function for updating the widget preview using Generated Previews.
*
* In a real application, this would be called whenever the widget's state changes.
*/
fun updateWidgetPreview(context: Context) {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
CoroutineScope(Dispatchers.IO).launch {
try {
val appwidgetManager = AppWidgetManager.getInstance(context)

appwidgetManager.setWidgetPreview(
ComponentName(context, JetcasterAppWidgetReceiver::class.java),
AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
JetcasterAppWidgetPreview().compose(
context,
size = DpSize(160.dp, 64.dp)
),
)
} catch (e: Exception) {
Log.e(TAG, e.message, e)
}
}
}
}

class JetcasterAppWidgetPreview : GlanceAppWidget() {
override val sizeMode: SizeMode
get() = SizeMode.Exact

override suspend fun provideGlance(context: Context, id: GlanceId) {

provideContent {
GlanceTheme {
Widget()
}
}
}
}

@Composable
private fun Widget() {

Scaffold {
Row(
modifier = GlanceModifier.fillMaxSize(),
verticalAlignment = Alignment.Vertical.CenterVertically
) {
Image(
modifier = GlanceModifier.wrapContentSize().size(SizesPreview.medium),
provider = ImageProvider(R.drawable.widget_preview_thumbnail),
contentDescription = ""
)
Spacer(GlanceModifier.defaultWeight())
SquareIconButton(
modifier = GlanceModifier.size(SizesPreview.medium),
imageProvider = ImageProvider(R.drawable.outline_play_arrow_24),
contentDescription = "",
onClick = { }
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">

<path android:fillColor="@android:color/white" android:pathData="M320,760L320,200L760,480L320,760ZM400,480L400,480L400,480L400,480ZM400,614L610,480L400,346L400,614Z"/>

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 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
~
~ http://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.
-->

<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="16dp"/>
</shape>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading