Five things land together — same surface, all the data/settings flow:
SettingsStore additions
themeMode: System / Light / Dark (default System)
cacheEnabled: Bool (default true). Single toggle gates both the
subs feed cache (FeedCacheStore) and the new search cache.
Theme override
StrawActivity reads Settings.themeMode and bypasses
isSystemInDarkTheme when Light or Dark is chosen. Changes apply
immediately via StateFlow recomposition — no restart needed.
SearchCacheStore (new)
SharedPreferences JSON of the last 30 searches with 20 items each
(~150 KB cap). Serialized via @Serializable StreamItem (already
serializable since vc=33).
Reactive search
SearchViewModel.onQueryChange now scans the merged corpus (saved
searches + subs feed cache, ~1500 records max) as the user types
past 2 chars. Matches on title or uploader (case-insensitive),
dedup by URL, cap 60. UI shows a "Cached results · …" hint when
the visible list is from cache so users know it's not the network
result yet.
submit() now paints any matching cached query immediately, then
kicks the network. Network results overwrite cache on success and
the cached preview survives network failures so offline users
still see something.
Cache opt-out
Settings switch "Enable local cache" wipes both stores when
flipped off. FeedCacheVM + SearchVM both short-circuit their
read/write paths when the flag is false.
Log dump
util/LogDump captures this PID's logcat (-d -v threadtime --pid=)
to cacheDir, returns a chooser Intent via FileProvider. New
Settings → "Export logs…" button. Toast surfaces the failure
reason if the dump command itself fails (sandbox-restricted
devices etc.).
FileProvider declared in manifest with cache-path "logs"; XML at
res/xml/file_paths.xml. Authority = ${applicationId}.fileprovider.