Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix AirPods Max and AirPods Max (USB-C) support #240

Merged
merged 1 commit into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ interface PodDevice {
"AirPods Max",
R.drawable.devic_headphones_generic
),
@Json(name = "airpods.max2") AIRPODS_MAX2(
"AirPods Max 2",
@Json(name = "airpods.max.usbc") AIRPODS_MAX_USBC(
"AirPods Max USB-C",
R.drawable.devic_headphones_generic
),
@Json(name = "beats.flex") BEATS_FLEX(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import eu.darken.capod.pods.core.apple.airpods.AirPodsGen3
import eu.darken.capod.pods.core.apple.airpods.AirPodsGen4
import eu.darken.capod.pods.core.apple.airpods.AirPodsGen4Anc
import eu.darken.capod.pods.core.apple.airpods.AirPodsMax
import eu.darken.capod.pods.core.apple.airpods.AirPodsMax2
import eu.darken.capod.pods.core.apple.airpods.AirPodsMaxUsbc
import eu.darken.capod.pods.core.apple.airpods.AirPodsPro
import eu.darken.capod.pods.core.apple.airpods.AirPodsPro2
import eu.darken.capod.pods.core.apple.airpods.AirPodsPro2Usbc
Expand Down Expand Up @@ -43,7 +43,7 @@ abstract class AppleFactoryModule {
@Binds @IntoSet abstract fun airPodsPro2(factory: AirPodsPro2.Factory): ApplePodsFactory<out ApplePods>
@Binds @IntoSet abstract fun airPodsPro2Usbc(factory: AirPodsPro2Usbc.Factory): ApplePodsFactory<out ApplePods>
@Binds @IntoSet abstract fun airPodsMax(factory: AirPodsMax.Factory): ApplePodsFactory<out ApplePods>
@Binds @IntoSet abstract fun airPodsMax2(factory: AirPodsMax2.Factory): ApplePodsFactory<out ApplePods>
@Binds @IntoSet abstract fun airPodsMax2(factory: AirPodsMaxUsbc.Factory): ApplePodsFactory<out ApplePods>

@Binds @IntoSet abstract fun beatsFlex(factory: BeatsFlex.Factory): ApplePodsFactory<out ApplePods>
@Binds @IntoSet abstract fun beatsSolo3(factory: BeatsSolo3.Factory): ApplePodsFactory<out ApplePods>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ data class AirPodsMax(
class Factory @Inject constructor() : SingleApplePodsFactory(TAG) {

override fun isResponsible(message: ProximityPairing.Message): Boolean = message.run {
getModelInfo().dirty == DEVICE_CODE_DIRTY && length == ProximityPairing.PAIRING_MESSAGE_LENGTH
getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH
}

override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods {
Expand All @@ -63,7 +63,7 @@ data class AirPodsMax(
}

companion object {
private val DEVICE_CODE_DIRTY = 0x200A.toUByte()
private val DEVICE_CODE = 0x0A20.toUShort()
private val TAG = logTag("PodDevice", "Apple", "AirPods", "Max")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import eu.darken.capod.pods.core.apple.protocol.ProximityPairing
import java.time.Instant
import javax.inject.Inject

data class AirPodsMax2(
data class AirPodsMaxUsbc(
override val identifier: PodDevice.Id = PodDevice.Id(),
override val seenLastAt: Instant = Instant.now(),
override val seenFirstAt: Instant = Instant.now(),
Expand All @@ -25,7 +25,7 @@ data class AirPodsMax2(
private val rssiAverage: Int? = null,
) : SingleApplePods, HasEarDetection, HasChargeDetection, HasAppleColor {

override val model: PodDevice.Model = PodDevice.Model.AIRPODS_MAX2
override val model: PodDevice.Model = PodDevice.Model.AIRPODS_MAX_USBC

override val rssi: Int
get() = rssiAverage ?: super<SingleApplePods>.rssi
Expand All @@ -39,11 +39,11 @@ data class AirPodsMax2(
class Factory @Inject constructor() : SingleApplePodsFactory(TAG) {

override fun isResponsible(message: ProximityPairing.Message): Boolean = message.run {
getModelInfo().dirty == DEVICE_CODE_DIRTY && length == ProximityPairing.PAIRING_MESSAGE_LENGTH
getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH
}

override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods {
var basic = AirPodsMax2(scanResult = scanResult, proximityMessage = message)
var basic = AirPodsMaxUsbc(scanResult = scanResult, proximityMessage = message)
val result = searchHistory(basic)

if (result != null) basic = basic.copy(identifier = result.id)
Expand All @@ -63,7 +63,7 @@ data class AirPodsMax2(
}

companion object {
private val DEVICE_CODE_DIRTY = 0x201F.toUByte()
private val TAG = logTag("PodDevice", "Apple", "AirPods", "Max2")
private val DEVICE_CODE = 0x1F20.toUShort()
private val TAG = logTag("PodDevice", "Apple", "AirPods", "Max", "USBC")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package eu.darken.capod.pods.core.apple.airpods

import eu.darken.capod.pods.core.PodDevice
import eu.darken.capod.pods.core.apple.BaseAirPodsTest
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test

class AirPodsMaxUsbcTest : BaseAirPodsTest() {

// Test data from https://github.com/d4rken-org/capod/issues/236
@Test
fun `default AirPods Max`() = runTest {
create<AirPodsMaxUsbc>("07 19 01 1F 20 2B 05 80 03 12 C5 2E 8B F9 9A 7E 19 7B 63 0F 30 6E D7 3B E2 EC 32") {
rawPrefix shouldBe 0x01.toUByte()
rawDeviceModel shouldBe 0x1F20.toUShort()
rawStatus shouldBe 0x2B.toUByte()
rawPodsBattery shouldBe 0x05.toUByte()
rawFlags shouldBe 0x8.toUShort()
rawCaseBattery shouldBe 0x0.toUShort()
rawCaseLidState shouldBe 0x03.toUByte()
rawDeviceColor shouldBe 0x12.toUByte()
rawSuffix shouldBe 0xC5.toUByte()

batteryHeadsetPercent shouldBe 0.5f

isHeadsetBeingCharged shouldBe false

model shouldBe PodDevice.Model.AIRPODS_MAX_USBC
}
}
}
Loading