android: add option for alternate head tracking packets (#176)

* Initial plan

* Add option for alternate head tracking packets

Co-authored-by: kavishdevar <46088622+kavishdevar@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: kavishdevar <46088622+kavishdevar@users.noreply.github.com>
This commit is contained in:
Copilot
2025-07-11 10:11:55 +05:30
committed by GitHub
parent db563fa75f
commit d9359cd81a
3 changed files with 74 additions and 3 deletions

View File

@@ -179,6 +179,10 @@ fun AppSettingsScreen(navController: NavController) {
mutableStateOf(sharedPreferences.getBoolean("takeover_when_media_start", true))
}
var useAlternateHeadTrackingPackets by remember {
mutableStateOf(sharedPreferences.getBoolean("use_alternate_head_tracking_packets", false))
}
var mDensity by remember { mutableFloatStateOf(0f) }
fun validateHexInput(input: String): Boolean {
@@ -1040,6 +1044,47 @@ fun AppSettingsScreen(navController: NavController) {
}
}
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
useAlternateHeadTrackingPackets = !useAlternateHeadTrackingPackets
sharedPreferences.edit().putBoolean("use_alternate_head_tracking_packets", useAlternateHeadTrackingPackets).apply()
},
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier
.weight(1f)
.padding(vertical = 8.dp)
.padding(end = 4.dp)
) {
Text(
text = "Use alternate head tracking packets",
fontSize = 16.sp,
color = textColor
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = "Enable this if head tracking doesn't work for you. This sends different data to AirPods for requesting/stopping head tracking data.",
fontSize = 14.sp,
color = textColor.copy(0.6f),
lineHeight = 16.sp,
)
}
StyledSwitch(
checked = useAlternateHeadTrackingPackets,
onCheckedChange = {
useAlternateHeadTrackingPackets = it
sharedPreferences.edit().putBoolean("use_alternate_head_tracking_packets", it).apply()
}
)
}
Row(
modifier = Modifier
.fillMaxWidth()

View File

@@ -1068,7 +1068,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
if (isInCall) return
if (config.headGestures) {
initGestureDetector()
aacpManager.sendStartHeadTracking()
startHeadTracking()
gestureDetector?.startDetection { accepted ->
if (accepted) {
answerCall()
@@ -2131,12 +2131,22 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
fun startHeadTracking() {
isHeadTrackingActive = true
aacpManager.sendStartHeadTracking()
val useAlternatePackets = sharedPreferences.getBoolean("use_alternate_head_tracking_packets", false)
if (useAlternatePackets) {
aacpManager.sendDataPacket(aacpManager.createAlternateStartHeadTrackingPacket())
} else {
aacpManager.sendStartHeadTracking()
}
HeadTracking.reset()
}
fun stopHeadTracking() {
aacpManager.sendStopHeadTracking()
val useAlternatePackets = sharedPreferences.getBoolean("use_alternate_head_tracking_packets", false)
if (useAlternatePackets) {
aacpManager.sendDataPacket(aacpManager.createAlternateStopHeadTrackingPacket())
} else {
aacpManager.sendStopHeadTracking()
}
isHeadTrackingActive = false
}

View File

@@ -370,6 +370,14 @@ class AACPManager {
return opcode + data
}
fun createAlternateStartHeadTrackingPacket(): ByteArray {
val opcode = byteArrayOf(Opcodes.HEADTRACKING, 0x00)
val data = byteArrayOf(
0x00, 0x00, 0x10, 0x00, 0x0F, 0x00, 0x08, 0x73, 0x42, 0x0B, 0x08, 0x10, 0x10, 0x02, 0x1A, 0x05, 0x01, 0x40, 0x9C.toByte(), 0x00, 0x00
)
return opcode + data
}
fun sendStopHeadTracking(): Boolean {
return sendDataPacket(createStopHeadTrackingPacket())
}
@@ -382,6 +390,14 @@ class AACPManager {
return opcode + data
}
fun createAlternateStopHeadTrackingPacket(): ByteArray {
val opcode = byteArrayOf(Opcodes.HEADTRACKING, 0x00)
val data = byteArrayOf(
0x00, 0x00, 0x10, 0x00, 0x0F, 0x00, 0x08, 0x75, 0x42, 0x0B, 0x08, 0x10, 0x10, 0x02, 0x1A, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00
)
return opcode + data
}
fun sendRename(name: String): Boolean {
return sendDataPacket(createRenamePacket(name))
}