Initial Commit
This commit is contained in:
86
README.md
Normal file
86
README.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# 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
|
||||
1. Open Android Studio → **Open** → select the `BudgetTrackerWidget` folder.
|
||||
2. 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).
|
||||
3. Run the `app` module on an emulator or device (API 26+).
|
||||
4. 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-out `parseBudgetJson()`
|
||||
shows how to parse a real API response — swap the body of `fetchBudgetData()`
|
||||
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 a `Bitmap`
|
||||
using `Canvas`. 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 an `ImageView`. 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`** — the `AppWidgetProvider`. Reads the widget's
|
||||
live size from `onAppWidgetOptionsChanged()` (fired whenever the user drags
|
||||
the resize handles) as well as `onUpdate()`, feeds it through
|
||||
`WidgetLayoutPlanner`, and builds `RemoteViews` accordingly — 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) `ProgressBar`s for the
|
||||
green/amber/red states, an `ImageView` for 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 single
|
||||
`ProgressBar` tinted at runtime via `BudgetColors`, 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`/`minHeight` in `budget_widget_info.xml` set the default placed
|
||||
size; `minResizeWidth`/`minResizeHeight` set how small it can be dragged.
|
||||
- At runtime, `onAppWidgetOptionsChanged()` reports the new size, and
|
||||
`WidgetLayoutPlanner` decides 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 type `List<BudgetItem>` and everything
|
||||
downstream (widget + activity) keeps working unchanged.
|
||||
- **Refresh cadence**: `updatePeriodMillis` in `budget_widget_info.xml` is set
|
||||
to 30 minutes (Android's minimum useful floor is ~30 min for this
|
||||
mechanism). For more frequent updates, trigger `ACTION_APPWIDGET_UPDATE`
|
||||
yourself from a `WorkManager` periodic job.
|
||||
- **Tap-to-open categories**: `setOnClickPendingIntent` is only wired to the
|
||||
whole widget right now; you could add per-row click targets if you split
|
||||
the chart into individual `ImageView`/`TextView` rows 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 the
|
||||
`onAppWidgetOptionsChanged()` callback. The current approach already
|
||||
covers all API levels (26+) and updates immediately once the drag ends.
|
||||
Reference in New Issue
Block a user