-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Add Recent Orders Widget #1567
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add Recent Orders Widget #1567
Conversation
Users list widget canonical layout from Platform Samples for resizing. Creates a generated widget preview, using APIs in Glance 1.2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello @ashnohe, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
Summary of Changes
Hello team, gemini-code-assist here to provide a summary of this pull request. This PR introduces a new "Recent Orders" widget to the Jetsnack application using Jetpack Glance. The implementation follows the canonical list widget layout patterns demonstrated in the Android Platform Samples, ensuring responsiveness across different widget sizes. It also leverages the new generated widget preview APIs available in Glance 1.2.
Highlights
- New Glance Widget: Adds a new App Widget,
RecentOrdersWidget
, implemented using Jetpack Glance, along with itsRecentOrdersWidgetReceiver
. - Responsive Layout: Implements a responsive layout (
ImageTextListLayout
) for the widget that adapts based on available size, displaying items in a list (with or without images) or a grid. - Glance 1.2 Preview API: Integrates the Glance 1.2 generated preview API, allowing the system to automatically generate previews for the widget in different sizes.
- Widget Data Handling: Includes a basic in-memory data repository (
RecentOrdersDataRepository
) to provide sample data for the widget and manage data per widget instance. - Deep Linking: Adds a deep link for the shopping cart screen to allow the widget's cart icon action to navigate directly to the cart.
Changelog
Click here to see the changelog
- Jetsnack/app/build.gradle.kts
- Added
androidx.glance:glance-appwidget
andandroidx.glance:glance-preview
dependencies. - Updated
androidx.glance
version to1.2.0-alpha01
.
- Added
- Jetsnack/app/src/main/AndroidManifest.xml
- Registered the
RecentOrdersWidgetReceiver
with the necessary intent filter and meta-data pointing to the widget info XML.
- Registered the
- Jetsnack/app/src/main/java/com/example/jetsnack/ui/MainActivity.kt
- Added logic in
onCreate
to callsetWidgetPreviews
on app startup for API 34+. - Implemented
setWidgetPreviews
function to useGlanceAppWidgetManager
to set generated widget previews if they are not already set.
- Added logic in
- Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/Home.kt
- Added a deep link (
https://jetsnack.example.com/home/cart
) to theHomeSections.CART
composable route.
- Added a deep link (
- Jetsnack/app/src/main/java/com/example/jetsnack/widget/ActionDemonstrationActivity.kt
- Added a new activity (
ActionDemonstrationActivity
) to serve as a target for widget actions, displaying a message indicating the action source.
- Added a new activity (
- Jetsnack/app/src/main/java/com/example/jetsnack/widget/CollectionsKtx.kt
- Added a
computeIfAbsent
extension function forMutableMap
.
- Added a
- Jetsnack/app/src/main/java/com/example/jetsnack/widget/RecentOrdersWidget.kt
- Added the
RecentOrdersWidget
class extendingGlanceAppWidget
, definingsizeMode
andpreviewSizeMode
. - Implemented
provideGlance
to load data from the repository and provide widget content usingImageTextListLayout
. - Implemented
WidgetContent
composable to build the widget UI based on data and size. - Implemented
providePreview
to provide content for generated previews. - Added the
RecentOrdersWidgetReceiver
class extendingGlanceAppWidgetReceiver
. - Implemented
onDeleted
in the receiver to clean up data associated with deleted widget instances.
- Added the
- Jetsnack/app/src/main/java/com/example/jetsnack/widget/data/RecentOrdersDataRepository.kt
- Added the
RecentOrdersDataRepository
class to simulate fetching and holding recent order data. - Defined
DemoDataItem
data class to represent sample order data. - Included companion object methods (
getImageTextListDataRepo
,cleanUp
) to manage repository instances per widget ID.
- Added the
- Jetsnack/app/src/main/java/com/example/jetsnack/widget/layout/EmptyListContent.kt
- Added
EmptyListContent
composable to display a 'No data' message and button when the order list is empty, usingNoDataContent
.
- Added
- Jetsnack/app/src/main/java/com/example/jetsnack/widget/layout/ImageTextListLayout.kt
- Added the
ImageTextListLayout
composable, the main layout for the widget. - Implemented logic to switch between list and grid views based on widget size (
ImageTextListLayoutSize
). - Defined
ImageTextListItemData
data class for list item content. - Added
FilledHorizontalListItem
composable to render individual items. - Included preview annotations and a preview composable for the layout.
- Added the
- Jetsnack/app/src/main/java/com/example/jetsnack/widget/layout/ListItem.kt
- Added a generic
ListItem
composable to structure content within a list item (leading, headline, supporting, trailing).
- Added a generic
- Jetsnack/app/src/main/java/com/example/jetsnack/widget/layout/NoDataContent.kt
- Added a generic
NoDataContent
composable to display a message and button for empty states.
- Added a generic
- Jetsnack/app/src/main/java/com/example/jetsnack/widget/layout/RoundedScrollingLazyColumn.kt
- Added
RoundedScrollingLazyColumn
composables, providing aLazyColumn
with rounded corners and optional item spacing.
- Added
- Jetsnack/app/src/main/java/com/example/jetsnack/widget/layout/RoundedScrollingLazyVerticalGrid.kt
- Added
RoundedScrollingLazyVerticalGrid
composables, providing aLazyVerticalGrid
with rounded corners and optional cell spacing.
- Added
- Jetsnack/app/src/main/java/com/example/jetsnack/widget/utils/ActionUtils.kt
- Added
ActionUtils
object with a helper functionactionStartDemoActivity
to create actions for launching the demo activity.
- Added
- Jetsnack/app/src/main/java/com/example/jetsnack/widget/utils/PreviewAnnotations.kt
- Added custom annotation classes (
SmallWidgetPreview
,MediumWidgetPreview
,LargeWidgetPreview
) for defining common widget preview sizes.
- Added custom annotation classes (
- Jetsnack/app/src/main/res/drawable/add_shopping_cart.xml
- Added a new vector drawable for an 'add shopping cart' icon.
- Jetsnack/app/src/main/res/drawable/shopping_cart.xml
- Added a new vector drawable for a 'shopping cart' icon.
- Jetsnack/app/src/main/res/drawable/widget_logo.xml
- Added a new vector drawable for the Jetsnack widget logo.
- Jetsnack/app/src/main/res/values-xlarge/dimens.xml
- Added dimension and integer resources defining target cell size, min/max size, and min/max resize size for the widget on large screens (tablets).
- Jetsnack/app/src/main/res/values/dimens.xml
- Added dimension and integer resources defining target cell size, min/max size, and min/max resize size for the widget on handheld devices.
- Jetsnack/app/src/main/res/values/strings.xml
- Added string resources for the widget title, name, description, shopping cart button label, and sample no data/add button text.
- Jetsnack/app/src/main/res/xml/snack_order_widget_info.xml
- Added the
appwidget-provider
XML file, configuring the widget's description, initial layout, size constraints (min/max, resize), resize mode, preview image, and category.
- Added the
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command>
or @gemini-code-assist <command>
. Below is a summary of the supported commands.
Feature | Command | Description |
---|---|---|
Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/
folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
A widget appears,
Showing snacks from past years,
Glance makes it bright,
Adapting to light,
Your orders, now free from all fears.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces a "Recent Orders Widget" using Android Glance, which is a significant feature addition. The implementation leverages canonical layouts from platform samples and new Glance 1.2 APIs for widget previews.
Overall, the structure is well-organized, and the use of Glance features like SizeMode.Exact
, responsive preview sizes, and per-widget data repositories (RecentOrdersDataRepository
) is commendable. The new layout components (ImageTextListLayout
, ListItem
, etc.) are modular and follow good Compose/Glance practices.
I've identified a critical issue related to image resource handling that could lead to a crash, and a few medium-severity issues concerning package naming, hardcoded strings, and the use of a custom collection extension where a standard library alternative exists. Addressing these will improve the robustness and maintainability of the new widget.
Additionally, there are minor points regarding copyright year consistency and newlines at the end of files, which are good to address for overall code hygiene. I'll also inquire about a potentially missing drawable referenced in the widget's XML configuration.
Summary of Findings
- Critical: Image Resource Handling: In
ImageTextListLayout.kt
, the conversionitem.supportingImage.toString().toInt()
forImageProvider
can cause aNumberFormatException
ifitem.supportingImage
isnull
. This needs to be handled safely. - Medium: Package Naming Inconsistency:
ActionDemonstrationActivity.kt
uses a package name from platform samples (com.example.platform...
). It should be moved to a package within the app's namespace (e.g.,com.example.jetsnack.widget.ui
). - Medium: Hardcoded String: In
RecentOrdersDataRepository.kt
, thetrailingIconButtonContentDescription
is hardcoded. It should be extracted to a string resource for localization and maintainability. - Medium: Custom
computeIfAbsent
Extension: The customcomputeIfAbsentExt
inCollectionsKtx.kt
(used inRecentOrdersDataRepository.kt
) can be replaced by the standard library'sMutableMap.getOrPut
function, which is more idiomatic and reduces custom code. - Low: Copyright Year and EOF Newlines: Many new files have a copyright year of 2025 and are missing a newline at the end of the file. These should be updated for consistency.
- Query: Missing Preview Drawable: The
snack_order_widget_info.xml
references@drawable/backward_compatible_widget_preview
. This drawable is not in the PR. Is it an existing asset or needs to be added for compatibility with older Android versions?
Merge Readiness
This pull request adds valuable widget functionality. However, due to the critical issue identified in image handling and several medium-severity concerns, I recommend that these changes be addressed before merging. Specifically, the potential crash in ImageTextListLayout.kt
must be fixed. Addressing the other points will also improve the overall quality and maintainability of the codebase. As an AI, I am not authorized to approve pull requests; please ensure further review and approval from team members after the suggested changes are made.
Image( | ||
provider = ImageProvider(item.supportingImage.toString().toInt()), | ||
// contentDescription is null because in this sample, it serves merely as a visual; but if |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a potential crash here if item.supportingImage
is null
. The supportingImage
in ImageTextListItemData
is an Int?
. If it's null
, item.supportingImage.toString()
will result in the string "null", and then toInt()
will throw a NumberFormatException
.
To fix this, you should safely unwrap the nullable Int
and only create the ImageProvider
and Image
composable if it's not null.
How about something like this?
item.supportingImage?.let {
Image(
provider = ImageProvider(it),
// ... other parameters
)
}
This would also mean the SupportingImage
composable itself might need to conditionally render the Image
.
provider = ImageProvider(item.supportingImage ?: 0), // Fallback to a default or handle null appropriately
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.example.platform.ui.appwidgets.glance.layout |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The package name com.example.platform.ui.appwidgets.glance.layout
seems to be from the platform samples. For better project organization and consistency, would it be more appropriate to move this activity to a package within the com.example.jetsnack
namespace, perhaps something like com.example.jetsnack.widget.ui
or com.example.jetsnack.ui.widget
?
trailingIconButton = R.drawable.add_shopping_cart, | ||
trailingIconButtonContentDescription = "Add to Shopping Cart", | ||
snackKeys = item.snackKeys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The trailingIconButtonContentDescription
is hardcoded as "Add to Shopping Cart". For better internationalization and maintainability, should this be a string resource? For example, context.getString(R.string.add_to_cart_content_description)
.
trailingIconButton = R.drawable.add_shopping_cart, | |
trailingIconButtonContentDescription = "Add to Shopping Cart", | |
snackKeys = item.snackKeys | |
trailingIconButtonContentDescription = LocalContext.current.getString(R.string.add_to_cart_content_description), // Assuming you add this string resource |
return synchronized(repositories) { | ||
repositories.computeIfAbsentExt(glanceId) { RecentOrdersDataRepository() }!! | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using the standard library's getOrPut
function for MutableMap
here. It achieves the same result as computeIfAbsentExt
for this use case (getting or creating a RecentOrdersDataRepository
) and is more idiomatic in Kotlin. This would also allow for the removal of the custom computeIfAbsentExt
extension from CollectionsKtx.kt
.
For example:
repositories.getOrPut(glanceId) { RecentOrdersDataRepository() }
Since RecentOrdersDataRepository()
is non-null, the !!
operator would no longer be necessary.
repositories.getOrPut(glanceId) { RecentOrdersDataRepository() }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor comments but feel free to merge once fixed
|
||
class MainActivity : ComponentActivity() { | ||
override fun onCreate(savedInstanceState: Bundle?) { | ||
enableEdgeToEdge() | ||
super.onCreate(savedInstanceState) | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { | ||
lifecycleScope.launch(Dispatchers.Default) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this ok on a background thread? Noob widget question
|
||
package com.example.jetsnack.widget | ||
|
||
fun <K, V> MutableMap<K,V>.computeIfAbsent(key : K, calulation :(K)->V):V? { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in calculation
|
||
package com.example.jetsnack.widget | ||
|
||
fun <K, V> MutableMap<K,V>.computeIfAbsent(key : K, calulation :(K)->V):V? { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this needed? You could probably use getOrPut
https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/get-or-put.html#
@@ -12,7 +12,7 @@ androidx-compose-bom = "2025.05.00" | |||
androidx-constraintlayout = "1.1.1" | |||
androidx-core-splashscreen = "1.0.1" | |||
androidx-corektx = "1.16.0" | |||
androidx-glance = "1.1.1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need up update the central toml file in scripts
or else this will be overriden by the next sample update
Uses list widget canonical layout from Platform Samples for resizing. Platform Samples Canonical Widget Layouts: https://github.com/android/platform-samples/tree/main/samples/user-interface/appwidgets
Creates a generated widget preview, using APIs in Glance 1.2: https://developer.android.com/jetpack/androidx/releases/glance#1.2.0-alpha01