Commit Graph
135 Commits
Author SHA1 Message Date
darken 384d81be18 feat(aap): Persist learned ANC settings across reconnects 2026-04-17 13:02:14 +02:00
darken 5ee094da3c feat(device-settings): Split ANC-with-one-pod into its own toggle 2026-04-17 13:02:14 +02:00
darken 77ea31216f feat(overview): Sort and collapse inactive device cards
Sort profiled devices into tiers (system-connected > nearby > cached), ordered by profile list within each tier. Collapse non-pinned cards to a compact battery tray with mini gauge rings matching the expanded card design. System-connected and top devices stay expanded; others can be tapped to toggle.
2026-04-17 13:02:14 +02:00
darken e5821de4dd feat(aap): Unified command queue with ear-detection gating and session engine extraction
Queue all setting commands when no pod is in ear, flush when a pod goes in. Enable the adaptive noise slider when ADAPTIVE mode is pending. Show an info box when settings changes are pending.

Extract AapSessionEngine (state, send path, message processing, inference) and AapSettingsCoordinator (queue, optimistic updates, verification) from AapConnection, reducing it from 773 to 173 lines.
2026-04-16 10:32:21 +02:00
darken 35c06d9982 feat(device-settings): Move device details to bottom sheet
Move serial, firmware, build, manufacturer, and per-pod serials from

the info card into a ModalBottomSheet triggered by an info icon.

Firmware+build and left+right pod serials render as paired rows.
2026-04-16 10:32:21 +02:00
darken 32ff7e5bdd feat(aap): Decode unknown settings, extend DeviceInfo, fix UTF-8 parsing
- Add UnknownSetting catch-all for 9 unconfirmed H2+ setting IDs (0x29, 0x2C, 0x2F, 0x30, 0x33, 0x37, 0x38, 0x3B, 0x3E)

- Downgrade known non-settings commands (0x0000, 0x0002, 0x000C, 0x0017, 0x002B, 0x004E, 0x0055, 0x0057) from INFO to VERBOSE

- Add earbud serials and build number to DeviceInfo (parsed from 0x001D segments 8-10, shown in DeviceInfoCard, persisted in cache)

- Fix UTF-8 device name parsing (curly quotes etc. no longer break segment indices)

- Add [was: ...] to setting change logs for easier protocol diff analysis

- Label DeviceInfoDump segments 5-12 (firmwareVersionDup, protocolVersion, updaterAppId, earbudSerials, buildNumber, encryptedBlob, timestamp)
2026-04-16 10:32:21 +02:00
darken 90af17bf7d feat(device-settings): Add experimental info box, revise pro-gating, fix switch bypass
Add SettingsInfoBox title support and experimental feature warning for Sleep Detection and Personalized Volume toggles with issue tracker action.

Pro-gate tone volume, microphone mode. Un-gate sleep detection, conversation awareness. Fix SettingsSwitchItem allowing direct switch toggle to bypass requiresUpgrade.
2026-04-16 10:32:21 +02:00
darken f17b6ca99f feat(device-settings): Merge one-pod mode settings into a single toggle
Combine reaction one-pod mode (autoplay/autopause) and firmware NC-with-one-AirPod into one setting. The merged toggle always sets the app-local flag and additionally syncs the firmware NC setting via AAP when connected.

Also remove redundant @OptIn(ExperimentalCoroutinesApi::class) annotations from tests, already covered by the global compiler opt-in flag.
2026-04-15 07:37:51 +02:00
darken 4b80ac7336 feat(overview): Add dynamic Bluetooth status icon to toolbar 2026-04-15 06:46:54 +02:00
Matthias Urhahn 5d5e6670d4 fix(settings): Improve AAP unavailable description with update guidance (#513) 2026-04-14 19:23:32 +02:00
darken da64406638 feat(device-settings): Show Auto Connect caveat as conditional info box 2026-04-14 19:21:34 +02:00
darken 8229163f70 fix(settings): Gate Adaptive Audio Noise slider on Adaptive noise mode 2026-04-14 15:57:07 +02:00
darken 6d313c077d feat(stem): Note that long-press overrides native ANC cycling 2026-04-14 14:44:22 +02:00
darken c28502fc8a refactor(device-settings): Fold Conversation Awareness and Sleep Detection into Reactions
AAP vs BLE is an implementation detail. From a user perspective these are 'when X happens, do Y' toggles like Auto Play/Pause. Gate them on device.isAapConnected so they stay hidden on phones without L2CAP support, avoiding confusion. The separate 'Smart features' section and its string resource are removed.
2026-04-14 14:39:01 +02:00
darken 2f3cae2b2c refactor(device-settings): Reorganize sections and extract components
Visual: wrap Noise Control in SettingsSection for consistency with Sound/Controls/Smart features, add divider between ANC mode picker and trailing items.

Categories: rename 'Other' to 'Smart features', move Microphone Mode to Sound, move Conversation Awareness to Smart features alongside Sleep Detection, place Adaptive Noise adjacent to ANC mode picker.

Extraction: split DeviceInfoCard, NotConnectedCard, AapUnavailableCard into cards/; AutoConnectConditionDialog, RenameDialog, SystemRenameUnavailableDialog into dialogs/; NoiseControlCombined into components/. Each with isolated previews.
2026-04-14 14:39:01 +02:00
darken 566a6cd120 feat(settings): Replace rename-failure snackbar with dialog 2026-04-14 11:38:09 +02:00
darken c0060ce5ca feat(popup): Suppress popup when app is visible and add settings hints
Hide the overlay popup when MainActivity is in the foreground since the user can already see battery info in the app. Show an info card when popups are enabled explaining they only appear outside the app. Show a warning card with a Fix button when monitor mode is MANUAL.

Refactor SettingsInfoBox into a reusable component with INFO/WARNING types and optional action slot.
2026-04-14 11:28:41 +02:00
darken 6825abaa41 Guard BLE-only autoplay and clarify one-pod mode 2026-04-14 11:28:41 +02:00
darken a37b9b0213 fix(settings): Show passive info when device is connected but AAP unavailable 2026-04-14 11:28:41 +02:00
darken 067eb2e9e0 feat(reactions): Move reaction settings to per-device profiles
Migrate reaction toggles (auto-play, auto-pause, auto-connect, popups) from global ReactionSettings singleton to per-profile fields on AppleDeviceProfile. Each paired device can now have independent reaction behavior.

Extract ReactionConfig snapshot to decouple PodDevice from AppleDeviceProfile — reaction consumers read device.reactions instead of device.profile. Remove auto-connect from upgrade benefits (now free). Add LegacyReactionSettingsReader for one-shot DataStore migration.
2026-04-14 11:28:41 +02:00
darken 37e2593d8a fix(overview): Warn when profile has no paired Bluetooth device 2026-04-11 17:56:21 +02:00
darken ed6c939284 refactor(settings): Rename ProBadge to UpgradeBadge for flavor neutrality
The 'Pro' label is gplay-specific vocabulary — FOSS unlocks the same features via sponsorship. Aligns with the existing flavor-neutral terms already used in UpgradeRepo, Nav.Main.Upgrade and launchUpgrade(). Renames the proLocked parameter to requiresUpgrade across SettingsBaseItem and its three wrappers, and adds a new common_upgrade_required_label string (Requires upgrade) for the badge content description.
2026-04-11 17:03:40 +02:00
darken ed37e142ad fix(ui): Fix signal bars and restore individual connection icons 2026-04-08 18:07:06 +02:00
darken cb915d6263 feat(aap): Also update system Bluetooth alias when renaming
After the AAP rename succeeds, try to update Android's per-device bond alias via the hidden BluetoothDevice.setAlias(String) method, so the new name also shows up in the system Bluetooth settings on this phone.

Known failure mode on Android 12+: setAlias is gated behind a Companion Device Manager (CDM) association at the service layer, and raises 'does not have a CDM association with the Bluetooth Device' for third-party apps that don't hold one. In that case, a dedicated snackbar explains that the system rename didn't go through and suggests renaming manually in system settings or re-pairing.

The AAP-level rename is always attempted first and is the load-bearing part; the system alias is a best-effort extra.
2026-04-08 15:36:18 +02:00
darken 41245d928c fix(aap): Make AirPods rename actually apply on device
Switch the AAP rename packet to the opcode 0x1A format (04 00 04 00 1A 00 01 [size] 00 [name]) matching the LibrePods documentation and Linux implementation. The previous 0x1E variant (from the LibrePods Android code) was silently ignored by AirPods Pro 2 USB-C firmware — no 0x001D echo, no persistence across reconnect.

Verified on AirPods Pro 2 USB-C (firmware 81.2675...): the device now echoes the new name back via the next 0x001D INFORMATION message, and the name persists after disconnect/reconnect.

Also hardens the rename UX: gate the edit icon on isAapReady (was isAapConnected, which allowed sending during HANDSHAKING), apply an optimistic deviceInfo update with a scoped rollback on send failure, surface send errors via a new Event.SendFailed + snackbar, and restrict dialog input to ASCII with inline error feedback. Unifies send() / sendProGated() through a single sendInternal() helper so error plumbing benefits every command, not just rename.

Note: this only updates the AirPods firmware's self-reported name. Android's system Bluetooth settings read from the bond database and are not affected — renaming there still requires the Android system Bluetooth UI.
2026-04-08 15:36:18 +02:00
darken ccd95a0981 feat(aap): Show 'Not connected' card when AAP is unavailable 2026-04-08 11:49:07 +02:00
darken 175f8e36b5 fix(aap): Address device settings review feedback
- Show connected device MACs in correct byte order (remove stale reversal)

- Rename 'Volume Swipe Length' setting to 'Volume Swipe Wait Time'

- Hide 'Charging Sounds' toggle: real case tones go over ATT and the actual effect of this AAP setting is unknown. Decode kept internally so AAP freshness signal still refreshes.

- Fix duplicate commands on End Call/Mute Mic radio buttons: use Modifier.selectable with Role.RadioButton, short-circuit when already selected, and wrap options in selectableGroup for TalkBack.
2026-04-07 18:53:54 +02:00
darken a102b03261 fix: Show signal strength as bar indicator instead of percent 2026-04-07 17:56:29 +02:00
darken 7e73ac80e9 fix(aap): Fix stem actions volume control, reset button, and Default/No Action semantics
Fix volume up/down doing nothing by using adjustSuggestedStreamVolume instead of dispatchMediaKeyEvent which ignores volume keycodes.

Add reset-to-defaults button with confirmation dialog in the stem actions TopAppBar.

Rename 'None' to 'Default' (firmware handles the press) and add 'No Action' (claimed but do nothing) to correctly model per-press-type claim mask semantics.

Remove disableNone lock; add cross-side auto-set logic so selecting Default resets both sides and selecting an action promotes the other side from Default to No Action.
2026-04-04 11:41:26 +02:00
darken 1ae9ae587e feat: Add device settings and controls to upgrade screen benefits 2026-04-04 11:41:26 +02:00
darken 33c8fe76a6 fix(aap): Polish noise control UI and fix ANC mode switching
Combine ANC mode selector with listening mode cycle into a card with eye icon visibility toggles and radio button mode selection. Send AllowOff command when toggling OFF visibility.

Fix ANC resend logic fighting rapid mode changes by cancelling pending resend jobs on new commands. Reorder settings into Noise Control, Sound, Controls, General, and Connections sections.
2026-04-04 11:41:26 +02:00
darken e356344ba6 feat(aap): Add new device settings, stem actions, and noise control UI
Add 8 new AAP writable settings (microphone mode, ear detection toggle, listening mode cycle, allow off, stem config, sleep detection, in-case tone, device rename) and 4 new data reception features (stem press events, connected devices, audio source, EQ data).

Implement stem press action system with per-bud configurable Android actions (play/pause, next/prev track, volume), auto-sent stem config on connection, and dedicated config screen.

Combine ANC mode selector with listening mode cycle visibility into a unified noise control component. Eye icons control which modes appear in the dashboard card and settings. Pro gating with stars icon for upgrade-required features.

Add 64 unit tests covering all new decoders, model feature flags, malformed payloads, and rename byte-length validation.
2026-04-04 11:41:26 +02:00
darken e014ad8045 feat: Slim down overview cards and fix cached device icons
Remove timestamps, connection state, and conversation awareness from overview cards. Move signal badge inline with model subtitle. Relocate removed info to device settings screen info card. Fix cached devices showing Gen 1 icons by adding per-pod icon properties to PodModel. Add profile name subtitle to device settings toolbar. Simplify preview coverage to full/minimal/cached variants.
2026-04-03 15:36:27 +02:00
darken adfedc1cce feat: Add device settings screen for AAP controls 2026-04-03 13:09:31 +02:00
darken c609b03148 feat(overview): Gate dashboard device count for free users
Free users see only their highest-priority profiled device. Additional devices are hidden behind an upgrade card showing the count and a flavor-aware CTA (Upgrade/Donate).
2026-04-02 18:30:40 +02:00
darken e91243e577 feat: Add unified device state cache for persistent battery display
Replace PodDeviceCache (raw BLE scan bytes) with DeviceStateCache that stores decoded combined device state (battery, charging, model) per profile.

Battery values persist across app restarts with per-slot timestamps. Cached-only cards appear for offline devices with muted visuals and a staleness indicator. Fallback chain: AAP -> BLE -> cached.
2026-04-02 13:56:09 +02:00
darken ceb4108306 feat(profiles): Filter paired devices already used by other profiles
Show devices claimed by other profiles as disabled in the dropdown with the claiming profile's name. Enforce address uniqueness at the repo layer.
2026-04-02 12:23:06 +02:00
darken 70e5b99e1d feat: Add connection type indicators to SignalBadge
Move BLE key and AAP connection icons into the SignalBadge pill.

Add BleKeyState enum on PodDevice to expose IRK/ENC state cleanly.

Key icon: outlined for IRK-only, solid for IRK+ENC.

Bluetooth icon shown when AAP transport is active.
2026-03-31 19:17:09 +02:00
darken c1cd99f701 feat: Add AAP L2CAP protocol support for reading and writing AirPods settings
Full integration of the Apple Accessory Protocol (AAP) over L2CAP, enabling direct communication with AirPods for 1% battery granularity, ANC mode control, Conversation Awareness toggle, and private key exchange for BLE encrypted battery.
2026-03-31 19:17:09 +02:00
darken 53447f3fb9 fix: Stop retrying auto-connect after SecurityException
BluetoothHeadset.connect() requires MODIFY_PHONE_STATE on modern Android, which is a system-only permission. Detect the SecurityException and stop further attempts instead of retrying every second.
2026-03-23 11:24:23 +00:00
darken 67474a30da fix(debug): Rename recorder screen button from Close to Keep
The keep button's onClick handler was already named onKeep but the label said Close. Renamed to Keep for clarity alongside the Delete/Share buttons. Added dedicated string resource with translations for all 75 locales.
2026-03-10 07:50:36 +00:00
darken 7f8daddfe0 feat(debug): Show confirmation dialog after sharing debug logs
After sharing a debug log or sending a contact form email, show a dialog on return asking if the send was successful. Confirming deletes the log session and closes/navigates back.
2026-03-09 11:17:50 +00:00
darken 62e39ce3d2 refactor(support): Add zipFile/compressedSize to Ready, harden session manager
Add zipFile and compressedSize fields to DebugSession.Ready, populated during scan. Simplify RecorderActivityVM by reading compressed size directly from session model. Apply code review fixes: fsMutex in zipSessionAsync, CancellationException handling, ZipOutputStream use{}, atomic file moves, dedup deriveSessionId, LifecycleResumeEffect for session refresh.
2026-03-09 04:53:01 +00:00
darken 7df74e3b44 refactor(support): Unify debug log session model and add session manager bottom sheet
Introduce DebugSession sealed interface as single source of truth for session lifecycle (Recording, Compressing, Ready, Failed). Auto-compress after recording stops. Replace scattered clear/delete actions with a session manager bottom sheet on the Support screen.
2026-03-09 04:53:01 +00:00
darken e35bce4d19 feat(upgrade): Add yearly subscription with trial to Google Play billing
Rewrite SKU type system from data class to interface hierarchy supporting both IAP and subscriptions. Add subscription query, offer matching, and billing flow launch for yearly plans with optional free trial.

FOSS: Add sponsor-gate with 10-second timer and snackbar nudge.
2026-03-04 22:38:23 +00:00
darken abb189f43c feat(upgrade): Replace upgrade dialog with full-screen upgrade screen
Add dedicated Compose upgrade screens for both FOSS and gplay flavors, presenting pro benefits before the purchase/sponsor action. Migrate all upgrade dialog callers to navigate to the new screen instead.
2026-03-03 20:35:32 +00:00
darken e13331dfcf feat(monitor): Show disable hint in foreground notification when extra notification is enabled 2026-03-03 15:26:10 +00:00
darken c760d5758d feat(support): Polish support screen, recorder UI, and settings
Reorder support screen items with category headers, add log session count to clear action, fix contact form hint strings, filter active recordings from log picker, convert RecorderActivity to Compose with proper system bar insets and Material 3 color tokens, refresh log metadata on resume, add wiki link to settings, and move support entry under Other category
2026-03-03 13:20:31 +01:00
darken b609c1aced feat(support): Add contact support form and enhanced debug recorder
Add structured contact form with category selection (Question/Feature/Bug), description with word count validation, expected behavior field for bugs, and debug log picker with inline recording.

Enhance RecorderActivity with full-screen hero layout, file list, Share/Keep/Discard actions. Migrate debug logs from flat files to session directories in external files dir. Add DebugLogZipper, EmailTool attachment support, log folder size display, and clear stored logs.
2026-03-03 13:20:31 +01:00
darken 073de3cf35 feat(settings): Add pro gating for theme customization
Gate theme mode, style, and color settings behind pro/upgrade status.

Add Material Design 3 surface container color tokens to all color palettes.

Move upgrade prompt string to main resources with generic wording.

Disable theme preference items for non-pro users instead of late rejection.
2026-03-02 16:36:52 +01:00