Monitor status bar 1/5: Extract audio device handling into AudioDevices - #90
Conversation
…tch out of the Monitor fragment into a shared AudioDevices object. The fragment keeps its spinner but only renders what the object reports: the list comes from AudioDevices.list, the restored selection from AudioDevices.selected, and a user pick goes through AudioDevices.select, which stores the choice and moves a running capture onto it. The choice is now stored even while the engine is stopped, so a pick made before pressing Start survives the fragment being recreated; before, an unsaved pick silently reverted on a tab switch. Start reads the stored choice instead of the spinner position, which also removes the TruSDX coercion from the start path: with a TruSDX rig the list itself holds only the two TruSDX inputs, so a stale non-TruSDX selection resolves to TruSDX Serial by construction instead of being forced there. The Settings screen offers the same audio choice next, which is what the shared object is for. The extraction absorbed the duplicates around it. The service's getDeviceName carried a verbatim copy of the device name table, so it delegates to AudioDevices.nameFor and the table exists once; the TruSDX display labels became constants beside the ids they name instead of literals in three files; and the trusdx check deciding whether the mic permission applies reads the same predicate the list uses. select skips the store and the dispatch when the choice is unchanged and reports whether it moved a live capture, so the switching snackbar keys off that answer and the fragment dropped its own copy of the selected id. Two leftovers went with it: the isUpdatingSpinner flag guarded programmatic selections that the user-initiated check already absorbs, because setSelection fires its callback only after the flag has been reset, and the refresh at the end of spinner setup duplicated the one onResume always makes. refreshAudioDevices resolves the saved choice against the list it just built instead of enumerating the devices twice, and selected cannot return null because the list is never empty.
|
I think there’s one edge case in AudioDevices.select() worth fixing before merge. Right now it returns early if the newly selected device matches the saved preference. That assumes the saved device is also the device the engine is currently using, but those can get out of sync. For example, if the saved USB device is unplugged at startup, the app falls back to another input without changing the saved preference. If that USB device later comes back and the user selects it, select() sees that it already matches the saved ID and does nothing, so the UI can show the USB device while the engine is still using the fallback input. I think the preference check should only control whether the preference needs updating. If the engine is running, it should still send the switch request and let JS8EngineService decide whether the requested device is already active, since it already has that check. |
…ed preference. The saved device and the device the engine is capturing on can differ: a saved input that is unplugged at startup resolves to a fallback without touching the preference, so when it comes back and gets picked, the early return concluded there was nothing to do and the engine stayed on the fallback while the UI showed the pick. The preference comparison now gates only the preference write, and a running engine always gets the switch request, since the service is the only party that knows the active device and already ignores a request for it.
|
♻️ thanks! |
Yeah, you're right. The saved id and the active capture can drift exactly the way you describe, and the service is the only side that knows the active device. The check now gates only the preference write and a running engine always gets the switch request. Funny enough the old fragment code had this right by accident, it deduped against the resolved selection rather than the raw preference, and I dropped that in the cleanup. Pushed as a follow up commit. |
Part 1 of 5 replacing the Monitor status card with a compact status strip.
What
Why
Test
./gradlew :app:testDebugUnitTestrig_type=trusdx_serialthe list swaps to the two TruSDX inputs. Picking TruSDX Speaker while stopped stores the choice and it survives a tab round trip.