Skip to content

android: fix stem double-press hang-up and add head gestures master toggle#584

Open
ntsour wants to merge 3 commits intokavishdevar:mainfrom
ntsour:fix/stem-press-teams-hangup
Open

android: fix stem double-press hang-up and add head gestures master toggle#584
ntsour wants to merge 3 commits intokavishdevar:mainfrom
ntsour:fix/stem-press-teams-hangup

Conversation

@ntsour
Copy link
Copy Markdown

@ntsour ntsour commented May 6, 2026

Summary

Three related improvements to call handling and head gesture control (stacked on top of #580 and #581):

  • Fix stem double-press not ending VoIP calls — During a call, both single and double press are now forced customized so the app intercepts them. The end-call press is routed to rejectCall(), which tries TelecomManager.endCall(), then Teams' own notification Hang Up action, then falls back to HEADSETHOOK for other VoIP apps. Previously the firmware handled end-call natively via HFP CHUP — which telephony handles fine but Teams and other non-Telecom VoIP apps ignore.

  • Teams call hang-up via notification actionTeamsNotifListener now also caches the Hang Up PendingIntent from Teams' ongoing-call notification. hangUp() fires it so Teams reacts as if the user tapped the button in its notification, keeping the in-app UI in sync. Requires Notification access to be granted to LibrePods.

  • Global head gestures master toggle — New head_gestures_enabled pref exposed as a master switch at the top of the Head Tracking screen. Disables all head gesture features at once (answer/decline + mute/unmute). Sub-toggles are grayed out when the master is off. The settings screen summary, ServiceConfig, and the ViewModel all respect the new pref. Android 9 fallback disables it along with the others.

Test plan

  • Single stem press mutes during a phone call; double press ends the call
  • Single stem press mutes during a Teams call; double press ends the Teams call (requires Notification access granted to LibrePods in Settings → Apps → Special app access → Notification access)
  • TeamsNotifListener: Cached actions from com.microsoft.teams: mute=…, hangUp=… appears in logcat when a Teams call starts
  • Disabling the head gestures master toggle stops nod/shake gestures from answering or muting calls
  • Sub-toggles (answer/mute) are grayed out when the master is off
  • Settings screen summary shows "Off" when master toggle is disabled

nikos and others added 3 commits May 6, 2026 11:38
Replace the single "Head Gestures" master toggle with two independent feature
toggles in the Head Tracking screen:
  * Answer/decline incoming calls (nod = answer, shake = decline)
  * Mute/unmute during a call (shake = mute, nod = unmute)

The two behaviors operate in different call phases (ringing vs. active) and
never overlap, so they are exposed as separate prefs (head_gestures_answer_call
and head_gestures_mute_call) rather than a single switch.

Additional gesture-related polish:
  * Stem-press toggleMicMute now plays the same confirm_yes/confirm_no tones
    used for head gestures (audible mute/unmute feedback over the AirPods).
  * New MUTE_CALL StemAction so a stem press can be mapped to mute toggling.
  * Periodic 15-second low-volume "still muted" reminder while the mic is muted
    during a call, cancelled automatically on unmute or call end.
  * Active-call gesture loop now mutes/unmutes directionally (shake mutes, nod
    unmutes) instead of toggling, fixing the case where the gesture detector
    re-fires the same direction during a missed restart.
  * Restart bug fix in GestureDetector: callback is now invoked AFTER
    stopDetection() so a callback that re-arms detection doesn't hit
    isRunning=true and silently no-op.
  * Removed per-movement "blip" sounds on every head turn; only the final
    confirmation tone is played.
  * Stale-state fix in testHeadGestures(): force a stop before re-arming so
    isRunning=true left over from a previous test no longer breaks the next.
  * Threshold tuning for the gesture detector to reduce accidental triggers.
Teams on Android does not register VoIP calls with the Telecom framework,
so InCallService.setMuted() cannot drive its in-app mute UI. Instead, watch
Teams' ongoing-call notification and fire the cached Mute/Unmute action
PendingIntent — Teams reacts as if the user had tapped the action in the
notification, which keeps the in-app mute icon in sync with the OS mute set
by hardware controls (stem press, head gesture).

  * New TeamsNotifListener service (BIND_NOTIFICATION_LISTENER_SERVICE) that
    caches the latest Mute and Unmute actions from notifications posted by
    com.microsoft.teams (and a couple of related package variants).
  * AirPodsService.toggleMicMute() and the active-call gesture loop now also
    call TeamsNotifListener.setMuted() alongside AudioManager.setMicrophoneMute,
    so Teams' UI follows the OS state.
  * Notification access permission is requested from the initial setup screen,
    matching the existing permission cards. The "Ask for regular permissions"
    button now also opens the system Notification access settings if not yet
    granted.

The user must enable LibrePods in Settings → Apps → Notification access for
the sync to work; without it the call is a no-op and OS-level mute still
applies, just without Teams' UI updating.
…oggle

Three related improvements to call handling and head gesture control:

1. Fix stem double-press not ending VoIP calls (Teams, etc.)
   - Force both single and double press to be reported to the app during
     any active call. Previously only the mute-press type was customized,
     leaving end-call to the firmware's native HFP CHUP — which telephony
     handles fine but Teams and other non-Telecom VoIP apps ignore.
   - handleCallStemPress() now routes the end-call press to rejectCall(),
     which tries TelecomManager, then Teams' own notification Hang Up
     action, then falls back to HEADSETHOOK for other VoIP apps.

2. Teams call hang-up via notification action
   - TeamsNotifListener now also caches the Hang Up PendingIntent from
     Teams' ongoing-call notification (keywords: hang, end call, colgar,
     raccrocher, auflegen, finalizar).
   - hangUp() fires it so Teams reacts as if the user tapped the button
     in the notification, keeping its in-app UI in sync.

3. Global head gestures master toggle (head_gestures_enabled pref)
   - New master switch in the Head Tracking screen that disables all head
     gesture features at once (answer/decline + mute/unmute).
   - Sub-toggles are grayed out when the master is off.
   - ServiceConfig, ViewModel, and the settings screen summary all respect
     the new pref. Android 9 fallback disables it along with the others.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ntsour ntsour force-pushed the fix/stem-press-teams-hangup branch from ca19dfa to 66b536d Compare May 6, 2026 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant