android / Build + lint + test (push) Failing after 1m22s
#367 shipped genre and year browsing on web only, which left the web tab bar's own comment -- "mirrors Android's LibraryScreen" -- half aspirational. Android now has both, straight after Albums, in the same order the web bar uses. Server-backed, and that is the one real decision here. Every other Library tab reads Room, and building these indexes locally was the obvious move: the cache is a full mirror and carries both genre and releaseDate. It does not work. /api/library/sync hydrates through GetTracksByIDs, which has no missing_since filter, and neither SyncTrackWire nor CachedTrackEntity has a field for it -- so the cache holds tracks whose files are gone and cannot tell you which, while the browse index excludes them. A locally-derived index would quietly disagree with the server's and with the web client, and could offer a genre that exists only in missing files. Filed as #2704; until it is resolved these two tabs need a connection, and their empty states say what they are rather than looking broken. Genre is a query parameter end to end, never a path segment: "Rock/Pop" is a real ID3 tag and a slash does not survive a path. That is also why the drill-down is a second state inside the tab instead of a nav destination -- a route would have had to carry the label. Index shapes mirror web because the reasoning was already worked out there: genres default to count order, since raw tags carry a long tail of one-offs that A-Z buries the real genres under, with an A-Z chip for when you already know the name; years group by decade, newest first, because a flat list of every year in a decades-deep library is a wall of numbers. Page size matches web's BROWSE_PAGE_SIZE so "Load more (N left)" steps identically on both. The orderings and grouping are pure functions, tested: server order left alone under count sort, case-insensitive A-Z, no mutation of the loaded state's list, a slashed tag surviving the filter intact, decade bucketing including the boundary year, and a count label that stays blank rather than flashing "0 albums" while the first page loads.