fix(widget): read category colors from aw-webui settings - #289
TimeToBuildBob wants to merge 2 commits into
Conversation
…ardcoded values The bar chart and category dots in the home-screen widget used three hardcoded accent colors (#00BFA5, #7986CB, #42A5F5) regardless of what colors the user configured for their categories in the web UI. Read the `classes` setting (same datastore key aw-webui persists to) and extract the configured color from each top-level category's `data.color` field. Fall back to the hardcoded defaults for categories that have no color configured. Closes ActivityWatch#288 Git-Session-Id: fd77e3bc-ee8c-51e6-a794-7065e7599bd3
|
|
|
||
| // Draw and set the bar chart |
There was a problem hiding this comment.
When a user selects a non-default category color, these configured colors are applied only to the bar bitmap. The row binding updates text and visibility but never colors the dot views, whose drawables still use fixed default colors. The displayed dot therefore disagrees with its corresponding bar segment, leaving the widget color fix incomplete.
Knowledge Base Used: Category time home screen widget
The row-binding loop updated text and visibility but never updated the dot ImageViews, so dots kept their hardcoded drawable colors regardless of what the user configured. Now resolveBarColors() is called once before the loop (same inputs already used for the bar bitmap) and the result is applied via setColorFilter so bar segments and their corresponding legend dots always match. Git-Session-Id: 3aced3c8-97ab-51ae-8ad1-cf9a4d27cdd9
|
Fixed the Greptile P1: the category dot
Dots and bar segments now use the same resolved color for each category. Fallback behavior (no configured color or invalid hex) is unchanged. |
|
@greptileai review |
|
CI-green and mergeable (Greptile 5/5) — waiting only on a maintainer click. This PR is ready to merge, but the bot has pull-only access to this repo and can't self-merge — surfacing it here so it isn't lost. The monitoring loop will stop re-flagging it now that this note is posted. |
🤖 AI code reviewThe PR replaces the hardcoded category accent colors in the widget with colors read from the aw-webui Not safe to merge — 2 P1 openConfidence 2/5 2 findings · ❌ 2 P1❌ P1 high — The How this was verified: Checked the aw-webui source (not provided) but the PR description says 'the same datastore key aw-webui persists to' and the existing parseCategories() in this file uses ❌ P1 high — The new How this was verified: The diff only modifies the Kotlin file and the test file. The layout file 2 out-of-scope findings (outside this PR's changed files)Each item here is an observation about the PR as a whole — informational only, not scored. The new The Files changed (2) — the diff as I read it
Reviewed Maintainer commands
|
| val array = JSONArray(v) | ||
| for (i in 0 until array.length()) { | ||
| val obj = array.optJSONObject(i) ?: continue | ||
| val nameArr = obj.optJSONArray("name") ?: continue |
There was a problem hiding this comment.
❌ P1 — The classes setting in aw-webui is a JSON array of category objects, but the name field is not always an array. In aw-webui's actual datastore, the classes setting is stored as a JSON array where each element has a name that is a string (e.g. "Work") or an array of strings for subcategories? Let me check. The PR's own test helper builds "name":["Work"] as an array. But the real aw-webui classes setting format: In aw-webui source, the classes setting is a list of objects with name being a string (the full path like "Work") and data containing color. Actually I need to verify. The PR description says "extracts each top-level category's data.color field" and the code checks nameArr.length() != 1 to only include top-level categories. If the real format has name as a string, then optJSONArray("name") returns null and the entry is skipped, so no colors are ever read. This would be a contract bug. But I'm not sure about the real format. The test helper uses arrays, but that may be invented. I should check the aw-webui code. Since I can't access it, I should be cautious. The PR description says "the same datastore key aw-webui persists to". The aw-webui classes setting is actually a JSON array of objects like {"name": "Work", "rule": {...}, "data": {"color": "#00BFA5"}}? I recall from ActivityWatch web UI, the categories are defined with a name that is a string, not an array. The $category field in events is an array, but the classes setting uses a string name. Let me think. In aw-webui's src/settings.js, the default classes are something like [{name: 'Work', rule: {...}, data: {color: '#...'}}]. The name is a string. So this code's assumption that name is an array is wrong. That would mean parseCategoryColors always returns an empty map, and the feature silently does nothing. This is a real bug. I should report it. The line is 195: val nameArr = obj.optJSONArray("name") ?: continue. The fix is to handle both string and array: if name is a string, use it directly; if it's an array, use the first element if length==1. But the PR's intent is to only include top-level categories. In aw-webui, subcategories are separate entries with names like "Work > Programming"? Actually the classes setting has a flat list of category definitions, each with a full name string. There is no hierarchy in the setting; the hierarchy comes from the $category array in events. So the check for nameArr.length() != 1 is based on a wrong assumption. The correct behavior is to read the name string directly. This is a P1 bug because the feature won't work at all. I'll report it.
| val (name, duration) = topApps[i] | ||
| views.setTextViewText(appNameIds[i], name) | ||
| views.setTextViewText(appTimeIds[i], formatDurationShort(duration)) | ||
| views.setInt(appDotIds[i], "setColorFilter", dotColors[i]) |
There was a problem hiding this comment.
❌ P1 — The new appDotIds array is used to set a color filter on the dot ImageViews. The layout widget_category_time.xml must have app_dot_1, app_dot_2, app_dot_3 defined as ImageViews. If these IDs do not exist in the layout, views.setInt(appDotIds[i], "setColorFilter", dotColors[i]) will throw an IllegalArgumentException ("View id not found") at runtime, which is caught by the outer try-catch and results in the widget showing zeros instead of the app list. The PR does not modify the layout file, so it is unclear whether these IDs exist. If they do not exist, the widget update fails entirely for all users.
|
Both P1s in the AI review above are false positives — verified against the actual sources:
PR is CI-green, Greptile 5/5, and ready to merge. Waiting on a maintainer. |
Fixes #288
What changed
The home-screen widget bar chart and category dots used three hardcoded
accent colors regardless of what the user configured in the web UI.
This PR reads the
classessetting (the same datastore key aw-webuipersists to) and extracts each top-level category's
data.colorfield.Categories without a configured color fall back to the existing defaults.
How it works
parseCategoryColors(settingsJson)readsgetSetting("classes"),the same API already used for
startOfDay. ReturnsMap<String, String>(name → hex color string) — kept free of Android APIs so it's testable
on the JVM without Robolectric.
resolveBarColors()converts the hex strings toColorints,falling back to the hardcoded defaults for any category with no
configured color or an invalid hex value.
Testing
Five new JVM unit tests cover
parseCategoryColors: top-level extraction,subcategory exclusion, null setting, missing color field, short hex,
malformed JSON.