Budget Tracker Widget
An Android home-screen widget that shows actual spend vs. budget: an overall progress bar (color-coded green/amber/red) plus a per-category breakdown chart, rendered for 7 sample budget items.
Opening the project
- Open Android Studio → Open → select the
BudgetTrackerWidgetfolder. - Let Gradle sync (Android Studio will fetch Gradle 8.7 per
gradle/wrapper/gradle-wrapper.properties— no wrapper jar is bundled, so the first sync needs an internet connection). - Run the
appmodule on an emulator or device (API 26+). - Long-press the home screen → Widgets → find Budget Tracker → drag it onto the home screen.
How it's wired together
BudgetItem.kt— data model for one budget line (category, budgeted, spent).BudgetRepository.kt— the single data source.fetchBudgetData()is a placeholder that simulates a network call and returns 7 sample categories (Rent, Groceries, Dining Out, Transport, Utilities, Entertainment, Savings) mixing under-, at-, and over-budget items. A commented-outparseBudgetJson()shows how to parse a real API response — swap the body offetchBudgetData()for an OkHttp/Retrofit/Ktor call when you have a real endpoint, and nothing else in the app needs to change.BudgetColors.kt— the single green/amber/red threshold definition, shared by the chart renderer, the widget's progress bar, and the app's progress bar, so status colors are always consistent.BudgetChartRenderer.kt— draws the category bar chart onto aBitmapusingCanvas. Widgets can't host arbitrary custom Views (RemoteViews only supports a fixed set of native widgets), so this is the standard way to get a custom visualization into a widget: draw it yourself, then push the bitmap into anImageView. The app screen reuses this exact same renderer, so the in-app view and the widget are pixel-identical, just at different sizes.WidgetLayoutPlanner.kt— pure logic that turns a widget's current width/height (dp) into a display plan: whether to show the percent label, whether there's room for the chart at all, how many category rows fit, and whether text should shrink for narrow widths.BudgetWidgetProvider.kt— theAppWidgetProvider. Reads the widget's live size fromonAppWidgetOptionsChanged()(fired whenever the user drags the resize handles) as well asonUpdate(), feeds it throughWidgetLayoutPlanner, and buildsRemoteViewsaccordingly — hiding the chart entirely at very small sizes, showing only the most over-budget categories (with a "+N more" footer) when space is tight, and sizing the rendered bitmap to match.widget_budget.xml— the widget layout: title, total, percent label, three stacked (mutually-exclusive-visibility)ProgressBars for the green/amber/red states, anImageViewfor the chart bitmap, and a "+N more" footer.MainActivity.kt/activity_main.xml— a full-screen version of the same card: same background, same title/total/percent, a singleProgressBartinted at runtime viaBudgetColors, and the same chart bitmap (rendered larger, showing every category since there's no space constraint).
Flexible widget sizing
The widget can be resized from a small ~2×1 cell (just title, total, and progress bar) up to a full card with the complete chart:
minWidth/minHeightinbudget_widget_info.xmlset the default placed size;minResizeWidth/minResizeHeightset how small it can be dragged.- At runtime,
onAppWidgetOptionsChanged()reports the new size, andWidgetLayoutPlannerdecides what fits — hiding the percent label and chart below certain heights, shrinking text below a certain width, and showing the categories closest to (or over) budget first when not all 7 fit. - Try dragging the widget to different sizes on the home screen to see it adapt live.
Extending it
- Real data: replace
BudgetRepository.fetchBudgetData()'s body with an actual HTTP call; keep the return typeList<BudgetItem>and everything downstream (widget + activity) keeps working unchanged. - Refresh cadence:
updatePeriodMillisinbudget_widget_info.xmlis set to 30 minutes (Android's minimum useful floor is ~30 min for this mechanism). For more frequent updates, triggerACTION_APPWIDGET_UPDATEyourself from aWorkManagerperiodic job. - Tap-to-open categories:
setOnClickPendingIntentis only wired to the whole widget right now; you could add per-row click targets if you split the chart into individualImageView/TextViewrows instead of one bitmap. - Android 12+ responsive layouts: for an even smoother resize experience
on API 31+, you could additionally supply a size-keyed
RemoteViews(Map<SizeF, RemoteViews>)so the system switches layouts instantly during a drag, rather than waiting for theonAppWidgetOptionsChanged()callback. The current approach already covers all API levels (26+) and updates immediately once the drag ends.
Description
Languages
Kotlin
100%