diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewAdapter.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewAdapter.kt index 1f6d05a8..15d4464f 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewAdapter.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewAdapter.kt @@ -14,9 +14,8 @@ import eu.darken.capod.common.lists.modular.mods.TypedVHCreatorMod import eu.darken.capod.main.ui.overview.cards.BluetoothDisabledVH import eu.darken.capod.main.ui.overview.cards.MissingMainDeviceVH import eu.darken.capod.main.ui.overview.cards.PermissionCardVH -import eu.darken.capod.main.ui.overview.cards.pods.BasicSingleApplePodsCardVH -import eu.darken.capod.main.ui.overview.cards.pods.DualApplePodsCardVH -import eu.darken.capod.main.ui.overview.cards.pods.SingleApplePodsCardVH +import eu.darken.capod.main.ui.overview.cards.pods.DualPodsCardVH +import eu.darken.capod.main.ui.overview.cards.pods.SinglePodsCardVH import eu.darken.capod.main.ui.overview.cards.pods.UnknownPodDeviceCardVH import javax.inject.Inject @@ -29,9 +28,8 @@ class OverviewAdapter @Inject constructor() : init { modules.add(DataBinderMod(data)) modules.add(TypedVHCreatorMod({ data[it] is PermissionCardVH.Item }) { PermissionCardVH(it) }) - modules.add(TypedVHCreatorMod({ data[it] is DualApplePodsCardVH.Item }) { DualApplePodsCardVH(it) }) - modules.add(TypedVHCreatorMod({ data[it] is SingleApplePodsCardVH.Item }) { SingleApplePodsCardVH(it) }) - modules.add(TypedVHCreatorMod({ data[it] is BasicSingleApplePodsCardVH.Item }) { BasicSingleApplePodsCardVH(it) }) + modules.add(TypedVHCreatorMod({ data[it] is DualPodsCardVH.Item }) { DualPodsCardVH(it) }) + modules.add(TypedVHCreatorMod({ data[it] is SinglePodsCardVH.Item }) { SinglePodsCardVH(it) }) modules.add(TypedVHCreatorMod({ data[it] is MissingMainDeviceVH.Item }) { MissingMainDeviceVH(it) }) modules.add(TypedVHCreatorMod({ data[it] is BluetoothDisabledVH.Item }) { BluetoothDisabledVH(it) }) modules.add(TypedVHCreatorMod({ data[it] is UnknownPodDeviceCardVH.Item }) { UnknownPodDeviceCardVH(it) }) diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragmentVM.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragmentVM.kt index 9c87a4bb..73f4b03c 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragmentVM.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragmentVM.kt @@ -20,23 +20,23 @@ import eu.darken.capod.main.core.PermissionTool import eu.darken.capod.main.ui.overview.cards.BluetoothDisabledVH import eu.darken.capod.main.ui.overview.cards.MissingMainDeviceVH import eu.darken.capod.main.ui.overview.cards.PermissionCardVH -import eu.darken.capod.main.ui.overview.cards.pods.* +import eu.darken.capod.main.ui.overview.cards.pods.DualPodsCardVH +import eu.darken.capod.main.ui.overview.cards.pods.SinglePodsCardVH +import eu.darken.capod.main.ui.overview.cards.pods.UnknownPodDeviceCardVH import eu.darken.capod.monitor.core.PodMonitor import eu.darken.capod.monitor.core.worker.MonitorControl +import eu.darken.capod.pods.core.DualPodDevice import eu.darken.capod.pods.core.PodDevice -import eu.darken.capod.pods.core.apple.BasicSingleApplePods -import eu.darken.capod.pods.core.apple.DualApplePods -import eu.darken.capod.pods.core.apple.SingleApplePods +import eu.darken.capod.pods.core.SinglePodDevice import kotlinx.coroutines.delay import kotlinx.coroutines.flow.* import kotlinx.coroutines.isActive import java.time.Instant -import java.util.* import javax.inject.Inject @HiltViewModel class OverviewFragmentVM @Inject constructor( - handle: SavedStateHandle, + @Suppress("UNUSED_PARAMETER") handle: SavedStateHandle, dispatcherProvider: DispatcherProvider, private val monitorControl: MonitorControl, private val podMonitor: PodMonitor, @@ -74,7 +74,7 @@ class OverviewFragmentVM @Inject constructor( monitorControl.startMonitor() } } - .map { Unit } + .map { } .asLiveData2() val requestPermissionEvent = SingleLiveEvent() @@ -129,19 +129,13 @@ class OverviewFragmentVM @Inject constructor( pods.map { val now = Instant.now() when (it) { - is DualApplePods -> DualApplePodsCardVH.Item( + is DualPodDevice -> DualPodsCardVH.Item( now = now, device = it, showDebug = isDebugMode, isMainPod = it == mainPod, ) - is SingleApplePods -> SingleApplePodsCardVH.Item( - now = now, - device = it, - showDebug = isDebugMode, - isMainPod = it == mainPod, - ) - is BasicSingleApplePods -> BasicSingleApplePodsCardVH.Item( + is SinglePodDevice -> SinglePodsCardVH.Item( now = now, device = it, showDebug = isDebugMode, diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/BasicSingleApplePodsCardVH.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/BasicSingleApplePodsCardVH.kt deleted file mode 100644 index 7d1a19c2..00000000 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/BasicSingleApplePodsCardVH.kt +++ /dev/null @@ -1,58 +0,0 @@ -package eu.darken.capod.main.ui.overview.cards.pods - -import android.graphics.Typeface -import android.view.ViewGroup -import androidx.core.view.isGone -import eu.darken.capod.R -import eu.darken.capod.common.lists.binding -import eu.darken.capod.databinding.OverviewPodsAppleSingleBasicItemBinding -import eu.darken.capod.pods.core.apple.BasicSingleApplePods -import eu.darken.capod.pods.core.getBatteryDrawable -import eu.darken.capod.pods.core.getBatteryLevelHeadset -import eu.darken.capod.pods.core.lastSeenFormatted -import java.time.Instant - -class BasicSingleApplePodsCardVH(parent: ViewGroup) : - PodDeviceVH( - R.layout.overview_pods_apple_single_basic_item, - parent - ) { - - override val viewBinding = lazy { OverviewPodsAppleSingleBasicItemBinding.bind(itemView) } - - override val onBindData = binding(payload = true) { item: Item -> - val device = item.device - - name.apply { - text = device.getLabel(context) - if (item.isMainPod) setTypeface(typeface, Typeface.BOLD) - else setTypeface(typeface, Typeface.NORMAL) - } - - deviceIcon.setImageResource(device.iconRes) - - lastSeen.text = device.lastSeenFormatted(item.now) - - reception.text = item.getReceptionText() - - batteryLabel.text = device.getBatteryLevelHeadset(context) - batteryIcon.setImageResource(getBatteryDrawable(device.batteryHeadsetPercent)) - - status.apply { - val sb = StringBuilder() - if (item.showDebug) { - sb.append("--- Debug ---") - sb.append("\n").append(device.rawDataHex) - } - text = sb - isGone = !item.showDebug - } - } - - data class Item( - override val now: Instant, - override val device: BasicSingleApplePods, - override val showDebug: Boolean, - override val isMainPod: Boolean, - ) : PodDeviceVH.Item -} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/DualApplePodsCardVH.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/DualApplePodsCardVH.kt deleted file mode 100644 index 92cee790..00000000 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/DualApplePodsCardVH.kt +++ /dev/null @@ -1,112 +0,0 @@ -package eu.darken.capod.main.ui.overview.cards.pods - -import android.graphics.Typeface -import android.view.ViewGroup -import androidx.core.view.isGone -import androidx.core.view.isInvisible -import eu.darken.capod.R -import eu.darken.capod.common.lists.binding -import eu.darken.capod.databinding.OverviewPodsAppleDualItemBinding -import eu.darken.capod.pods.core.* -import eu.darken.capod.pods.core.apple.DualApplePods -import eu.darken.capod.pods.core.apple.DualApplePods.DeviceColor -import eu.darken.capod.pods.core.apple.DualApplePods.LidState -import java.time.Duration -import java.time.Instant - -class DualApplePodsCardVH(parent: ViewGroup) : - PodDeviceVH( - R.layout.overview_pods_apple_dual_item, - parent - ) { - - override val viewBinding = lazy { OverviewPodsAppleDualItemBinding.bind(itemView) } - - override val onBindData = binding(payload = true) { item: Item -> - val device = item.device - name.apply { - val sb = StringBuilder(device.getLabel(context)) - if (!listOf(DeviceColor.WHITE, DeviceColor.UNKNOWN).contains(device.deviceColor)) { - sb.append(" (${device.deviceColor.name})") - } - text = sb - if (item.isMainPod) setTypeface(typeface, Typeface.BOLD) - else setTypeface(typeface, Typeface.NORMAL) - if (item.showDebug) { - append(" [${device.primaryPod.name}]") - } - } - deviceIcon.setImageResource(device.iconRes) - - lastSeen.text = context.getString(R.string.last_seen_x, device.lastSeenFormatted(item.now)) - firstSeen.text = context.getString(R.string.first_seen_x, device.firstSeenFormatted(item.now)) - firstSeen.isGone = Duration.between(device.seenFirstAt, device.seenLastAt).toMinutes() < 1 - - reception.text = item.getReceptionText() - - // Left Pod - device.apply { - podLeftBatteryIcon.setImageResource(getBatteryDrawable(batteryLeftPodPercent)) - podLeftBatteryLabel.text = getBatteryLevelLeftPod(context) - - podLeftChargingIcon.isInvisible = !isLeftPodCharging - podLeftChargingLabel.isInvisible = !isLeftPodCharging - - podLeftMicrophoneIcon.isInvisible = !isLeftPodMicrophone - podLeftMicrophoneLabel.isInvisible = !isLeftPodMicrophone - - podLeftWearIcon.isInvisible = !isLeftPodInEar - podLeftWearLabel.isInvisible = !isLeftPodInEar - } - - // Right Pod - device.apply { - podRightBatteryIcon.setImageResource(getBatteryDrawable(batteryRightPodPercent)) - podRightBatteryLabel.text = getBatteryLevelRightPod(context) - - podRightChargingIcon.isInvisible = !isRightPodCharging - podRightChargingLabel.isInvisible = !isRightPodCharging - - podRightMicrophoneIcon.isInvisible = !isRightPodMicrophone - podRightMicrophoneLabel.isInvisible = !isRightPodMicrophone - - podRightWearIcon.isInvisible = !isRightPodInEar - podRightWearLabel.isInvisible = !isRightPodInEar - } - - // Case - device.apply { - podCaseBatteryIcon.setImageResource(getBatteryDrawable(batteryCasePercent)) - podCaseBatteryLabel.text = getBatteryLevelCase(context) - - podCaseChargingIcon.isInvisible = !isCaseCharging - podCaseChargingLabel.isInvisible = !isCaseCharging - - podCaseLidLabel.text = when (caseLidState) { - LidState.OPEN -> context.getString(R.string.pods_case_status_open_label) - LidState.CLOSED -> context.getString(R.string.pods_case_status_closed_label) - else -> context.getString(R.string.pods_case_unknown_state) - } - - val hideInfo = !listOf(LidState.OPEN, LidState.CLOSED).contains(caseLidState) - podCaseLidIcon.isInvisible = hideInfo - podCaseLidLabel.isInvisible = hideInfo - } - - status.apply { - val sb = StringBuilder(device.getConnectionStateLabel(context)) - if (item.showDebug) { - sb.append("\n\n").append("---Debug---") - sb.append("\n").append(device.rawDataHex) - } - text = sb - } - } - - data class Item( - override val now: Instant, - override val device: DualApplePods, - override val showDebug: Boolean, - override val isMainPod: Boolean, - ) : PodDeviceVH.Item -} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/DualPodsCardVH.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/DualPodsCardVH.kt new file mode 100644 index 00000000..c76491e6 --- /dev/null +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/DualPodsCardVH.kt @@ -0,0 +1,165 @@ +package eu.darken.capod.main.ui.overview.cards.pods + +import android.graphics.Typeface +import android.view.ViewGroup +import androidx.core.view.isGone +import androidx.core.view.isInvisible +import eu.darken.capod.R +import eu.darken.capod.common.lists.binding +import eu.darken.capod.databinding.OverviewPodsDualItemBinding +import eu.darken.capod.pods.core.* +import eu.darken.capod.pods.core.apple.DualAirPods +import eu.darken.capod.pods.core.apple.DualAirPods.LidState +import java.time.Duration +import java.time.Instant + +class DualPodsCardVH(parent: ViewGroup) : + PodDeviceVH( + R.layout.overview_pods_dual_item, + parent + ) { + + override val viewBinding = lazy { OverviewPodsDualItemBinding.bind(itemView) } + + override val onBindData = binding(payload = true) { item: Item -> + val device = item.device + name.apply { + val sb = StringBuilder(device.getLabel(context)) + if (device is HasPodStyle && item.showDebug) { + val style = device.podStyle + sb.append(" (${style.getColor(context)})") + } + text = sb + + if (item.isMainPod) setTypeface(typeface, Typeface.BOLD) + else setTypeface(typeface, Typeface.NORMAL) + + if (device is DualAirPods && item.showDebug) { + append(" [${device.primaryPod.name}]") + } + } + deviceIcon.setImageResource(device.iconRes) + + lastSeen.text = context.getString(R.string.last_seen_x, device.lastSeenFormatted(item.now)) + firstSeen.text = context.getString(R.string.first_seen_x, device.firstSeenFormatted(item.now)) + firstSeen.isGone = Duration.between(device.seenFirstAt, device.seenLastAt).toMinutes() < 1 + + reception.text = item.getReceptionText() + + // Pods battery state + device.apply { + podLeftBatteryIcon.setImageResource(getBatteryDrawable(batteryLeftPodPercent)) + podLeftBatteryLabel.text = getBatteryLevelLeftPod(context) + + podRightBatteryIcon.setImageResource(getBatteryDrawable(batteryRightPodPercent)) + podRightBatteryLabel.text = getBatteryLevelRightPod(context) + } + + // Pods charging state + device.apply { + if (this is HasChargeDetectionDual) { + podLeftChargingIcon.isInvisible = !isLeftPodCharging + podLeftChargingLabel.isInvisible = !isLeftPodCharging + + podRightChargingIcon.isInvisible = !isRightPodCharging + podRightChargingLabel.isInvisible = !isRightPodCharging + } else { + podLeftChargingIcon.isGone = true + podLeftChargingLabel.isGone = true + + podRightChargingIcon.isGone = true + podRightChargingLabel.isGone = true + } + } + + // Microphone state + device.apply { + if (this is HasDualMicrophone) { + podLeftMicrophoneIcon.isInvisible = !isLeftPodMicrophone + podLeftMicrophoneLabel.isInvisible = !isLeftPodMicrophone + + podRightMicrophoneIcon.isInvisible = !isRightPodMicrophone + podRightMicrophoneLabel.isInvisible = !isRightPodMicrophone + } else { + podLeftMicrophoneIcon.isGone = true + podLeftMicrophoneLabel.isGone = true + + podRightMicrophoneIcon.isGone = true + podRightMicrophoneLabel.isGone = true + } + } + + // Pods wear state + device.apply { + if (this is HasEarDetectionDual) { + podLeftWearIcon.isInvisible = !isLeftPodInEar + podLeftWearLabel.isInvisible = !isLeftPodInEar + + podRightWearIcon.isInvisible = !isRightPodInEar + podRightWearLabel.isInvisible = !isRightPodInEar + } else { + podLeftWearIcon.isGone = true + podLeftWearLabel.isGone = true + + podRightWearIcon.isGone = true + podRightWearLabel.isGone = true + } + } + + // Case charge state + device.apply { + if (this is HasCase) { + podCaseBatteryIcon.isGone = false + podCaseBatteryIcon.setImageResource(getBatteryDrawable(batteryCasePercent)) + podCaseBatteryLabel.text = getBatteryLevelCase(context) + + podCaseChargingIcon.isInvisible = !isCaseCharging + podCaseChargingLabel.isInvisible = !isCaseCharging + } else { + podCaseBatteryIcon.isGone = true + podCaseBatteryLabel.isGone = true + + podCaseChargingIcon.isGone = true + podCaseChargingLabel.isGone = true + } + } + + // Case lid state + device.apply { + if (this is DualAirPods) { + podCaseLidLabel.text = when (caseLidState) { + LidState.OPEN -> context.getString(R.string.pods_case_status_open_label) + LidState.CLOSED -> context.getString(R.string.pods_case_status_closed_label) + else -> context.getString(R.string.pods_case_unknown_state) + } + + val hideInfo = !listOf(LidState.OPEN, LidState.CLOSED).contains(caseLidState) + podCaseLidIcon.isInvisible = hideInfo + podCaseLidLabel.isInvisible = hideInfo + } else { + podCaseLidIcon.isGone = true + podCaseLidLabel.isGone = true + } + } + + // Connection state + device.apply { + val sb = StringBuilder() + if (this is HasStateDetection) { + sb.append(state.getLabel(context)) + } + if (item.showDebug) { + sb.append("\n\n").append("---Debug---") + sb.append("\n").append(rawDataHex) + } + status.text = sb + } + } + + data class Item( + override val now: Instant, + override val device: DualPodDevice, + override val showDebug: Boolean, + override val isMainPod: Boolean, + ) : PodDeviceVH.Item +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/SingleApplePodsCardVH.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/SingleApplePodsCardVH.kt deleted file mode 100644 index d9a99905..00000000 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/SingleApplePodsCardVH.kt +++ /dev/null @@ -1,65 +0,0 @@ -package eu.darken.capod.main.ui.overview.cards.pods - -import android.graphics.Typeface -import android.view.ViewGroup -import androidx.core.view.isGone -import androidx.core.view.isInvisible -import eu.darken.capod.R -import eu.darken.capod.common.lists.binding -import eu.darken.capod.databinding.OverviewPodsAppleSingleItemBinding -import eu.darken.capod.pods.core.apple.SingleApplePods -import eu.darken.capod.pods.core.getBatteryDrawable -import eu.darken.capod.pods.core.getBatteryLevelHeadset -import eu.darken.capod.pods.core.lastSeenFormatted -import java.time.Instant - -class SingleApplePodsCardVH(parent: ViewGroup) : - PodDeviceVH( - R.layout.overview_pods_apple_single_item, - parent - ) { - - override val viewBinding = lazy { OverviewPodsAppleSingleItemBinding.bind(itemView) } - - override val onBindData = binding(payload = true) { item: Item -> - val device = item.device - - name.apply { - text = device.getLabel(context) - if (item.isMainPod) setTypeface(typeface, Typeface.BOLD) - else setTypeface(typeface, Typeface.NORMAL) - } - - deviceIcon.setImageResource(device.iconRes) - - lastSeen.text = device.lastSeenFormatted(item.now) - - reception.text = item.getReceptionText() - - batteryLabel.text = device.getBatteryLevelHeadset(context) - batteryIcon.setImageResource(getBatteryDrawable(device.batteryHeadsetPercent)) - - chargingIcon.isInvisible = device.isHeadsetBeingCharged - chargingLabel.isInvisible = device.isHeadsetBeingCharged - - wearIcon.isInvisible = device.isHeadphonesBeingWorn - wearLabel.isInvisible = device.isHeadphonesBeingWorn - - status.apply { - val sb = StringBuilder() - if (item.showDebug) { - sb.append("--- Debug ---") - sb.append("\n").append(device.rawDataHex) - } - text = sb - isGone = !item.showDebug - } - } - - data class Item( - override val now: Instant, - override val device: SingleApplePods, - override val showDebug: Boolean, - override val isMainPod: Boolean, - ) : PodDeviceVH.Item -} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/SinglePodsCardVH.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/SinglePodsCardVH.kt new file mode 100644 index 00000000..26837e2c --- /dev/null +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/SinglePodsCardVH.kt @@ -0,0 +1,83 @@ +package eu.darken.capod.main.ui.overview.cards.pods + +import android.graphics.Typeface +import android.view.ViewGroup +import androidx.core.view.isGone +import androidx.core.view.isInvisible +import eu.darken.capod.R +import eu.darken.capod.common.lists.binding +import eu.darken.capod.databinding.OverviewPodsSingleItemBinding +import eu.darken.capod.pods.core.* +import java.time.Instant + +class SinglePodsCardVH(parent: ViewGroup) : + PodDeviceVH( + R.layout.overview_pods_single_item, + parent + ) { + + override val viewBinding = lazy { OverviewPodsSingleItemBinding.bind(itemView) } + + override val onBindData = binding(payload = true) { item: Item -> + val device = item.device + + name.apply { + text = device.getLabel(context) + if (item.isMainPod) setTypeface(typeface, Typeface.BOLD) + else setTypeface(typeface, Typeface.NORMAL) + } + + deviceIcon.setImageResource(device.iconRes) + + lastSeen.text = device.lastSeenFormatted(item.now) + + reception.text = item.getReceptionText() + + // Battery level + device.apply { + batteryLabel.text = getBatteryLevelHeadset(context) + batteryIcon.setImageResource(getBatteryDrawable(batteryHeadsetPercent)) + } + + // Charge state + device.apply { + if (this !is HasChargeDetection) { + chargingIcon.isInvisible = true + chargingLabel.isInvisible = true + return@apply + } + + chargingIcon.isInvisible = isHeadsetBeingCharged + chargingLabel.isInvisible = isHeadsetBeingCharged + } + + // Has ear detection + device.apply { + if (this !is HasEarDetection) { + wearIcon.isInvisible = true + wearLabel.isInvisible = true + return@apply + } + + wearIcon.isInvisible = isBeingWorn + wearLabel.isInvisible = isBeingWorn + } + + status.apply { + val sb = StringBuilder() + if (item.showDebug) { + sb.append("--- Debug ---") + sb.append("\n").append(device.rawDataHex) + } + text = sb + isGone = !item.showDebug + } + } + + data class Item( + override val now: Instant, + override val device: SinglePodDevice, + override val showDebug: Boolean, + override val isMainPod: Boolean, + ) : PodDeviceVH.Item +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/monitor/ui/MonitorNotificationViewFactory.kt b/app/src/main/java/eu/darken/capod/monitor/ui/MonitorNotificationViewFactory.kt index f89e2299..6848e134 100644 --- a/app/src/main/java/eu/darken/capod/monitor/ui/MonitorNotificationViewFactory.kt +++ b/app/src/main/java/eu/darken/capod/monitor/ui/MonitorNotificationViewFactory.kt @@ -6,8 +6,7 @@ import android.widget.RemoteViews import dagger.hilt.android.qualifiers.ApplicationContext import eu.darken.capod.R import eu.darken.capod.pods.core.* -import eu.darken.capod.pods.core.apple.BasicSingleApplePods -import eu.darken.capod.pods.core.apple.DualApplePods +import eu.darken.capod.pods.core.apple.DualAirPods import eu.darken.capod.pods.core.apple.SingleApplePods import javax.inject.Inject @@ -17,13 +16,12 @@ class MonitorNotificationViewFactory @Inject constructor( ) { fun createContentView(device: PodDevice): RemoteViews = when (device) { - is DualApplePods -> createDualApplePods(device) + is DualAirPods -> createDualApplePods(device) is SingleApplePods -> createSingleApplePods(device) - is BasicSingleApplePods -> createSingleBasicApplePods(device) else -> createUnknownDevice(device) } - private fun createDualApplePods(device: DualApplePods): RemoteViews = RemoteViews( + private fun createDualApplePods(device: DualAirPods): RemoteViews = RemoteViews( context.packageName, R.layout.monitor_notification_dual_pods_small ).apply { @@ -52,19 +50,12 @@ class MonitorNotificationViewFactory @Inject constructor( setTextViewText(R.id.headphones_label, getLabel(context)) setImageViewResource(R.id.headphones_battery_icon, getBatteryDrawable(batteryHeadsetPercent)) setTextViewText(R.id.headphones_battery_label, getBatteryLevelHeadset(context)) - setViewVisibility(R.id.headphones_charging, if (isHeadsetBeingCharged) View.VISIBLE else View.GONE) - setViewVisibility(R.id.headphones_worn, if (isHeadphonesBeingWorn) View.VISIBLE else View.GONE) - } - } - - private fun createSingleBasicApplePods(device: BasicSingleApplePods): RemoteViews = RemoteViews( - context.packageName, - R.layout.monitor_notification_single_pods_basic_small - ).apply { - device.apply { - setTextViewText(R.id.headphones_label, getLabel(context)) - setImageViewResource(R.id.headphones_battery_icon, getBatteryDrawable(batteryHeadsetPercent)) - setTextViewText(R.id.headphones_battery_label, getBatteryLevelHeadset(context)) + if (this is HasEarDetection) { + setViewVisibility(R.id.headphones_worn, if (isBeingWorn) View.VISIBLE else View.GONE) + } + if (this is HasChargeDetectionDual) { + setViewVisibility(R.id.headphones_charging, if (isHeadsetBeingCharged) View.VISIBLE else View.GONE) + } } } diff --git a/app/src/main/java/eu/darken/capod/pods/core/HasDualPods.kt b/app/src/main/java/eu/darken/capod/pods/core/DualPodDevice.kt similarity index 81% rename from app/src/main/java/eu/darken/capod/pods/core/HasDualPods.kt rename to app/src/main/java/eu/darken/capod/pods/core/DualPodDevice.kt index 15570a06..74276382 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/HasDualPods.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/DualPodDevice.kt @@ -1,6 +1,6 @@ package eu.darken.capod.pods.core -interface HasDualPods { +interface DualPodDevice : PodDevice { enum class Pod { LEFT, diff --git a/app/src/main/java/eu/darken/capod/pods/core/HasChargeDetection.kt b/app/src/main/java/eu/darken/capod/pods/core/HasChargeDetection.kt new file mode 100644 index 00000000..83d66f03 --- /dev/null +++ b/app/src/main/java/eu/darken/capod/pods/core/HasChargeDetection.kt @@ -0,0 +1,7 @@ +package eu.darken.capod.pods.core + +interface HasChargeDetection { + + val isHeadsetBeingCharged: Boolean + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/pods/core/HasChargeDetectionDual.kt b/app/src/main/java/eu/darken/capod/pods/core/HasChargeDetectionDual.kt new file mode 100644 index 00000000..34000a86 --- /dev/null +++ b/app/src/main/java/eu/darken/capod/pods/core/HasChargeDetectionDual.kt @@ -0,0 +1,14 @@ +package eu.darken.capod.pods.core + +interface HasChargeDetectionDual : HasChargeDetection { + + val isLeftPodCharging: Boolean + + val isRightPodCharging: Boolean + + val isEitherPodCharging: Boolean + get() = isLeftPodCharging || isRightPodCharging + + override val isHeadsetBeingCharged: Boolean + get() = isEitherPodCharging +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/pods/core/HasDualMicrophone.kt b/app/src/main/java/eu/darken/capod/pods/core/HasDualMicrophone.kt new file mode 100644 index 00000000..e20215e8 --- /dev/null +++ b/app/src/main/java/eu/darken/capod/pods/core/HasDualMicrophone.kt @@ -0,0 +1,9 @@ +package eu.darken.capod.pods.core + +interface HasDualMicrophone { + + val isLeftPodMicrophone: Boolean + + val isRightPodMicrophone: Boolean + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/pods/core/HasEarDetection.kt b/app/src/main/java/eu/darken/capod/pods/core/HasEarDetection.kt index 326c4c94..ee88849c 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/HasEarDetection.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/HasEarDetection.kt @@ -1,6 +1,6 @@ package eu.darken.capod.pods.core -interface HasEarDetection : PodDevice { +interface HasEarDetection { val isBeingWorn: Boolean diff --git a/app/src/main/java/eu/darken/capod/pods/core/HasDualEarDetection.kt b/app/src/main/java/eu/darken/capod/pods/core/HasEarDetectionDual.kt similarity index 84% rename from app/src/main/java/eu/darken/capod/pods/core/HasDualEarDetection.kt rename to app/src/main/java/eu/darken/capod/pods/core/HasEarDetectionDual.kt index e609c755..98946d24 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/HasDualEarDetection.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/HasEarDetectionDual.kt @@ -1,6 +1,6 @@ package eu.darken.capod.pods.core -interface HasDualEarDetection : HasEarDetection { +interface HasEarDetectionDual : HasEarDetection { val isLeftPodInEar: Boolean diff --git a/app/src/main/java/eu/darken/capod/pods/core/HasPodStyle.kt b/app/src/main/java/eu/darken/capod/pods/core/HasPodStyle.kt new file mode 100644 index 00000000..2d848be1 --- /dev/null +++ b/app/src/main/java/eu/darken/capod/pods/core/HasPodStyle.kt @@ -0,0 +1,19 @@ +package eu.darken.capod.pods.core + +import android.content.Context +import androidx.annotation.ColorRes + +interface HasPodStyle { + + val podStyle: PodStyle + + interface PodStyle { + fun getLabel(context: Context): String + + @ColorRes + fun getColor(context: Context): Int + + val identifier: String + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/pods/core/HasStateDetection.kt b/app/src/main/java/eu/darken/capod/pods/core/HasStateDetection.kt new file mode 100644 index 00000000..3aa6cd2c --- /dev/null +++ b/app/src/main/java/eu/darken/capod/pods/core/HasStateDetection.kt @@ -0,0 +1,12 @@ +package eu.darken.capod.pods.core + +import android.content.Context + +interface HasStateDetection { + + val state: State + + interface State { + fun getLabel(context: Context): String + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/pods/core/PodDeviceExtensions.kt b/app/src/main/java/eu/darken/capod/pods/core/PodDeviceExtensions.kt index 933fa444..12069f6a 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/PodDeviceExtensions.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/PodDeviceExtensions.kt @@ -8,11 +8,11 @@ import java.time.Duration import java.time.Instant import kotlin.math.roundToInt -fun HasDualPods.getBatteryLevelLeftPod(context: Context): String = +fun DualPodDevice.getBatteryLevelLeftPod(context: Context): String = batteryLeftPodPercent?.let { "${(it * 100).roundToInt()}%" } ?: context.getString(R.string.general_value_not_available_label) -fun HasDualPods.getBatteryLevelRightPod(context: Context): String = +fun DualPodDevice.getBatteryLevelRightPod(context: Context): String = batteryRightPodPercent?.let { "${(it * 100).roundToInt()}%" } ?: context.getString(R.string.general_value_not_available_label) @@ -20,7 +20,7 @@ fun HasCase.getBatteryLevelCase(context: Context): String = batteryCasePercent?.let { "${(it * 100).roundToInt()}%" } ?: context.getString(R.string.general_value_not_available_label) -fun HasSinglePod.getBatteryLevelHeadset(context: Context): String = +fun SinglePodDevice.getBatteryLevelHeadset(context: Context): String = batteryHeadsetPercent?.let { "${(it * 100).roundToInt()}%" } ?: context.getString(R.string.general_value_not_available_label) diff --git a/app/src/main/java/eu/darken/capod/pods/core/HasSinglePod.kt b/app/src/main/java/eu/darken/capod/pods/core/SinglePodDevice.kt similarity index 65% rename from app/src/main/java/eu/darken/capod/pods/core/HasSinglePod.kt rename to app/src/main/java/eu/darken/capod/pods/core/SinglePodDevice.kt index bbde7377..d00a1176 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/HasSinglePod.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/SinglePodDevice.kt @@ -1,6 +1,6 @@ package eu.darken.capod.pods.core -interface HasSinglePod { +interface SinglePodDevice : PodDevice { val batteryHeadsetPercent: Float? } \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/AppleFactory.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/AppleFactory.kt index 47d295ef..dc854507 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/AppleFactory.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/AppleFactory.kt @@ -6,6 +6,7 @@ import eu.darken.capod.common.debug.logging.asLog import eu.darken.capod.common.debug.logging.log import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.pods.core.PodDevice +import eu.darken.capod.pods.core.apple.misc.UnknownAppleDevice import eu.darken.capod.pods.core.apple.protocol.ContinuityProtocol import eu.darken.capod.pods.core.apple.protocol.ProximityPairing import kotlinx.coroutines.sync.Mutex diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/AppleFactoryModule.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/AppleFactoryModule.kt index 363c6f62..2769c2df 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/AppleFactoryModule.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/AppleFactoryModule.kt @@ -7,7 +7,7 @@ import dagger.hilt.components.SingletonComponent import dagger.multibindings.IntoSet import eu.darken.capod.pods.core.apple.airpods.* import eu.darken.capod.pods.core.apple.beats.* -import eu.darken.capod.pods.core.apple.fakes.Twsi99999 +import eu.darken.capod.pods.core.apple.misc.Twsi99999 @InstallIn(SingletonComponent::class) @Module diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/ApplePodsFactory.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/ApplePodsFactory.kt index 1756f10c..0a861a2a 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/ApplePodsFactory.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/ApplePodsFactory.kt @@ -5,7 +5,9 @@ import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE import eu.darken.capod.common.debug.logging.log import eu.darken.capod.common.lowerNibble import eu.darken.capod.common.upperNibble +import eu.darken.capod.pods.core.HasCase import eu.darken.capod.pods.core.PodDevice +import eu.darken.capod.pods.core.apple.airpods.AirPodsPro import eu.darken.capod.pods.core.apple.protocol.ProximityPairing import java.time.Duration import java.time.Instant @@ -21,7 +23,7 @@ abstract class ApplePodsFactory(private val tag: String) { val deviceColor: UByte, ) - fun ProximityPairing.Message.getApplePodsMarkings(): Markings = Markings( + private fun ProximityPairing.Message.getApplePodsMarkings(): Markings = Markings( vendor = ProximityPairing.CONTINUITY_PROTOCOL_MESSAGE_TYPE_PROXIMITY_PAIRING, length = ProximityPairing.PAIRING_MESSAGE_LENGTH.toUByte(), device = (((data[1].toInt() and 255) shl 8) or (data[2].toInt() and 255)).toUShort(), @@ -70,6 +72,22 @@ abstract class ApplePodsFactory(private val tag: String) { internal val knownDevices = mutableMapOf() + + fun KnownDevice.getLatestCaseBattery(): Float? = history + .filterIsInstance() + .mapNotNull { it.batteryCasePercent } + .lastOrNull() + + fun KnownDevice.getLatestCaseLidState(basic: DualAirPods): DualAirPods.LidState? { + val definitive = setOf(DualAirPods.LidState.OPEN, DualAirPods.LidState.CLOSED) + if (definitive.contains(basic.caseLidState)) return null + + return history + .filterIsInstance() + .lastOrNull { it.caseLidState != DualAirPods.LidState.NOT_IN_CASE } + ?.caseLidState + } + internal open fun searchHistory(current: PodType): KnownDevice? { val scanResult = current.scanResult val message = current.proximityMessage diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/BasicSingleApplePods.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/BasicSingleApplePods.kt deleted file mode 100644 index a4dd3a5e..00000000 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/BasicSingleApplePods.kt +++ /dev/null @@ -1,19 +0,0 @@ -package eu.darken.capod.pods.core.apple - -import eu.darken.capod.common.debug.logging.log -import eu.darken.capod.common.lowerNibble -import eu.darken.capod.pods.core.HasSinglePod - -interface BasicSingleApplePods : ApplePods, HasSinglePod { - - override val batteryHeadsetPercent: Float? - get() = when (val value = rawPodsBattery.lowerNibble.toInt()) { - 15 -> null - else -> if (value > 10) { - log { "Left pod: Above 100% battery: $value" } - 1.0f - } else { - (value / 10f) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/BasicSingleApplePodsFactory.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/BasicSingleApplePodsFactory.kt deleted file mode 100644 index 6b2aeb3e..00000000 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/BasicSingleApplePodsFactory.kt +++ /dev/null @@ -1,3 +0,0 @@ -package eu.darken.capod.pods.core.apple - -abstract class BasicSingleApplePodsFactory(private val tag: String) : ApplePodsFactory(tag) \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/DualApplePods.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/DualAirPods.kt similarity index 81% rename from app/src/main/java/eu/darken/capod/pods/core/apple/DualApplePods.kt rename to app/src/main/java/eu/darken/capod/pods/core/apple/DualAirPods.kt index 60eb8574..b4ee6750 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/DualApplePods.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/DualAirPods.kt @@ -7,12 +7,11 @@ import eu.darken.capod.common.debug.logging.log import eu.darken.capod.common.isBitSet import eu.darken.capod.common.lowerNibble import eu.darken.capod.common.upperNibble -import eu.darken.capod.pods.core.HasCase -import eu.darken.capod.pods.core.HasDualEarDetection -import eu.darken.capod.pods.core.HasDualPods -import eu.darken.capod.pods.core.HasDualPods.Pod +import eu.darken.capod.pods.core.* +import eu.darken.capod.pods.core.DualPodDevice.Pod -interface DualApplePods : ApplePods, HasDualPods, HasDualEarDetection, HasCase { +interface DualAirPods : ApplePods, HasChargeDetectionDual, DualPodDevice, HasEarDetectionDual, HasCase, + HasStateDetection, HasDualMicrophone, HasAppleColor { val primaryPod: Pod get() = when (rawStatus.isBitSet(5)) { @@ -86,23 +85,23 @@ interface DualApplePods : ApplePods, HasDualPods, HasDualEarDetection, HasCase { * The data flip bit is set if the left pod is primary. * For the pod that is in the case, this is flipped again though. */ - val isLeftPodMicrophone: Boolean + override val isLeftPodMicrophone: Boolean get() = rawStatus.isBitSet(5) xor isThisPodInThecase /** * The data flip bit is UNset if the right pod is primary. * For the pod that is in the case, this is flipped again though. */ - val isRightPodMicrophone: Boolean + override val isRightPodMicrophone: Boolean get() = !rawStatus.isBitSet(5) xor isThisPodInThecase - val isLeftPodCharging: Boolean + override val isLeftPodCharging: Boolean get() = when (areValuesFlipped) { false -> rawFlags.isBitSet(0) true -> rawFlags.isBitSet(1) } - val isRightPodCharging: Boolean + override val isRightPodCharging: Boolean get() = when (areValuesFlipped) { false -> rawFlags.isBitSet(1) true -> rawFlags.isBitSet(0) @@ -143,35 +142,10 @@ interface DualApplePods : ApplePods, HasDualPods, HasDualEarDetection, HasCase { UNKNOWN(0xFF..0xFF); } - val deviceColor: DeviceColor - get() = DeviceColor.values().firstOrNull { it.raw == rawDeviceColor } ?: DeviceColor.UNKNOWN - - enum class DeviceColor(val raw: UByte?) { - - WHITE(0x00), - BLACK(0x01), - RED(0x02), - BLUE(0x03), - PINK(0x04), - GRAY(0x05), - SILVER(0x06), - GOLD(0x07), - ROSE_GOLD(0x08), - SPACE_GRAY(0x09), - DARK_BLUE(0x0a), - LIGHT_BLUE(0x0b), - YELLOW(0x0c), - UNKNOWN(null); - - constructor(raw: Int) : this(raw.toUByte()) - } - - val connectionState: ConnectionState + override val state: ConnectionState get() = ConnectionState.values().firstOrNull { rawSuffix == it.raw } ?: ConnectionState.UNKNOWN - fun getConnectionStateLabel(context: Context): String = context.getString(connectionState.labelRes) - - enum class ConnectionState(val raw: UByte?, @StringRes val labelRes: Int) { + enum class ConnectionState(val raw: UByte?, @StringRes val labelRes: Int) : HasStateDetection.State { DISCONNECTED(0x00, R.string.pods_connection_state_disconnected_label), IDLE(0x04, R.string.pods_connection_state_idle_label), MUSIC(0x05, R.string.pods_connection_state_music_label), @@ -180,6 +154,8 @@ interface DualApplePods : ApplePods, HasDualPods, HasDualEarDetection, HasCase { HANGING_UP(0x09, R.string.pods_connection_state_hanging_up_label), UNKNOWN(null, R.string.pods_connection_state_unknown_label); + override fun getLabel(context: Context): String = context.getString(labelRes) + constructor(raw: Int, @StringRes labelRes: Int) : this(raw.toUByte(), labelRes) } diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/DualApplePodsFactory.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/DualApplePodsFactory.kt index 4e5829c6..423d4d56 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/DualApplePodsFactory.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/DualApplePodsFactory.kt @@ -3,18 +3,17 @@ package eu.darken.capod.pods.core.apple import eu.darken.capod.common.debug.logging.Logging.Priority.DEBUG import eu.darken.capod.common.debug.logging.log import eu.darken.capod.pods.core.PodDevice -import eu.darken.capod.pods.core.apple.airpods.AirPodsPro -abstract class DualApplePodsFactory(private val tag: String) : ApplePodsFactory(tag) { +abstract class DualApplePodsFactory(private val tag: String) : ApplePodsFactory(tag) { - fun DualApplePods.getCaseMatchMarkings() = SplitPodsMarkings( + fun DualAirPods.getCaseMatchMarkings() = SplitPodsMarkings( leftPodBattery = batteryLeftPodPercent, rightPodBattery = batteryRightPodPercent, microPhoneLeft = isLeftPodMicrophone, microPhoneRight = isRightPodMicrophone, chargingLeft = isLeftPodCharging, chargingRight = isRightPodCharging, - color = deviceColor, + color = rawDeviceColor, model = model ) @@ -28,36 +27,21 @@ abstract class DualApplePodsFactory(private val tag: String) : ApplePodsFactory< val microPhoneRight: Boolean, val chargingLeft: Boolean, val chargingRight: Boolean, - val color: DualApplePods.DeviceColor, + val color: UByte, val model: PodDevice.Model, ) - private fun Collection.findSplitPodsMatch(device: DualApplePods): Collection { + private fun Collection.findSplitPodsMatch(device: DualAirPods): Collection { val target = device.getCaseMatchMarkings() return filter { known -> known.history - .filterIsInstance() + .filterIsInstance() .any { it.getCaseMatchMarkings() == target } } } - fun KnownDevice.getLatestCaseBattery(): Float? = history - .filterIsInstance() - .mapNotNull { it.batteryCasePercent } - .lastOrNull() - - fun KnownDevice.getLatestCaseLidState(basic: DualApplePods): DualApplePods.LidState? { - val definitive = setOf(DualApplePods.LidState.OPEN, DualApplePods.LidState.CLOSED) - if (definitive.contains(basic.caseLidState)) return null - - return history - .filterIsInstance() - .lastOrNull { it.caseLidState != DualApplePods.LidState.NOT_IN_CASE } - ?.caseLidState - } - - override fun searchHistory(current: DualApplePods): KnownDevice? { + override fun searchHistory(current: DualAirPods): KnownDevice? { val basicResult = super.searchHistory(current) val caseIgnored = knownDevices.values.findSplitPodsMatch(current) diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/HasAppleColor.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/HasAppleColor.kt new file mode 100644 index 00000000..36329979 --- /dev/null +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/HasAppleColor.kt @@ -0,0 +1,38 @@ +package eu.darken.capod.pods.core.apple + +import android.content.Context +import eu.darken.capod.pods.core.HasPodStyle + +interface HasAppleColor : ApplePods, HasPodStyle { + + override val podStyle: HasPodStyle.PodStyle + get() = DeviceColor.values() + .firstOrNull { it.raw == rawDeviceColor } + ?: DeviceColor.UNKNOWN + + enum class DeviceColor(val raw: UByte?) : HasPodStyle.PodStyle { + WHITE(0x00), + BLACK(0x01), + RED(0x02), + BLUE(0x03), + PINK(0x04), + GRAY(0x05), + SILVER(0x06), + GOLD(0x07), + ROSE_GOLD(0x08), + SPACE_GRAY(0x09), + DARK_BLUE(0x0a), + LIGHT_BLUE(0x0b), + YELLOW(0x0c), + UNKNOWN(null); + + override fun getLabel(context: Context): String = this.name + + override fun getColor(context: Context): Int = android.R.color.white + + override val identifier: String + get() = name + + constructor(raw: Int) : this(raw.toUByte()) + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/SingleApplePods.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/SingleApplePods.kt index 6d325b3d..eea441b0 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/SingleApplePods.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/SingleApplePods.kt @@ -1,17 +1,22 @@ package eu.darken.capod.pods.core.apple -import eu.darken.capod.common.isBitSet -import eu.darken.capod.pods.core.HasEarDetection -import eu.darken.capod.pods.core.HasSinglePod +import eu.darken.capod.common.debug.logging.log +import eu.darken.capod.common.lowerNibble +import eu.darken.capod.pods.core.SinglePodDevice -interface SingleApplePods : BasicSingleApplePods, HasEarDetection, HasSinglePod { +/** + * Devices that only present a single charge level, e.g. most Beats devices + */ +interface SingleApplePods : ApplePods, SinglePodDevice, HasAppleColor { - val isHeadphonesBeingWorn: Boolean - get() = rawStatus.isBitSet(1) - - val isHeadsetBeingCharged: Boolean - get() = rawFlags.isBitSet(0) - - override val isBeingWorn: Boolean - get() = isHeadphonesBeingWorn + override val batteryHeadsetPercent: Float? + get() = when (val value = rawPodsBattery.lowerNibble.toInt()) { + 15 -> null + else -> if (value > 10) { + log { "Headset above 100% battery: $value" } + 1.0f + } else { + (value / 10f) + } + } } \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1.kt index ca9d8341..75e0f2b8 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1.kt @@ -4,7 +4,7 @@ import eu.darken.capod.common.bluetooth.BleScanResult import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.ApplePods -import eu.darken.capod.pods.core.apple.DualApplePods +import eu.darken.capod.pods.core.apple.DualAirPods import eu.darken.capod.pods.core.apple.DualApplePodsFactory import eu.darken.capod.pods.core.apple.protocol.ProximityPairing import java.time.Instant @@ -20,15 +20,15 @@ data class AirPodsGen1 constructor( override val confidence: Float = PodDevice.BASE_CONFIDENCE, private val rssiAverage: Int? = null, private val cachedBatteryPercentage: Float? = null, - private val cachedCaseState: DualApplePods.LidState? = null -) : DualApplePods { + private val cachedCaseState: DualAirPods.LidState? = null +) : DualAirPods { override val model: PodDevice.Model = PodDevice.Model.AIRPODS_GEN1 override val batteryCasePercent: Float? get() = super.batteryCasePercent ?: cachedBatteryPercentage - override val caseLidState: DualApplePods.LidState + override val caseLidState: DualAirPods.LidState get() = cachedCaseState ?: super.caseLidState override val rssi: Int diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2.kt index cf3a8d47..270140d9 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2.kt @@ -4,7 +4,7 @@ import eu.darken.capod.common.bluetooth.BleScanResult import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.ApplePods -import eu.darken.capod.pods.core.apple.DualApplePods +import eu.darken.capod.pods.core.apple.DualAirPods import eu.darken.capod.pods.core.apple.DualApplePodsFactory import eu.darken.capod.pods.core.apple.protocol.ProximityPairing import java.time.Instant @@ -20,15 +20,15 @@ data class AirPodsGen2 constructor( override val confidence: Float = PodDevice.BASE_CONFIDENCE, private val rssiAverage: Int? = null, private val cachedBatteryPercentage: Float? = null, - private val cachedCaseState: DualApplePods.LidState? = null -) : DualApplePods { + private val cachedCaseState: DualAirPods.LidState? = null +) : DualAirPods { override val model: PodDevice.Model = PodDevice.Model.AIRPODS_GEN2 override val batteryCasePercent: Float? get() = super.batteryCasePercent ?: cachedBatteryPercentage - override val caseLidState: DualApplePods.LidState + override val caseLidState: DualAirPods.LidState get() = cachedCaseState ?: super.caseLidState override val rssi: Int diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3.kt index b3bc919d..ae7c147c 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3.kt @@ -4,7 +4,7 @@ import eu.darken.capod.common.bluetooth.BleScanResult import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.ApplePods -import eu.darken.capod.pods.core.apple.DualApplePods +import eu.darken.capod.pods.core.apple.DualAirPods import eu.darken.capod.pods.core.apple.DualApplePodsFactory import eu.darken.capod.pods.core.apple.protocol.ProximityPairing import java.time.Instant @@ -20,14 +20,14 @@ data class AirPodsGen3 constructor( override val confidence: Float = PodDevice.BASE_CONFIDENCE, private val rssiAverage: Int? = null, private val cachedBatteryPercentage: Float? = null, - private val cachedCaseState: DualApplePods.LidState? = null -) : DualApplePods { + private val cachedCaseState: DualAirPods.LidState? = null +) : DualAirPods { override val model: PodDevice.Model = PodDevice.Model.AIRPODS_GEN3 override val batteryCasePercent: Float? get() = super.batteryCasePercent ?: cachedBatteryPercentage - override val caseLidState: DualApplePods.LidState + override val caseLidState: DualAirPods.LidState get() = cachedCaseState ?: super.caseLidState override val rssi: Int diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMax.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMax.kt index dac4505e..a1242391 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMax.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMax.kt @@ -2,6 +2,8 @@ package eu.darken.capod.pods.core.apple.airpods import eu.darken.capod.common.bluetooth.BleScanResult import eu.darken.capod.common.debug.logging.logTag +import eu.darken.capod.common.isBitSet +import eu.darken.capod.pods.core.HasEarDetection import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.ApplePods import eu.darken.capod.pods.core.apple.SingleApplePods @@ -19,13 +21,22 @@ data class AirPodsMax( override val proximityMessage: ProximityPairing.Message, override val confidence: Float = PodDevice.BASE_CONFIDENCE, private val rssiAverage: Int? = null, -) : SingleApplePods { +) : SingleApplePods, HasEarDetection { override val model: PodDevice.Model = PodDevice.Model.AIRPODS_MAX override val rssi: Int get() = rssiAverage ?: super.rssi + val isHeadphonesBeingWorn: Boolean + get() = rawStatus.isBitSet(1) + + val isHeadsetBeingCharged: Boolean + get() = rawFlags.isBitSet(0) + + override val isBeingWorn: Boolean + get() = isHeadphonesBeingWorn + class Factory @Inject constructor() : SingleApplePodsFactory(TAG) { override fun isResponsible(message: ProximityPairing.Message): Boolean = message.run { @@ -49,7 +60,6 @@ data class AirPodsMax( rssiAverage = result.averageRssi(basic.rssi), ) } - } companion object { diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro.kt index 25d03799..179fb662 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro.kt @@ -4,8 +4,8 @@ import eu.darken.capod.common.bluetooth.BleScanResult import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.ApplePods -import eu.darken.capod.pods.core.apple.DualApplePods -import eu.darken.capod.pods.core.apple.DualApplePods.LidState +import eu.darken.capod.pods.core.apple.DualAirPods +import eu.darken.capod.pods.core.apple.DualAirPods.LidState import eu.darken.capod.pods.core.apple.DualApplePodsFactory import eu.darken.capod.pods.core.apple.protocol.ProximityPairing import java.time.Instant @@ -22,7 +22,7 @@ data class AirPodsPro( private val rssiAverage: Int? = null, private val cachedBatteryPercentage: Float? = null, private val cachedCaseState: LidState? = null -) : DualApplePods { +) : DualAirPods { override val model: PodDevice.Model = PodDevice.Model.AIRPODS_PRO diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFlex.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFlex.kt index 9b783b9a..9b29c25b 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFlex.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFlex.kt @@ -4,8 +4,8 @@ import eu.darken.capod.common.bluetooth.BleScanResult import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.ApplePods -import eu.darken.capod.pods.core.apple.BasicSingleApplePods -import eu.darken.capod.pods.core.apple.BasicSingleApplePodsFactory +import eu.darken.capod.pods.core.apple.SingleApplePods +import eu.darken.capod.pods.core.apple.SingleApplePodsFactory import eu.darken.capod.pods.core.apple.protocol.ProximityPairing import java.time.Instant import javax.inject.Inject @@ -19,14 +19,14 @@ data class BeatsFlex( override val proximityMessage: ProximityPairing.Message, override val confidence: Float = PodDevice.BASE_CONFIDENCE, private val rssiAverage: Int? = null, -) : BasicSingleApplePods { +) : SingleApplePods { override val model: PodDevice.Model = PodDevice.Model.BEATS_FLEX override val rssi: Int get() = rssiAverage ?: super.rssi - class Factory @Inject constructor() : BasicSingleApplePodsFactory(TAG) { + class Factory @Inject constructor() : SingleApplePodsFactory(TAG) { override fun isResponsible(message: ProximityPairing.Message): Boolean = message.run { getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsSolo3.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsSolo3.kt index 7f198481..d051fe90 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsSolo3.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsSolo3.kt @@ -4,8 +4,8 @@ import eu.darken.capod.common.bluetooth.BleScanResult import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.ApplePods -import eu.darken.capod.pods.core.apple.BasicSingleApplePods -import eu.darken.capod.pods.core.apple.BasicSingleApplePodsFactory +import eu.darken.capod.pods.core.apple.SingleApplePods +import eu.darken.capod.pods.core.apple.SingleApplePodsFactory import eu.darken.capod.pods.core.apple.protocol.ProximityPairing import java.time.Instant import javax.inject.Inject @@ -19,13 +19,13 @@ data class BeatsSolo3( override val proximityMessage: ProximityPairing.Message, override val confidence: Float = PodDevice.BASE_CONFIDENCE, private val rssiAverage: Int? = null, -) : BasicSingleApplePods { +) : SingleApplePods { override val model: PodDevice.Model = PodDevice.Model.BEATS_SOLO_3 override val rssi: Int get() = rssiAverage ?: super.rssi - class Factory @Inject constructor() : BasicSingleApplePodsFactory(TAG) { + class Factory @Inject constructor() : SingleApplePodsFactory(TAG) { override fun isResponsible(message: ProximityPairing.Message): Boolean = message.run { getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsStudio3.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsStudio3.kt index 4f556488..c3a59a60 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsStudio3.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsStudio3.kt @@ -4,8 +4,8 @@ import eu.darken.capod.common.bluetooth.BleScanResult import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.ApplePods -import eu.darken.capod.pods.core.apple.BasicSingleApplePods -import eu.darken.capod.pods.core.apple.BasicSingleApplePodsFactory +import eu.darken.capod.pods.core.apple.SingleApplePods +import eu.darken.capod.pods.core.apple.SingleApplePodsFactory import eu.darken.capod.pods.core.apple.protocol.ProximityPairing import java.time.Instant import javax.inject.Inject @@ -19,11 +19,11 @@ data class BeatsStudio3( override val proximityMessage: ProximityPairing.Message, override val confidence: Float = PodDevice.BASE_CONFIDENCE, private val rssiAverage: Int? = null, -) : BasicSingleApplePods { +) : SingleApplePods { override val model: PodDevice.Model = PodDevice.Model.BEATS_STUDIO_3 - class Factory @Inject constructor() : BasicSingleApplePodsFactory(TAG) { + 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 @@ -46,7 +46,6 @@ data class BeatsStudio3( rssiAverage = result.averageRssi(basic.rssi), ) } - } companion object { diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsX.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsX.kt index 0df41bb8..ed8d955e 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsX.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsX.kt @@ -4,8 +4,8 @@ import eu.darken.capod.common.bluetooth.BleScanResult import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.ApplePods -import eu.darken.capod.pods.core.apple.BasicSingleApplePods -import eu.darken.capod.pods.core.apple.BasicSingleApplePodsFactory +import eu.darken.capod.pods.core.apple.SingleApplePods +import eu.darken.capod.pods.core.apple.SingleApplePodsFactory import eu.darken.capod.pods.core.apple.protocol.ProximityPairing import java.time.Instant import javax.inject.Inject @@ -19,14 +19,14 @@ data class BeatsX( override val proximityMessage: ProximityPairing.Message, override val confidence: Float = PodDevice.BASE_CONFIDENCE, private val rssiAverage: Int? = null, -) : BasicSingleApplePods { +) : SingleApplePods { override val model: PodDevice.Model = PodDevice.Model.BEATS_X override val rssi: Int get() = rssiAverage ?: super.rssi - class Factory @Inject constructor() : BasicSingleApplePodsFactory(TAG) { + class Factory @Inject constructor() : SingleApplePodsFactory(TAG) { override fun isResponsible(message: ProximityPairing.Message): Boolean = message.run { getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats3.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats3.kt index 1547dfdc..76fdc394 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats3.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats3.kt @@ -4,8 +4,8 @@ import eu.darken.capod.common.bluetooth.BleScanResult import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.ApplePods -import eu.darken.capod.pods.core.apple.BasicSingleApplePods -import eu.darken.capod.pods.core.apple.BasicSingleApplePodsFactory +import eu.darken.capod.pods.core.apple.SingleApplePods +import eu.darken.capod.pods.core.apple.SingleApplePodsFactory import eu.darken.capod.pods.core.apple.protocol.ProximityPairing import java.time.Instant import javax.inject.Inject @@ -19,13 +19,14 @@ data class PowerBeats3( override val proximityMessage: ProximityPairing.Message, override val confidence: Float = PodDevice.BASE_CONFIDENCE, private val rssiAverage: Int? = null, -) : BasicSingleApplePods { +) : SingleApplePods { + override val model: PodDevice.Model = PodDevice.Model.POWERBEATS_3 override val rssi: Int get() = rssiAverage ?: super.rssi - class Factory @Inject constructor() : BasicSingleApplePodsFactory(TAG) { + class Factory @Inject constructor() : SingleApplePodsFactory(TAG) { override fun isResponsible(message: ProximityPairing.Message): Boolean = message.run { getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro.kt index 960b1014..ad4744e1 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro.kt @@ -4,7 +4,7 @@ import eu.darken.capod.common.bluetooth.BleScanResult import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.ApplePods -import eu.darken.capod.pods.core.apple.DualApplePods +import eu.darken.capod.pods.core.apple.DualAirPods import eu.darken.capod.pods.core.apple.DualApplePodsFactory import eu.darken.capod.pods.core.apple.protocol.ProximityPairing import java.time.Instant @@ -20,15 +20,15 @@ data class PowerBeatsPro( override val confidence: Float = PodDevice.BASE_CONFIDENCE, private val rssiAverage: Int? = null, private val cachedBatteryPercentage: Float? = null, - private val cachedCaseState: DualApplePods.LidState? = null -) : DualApplePods { + private val cachedCaseState: DualAirPods.LidState? = null +) : DualAirPods { override val model: PodDevice.Model = PodDevice.Model.POWERBEATS_PRO override val batteryCasePercent: Float? get() = super.batteryCasePercent ?: cachedBatteryPercentage - override val caseLidState: DualApplePods.LidState + override val caseLidState: DualAirPods.LidState get() = cachedCaseState ?: super.caseLidState override val rssi: Int diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/fakes/Twsi99999.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/fakes/Twsi99999.kt deleted file mode 100644 index ef5c8b94..00000000 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/fakes/Twsi99999.kt +++ /dev/null @@ -1,72 +0,0 @@ -package eu.darken.capod.pods.core.apple.fakes - -import eu.darken.capod.common.bluetooth.BleScanResult -import eu.darken.capod.common.debug.logging.logTag -import eu.darken.capod.pods.core.PodDevice -import eu.darken.capod.pods.core.apple.ApplePods -import eu.darken.capod.pods.core.apple.DualApplePods -import eu.darken.capod.pods.core.apple.DualApplePodsFactory -import eu.darken.capod.pods.core.apple.protocol.ProximityPairing -import java.time.Instant -import javax.inject.Inject - -/** - * Basically an AirPods GEN1 clone - */ -data class Twsi99999 constructor( - override val identifier: PodDevice.Id = PodDevice.Id(), - override val seenLastAt: Instant = Instant.now(), - override val seenFirstAt: Instant = Instant.now(), - override val seenCounter: Int = 1, - override val scanResult: BleScanResult, - override val proximityMessage: ProximityPairing.Message, - override val confidence: Float = PodDevice.BASE_CONFIDENCE, - private val rssiAverage: Int? = null, - private val cachedBatteryPercentage: Float? = null, - private val cachedCaseState: DualApplePods.LidState? = null -) : DualApplePods { - - override val model: PodDevice.Model = PodDevice.Model.TWS_I99999 - - override val batteryCasePercent: Float? - get() = super.batteryCasePercent ?: cachedBatteryPercentage - - override val caseLidState: DualApplePods.LidState - get() = cachedCaseState ?: super.caseLidState - - override val rssi: Int - get() = rssiAverage ?: super.rssi - - class Factory @Inject constructor() : DualApplePodsFactory(TAG) { - - override fun isResponsible(message: ProximityPairing.Message): Boolean = message.run { - // Official message length is 19HEX, i.e. binary 25, did they copy this wrong? - getModelInfo().full == DEVICE_CODE && length == 19 - } - - override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { - var basic = Twsi99999(scanResult = scanResult, proximityMessage = message) - val result = searchHistory(basic) - - if (result != null) basic = basic.copy(identifier = result.id) - updateHistory(basic) - - if (result == null) return basic - - return basic.copy( - identifier = result.id, - seenFirstAt = result.seenFirstAt, - seenCounter = result.seenCounter, - confidence = result.confidence, - cachedBatteryPercentage = result.getLatestCaseBattery(), - rssiAverage = result.averageRssi(basic.rssi), - cachedCaseState = result.getLatestCaseLidState(basic) - ) - } - } - - companion object { - private val DEVICE_CODE = 0x0220.toUShort() - private val TAG = logTag("PodDevice", "Apple", "TWS", "i99999") - } -} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/misc/Twsi99999.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/misc/Twsi99999.kt new file mode 100644 index 00000000..73144172 --- /dev/null +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/misc/Twsi99999.kt @@ -0,0 +1,143 @@ +package eu.darken.capod.pods.core.apple.misc + +import eu.darken.capod.common.bluetooth.BleScanResult +import eu.darken.capod.common.debug.logging.log +import eu.darken.capod.common.debug.logging.logTag +import eu.darken.capod.common.isBitSet +import eu.darken.capod.common.lowerNibble +import eu.darken.capod.common.upperNibble +import eu.darken.capod.pods.core.DualPodDevice +import eu.darken.capod.pods.core.HasCase +import eu.darken.capod.pods.core.HasDualMicrophone +import eu.darken.capod.pods.core.PodDevice +import eu.darken.capod.pods.core.apple.ApplePods +import eu.darken.capod.pods.core.apple.ApplePodsFactory +import eu.darken.capod.pods.core.apple.protocol.ProximityPairing +import java.time.Instant +import javax.inject.Inject + +/** + * Basically an AirPods GEN1 clone + * Similar data structure but a lot of placeholder values or hardcoded values + */ +data class Twsi99999 constructor( + override val identifier: PodDevice.Id = PodDevice.Id(), + override val seenLastAt: Instant = Instant.now(), + override val seenFirstAt: Instant = Instant.now(), + override val seenCounter: Int = 1, + override val scanResult: BleScanResult, + override val proximityMessage: ProximityPairing.Message, + override val confidence: Float = PodDevice.BASE_CONFIDENCE, + private val rssiAverage: Int? = null, + private val cachedBatteryPercentage: Float? = null, +) : ApplePods, DualPodDevice, HasDualMicrophone, HasCase { + + override val model: PodDevice.Model = PodDevice.Model.TWS_I99999 + + override val rssi: Int + get() = rssiAverage ?: super.rssi + + /** + * Normally values for the left pod are in the lower nibbles, if the left pod is primary (microphone) + * If the right pod is the primary, the values are flipped. + */ + val areValuesFlipped: Boolean + get() = !rawStatus.isBitSet(5) + + override val batteryLeftPodPercent: Float? + get() { + val value = when (areValuesFlipped) { + true -> rawPodsBattery.upperNibble.toInt() + false -> rawPodsBattery.lowerNibble.toInt() + } + return when (value) { + 15 -> null + else -> if (value > 10) { + log { "Left pod: Above 100% battery: $value" } + 1.0f + } else { + (value / 10f) + } + } + } + + override val batteryRightPodPercent: Float? + get() { + val value = when (areValuesFlipped) { + true -> rawPodsBattery.lowerNibble.toInt() + false -> rawPodsBattery.upperNibble.toInt() + } + return when (value) { + 15 -> null + else -> if (value > 10) { + log { "Right pod: Above 100% battery: $value" } + 1.0f + } else { + value / 10f + } + } + } + + val isThisPodInThecase: Boolean + get() = rawStatus.isBitSet(6) + + /** + * The data flip bit is set if the left pod is primary. + * For the pod that is in the case, this is flipped again though. + */ + override val isLeftPodMicrophone: Boolean + get() = rawStatus.isBitSet(5) xor isThisPodInThecase + + /** + * The data flip bit is UNset if the right pod is primary. + * For the pod that is in the case, this is flipped again though. + */ + override val isRightPodMicrophone: Boolean + get() = !rawStatus.isBitSet(5) xor isThisPodInThecase + + override val batteryCasePercent: Float? + get() = when (val value = rawCaseBattery.toInt()) { + 15 -> cachedBatteryPercentage + else -> if (value > 10) { + log { "Case: Above 100% battery: $value" } + 1.0f + } else { + value / 10f + } + } + + override val isCaseCharging: Boolean + get() = rawFlags.isBitSet(2) + + class Factory @Inject constructor() : ApplePodsFactory(TAG) { + + override fun isResponsible(message: ProximityPairing.Message): Boolean = message.run { + // Official message length is 19HEX, i.e. binary 25, did they copy this wrong? + getModelInfo().full == DEVICE_CODE && length == 19 + } + + override fun create(scanResult: BleScanResult, message: ProximityPairing.Message): ApplePods { + var basic = Twsi99999(scanResult = scanResult, proximityMessage = message) + val result = searchHistory(basic) + + if (result != null) basic = basic.copy(identifier = result.id) + updateHistory(basic) + + if (result == null) return basic + + return basic.copy( + identifier = result.id, + seenFirstAt = result.seenFirstAt, + seenCounter = result.seenCounter, + confidence = result.confidence, + cachedBatteryPercentage = result.getLatestCaseBattery(), + rssiAverage = result.averageRssi(basic.rssi), + ) + } + } + + companion object { + private val DEVICE_CODE = 0x0220.toUShort() + private val TAG = logTag("PodDevice", "Apple", "TWS", "i99999") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/UnknownAppleDevice.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/misc/UnknownAppleDevice.kt similarity index 93% rename from app/src/main/java/eu/darken/capod/pods/core/apple/UnknownAppleDevice.kt rename to app/src/main/java/eu/darken/capod/pods/core/apple/misc/UnknownAppleDevice.kt index 860c29d4..a0602aaf 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/UnknownAppleDevice.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/misc/UnknownAppleDevice.kt @@ -1,10 +1,12 @@ -package eu.darken.capod.pods.core.apple +package eu.darken.capod.pods.core.apple.misc import android.content.Context import eu.darken.capod.R import eu.darken.capod.common.bluetooth.BleScanResult import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.pods.core.PodDevice +import eu.darken.capod.pods.core.apple.ApplePods +import eu.darken.capod.pods.core.apple.ApplePodsFactory import eu.darken.capod.pods.core.apple.protocol.ProximityPairing import java.time.Instant import javax.inject.Inject diff --git a/app/src/main/java/eu/darken/capod/reaction/autoconnect/AutoConnect.kt b/app/src/main/java/eu/darken/capod/reaction/autoconnect/AutoConnect.kt index 580744ca..c3fac002 100644 --- a/app/src/main/java/eu/darken/capod/reaction/autoconnect/AutoConnect.kt +++ b/app/src/main/java/eu/darken/capod/reaction/autoconnect/AutoConnect.kt @@ -8,9 +8,9 @@ import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.common.flow.setupCommonEventHandlers import eu.darken.capod.main.core.GeneralSettings import eu.darken.capod.monitor.core.PodMonitor -import eu.darken.capod.pods.core.HasDualEarDetection import eu.darken.capod.pods.core.HasEarDetection -import eu.darken.capod.pods.core.apple.DualApplePods +import eu.darken.capod.pods.core.HasEarDetectionDual +import eu.darken.capod.pods.core.apple.DualAirPods import eu.darken.capod.reaction.settings.ReactionSettings import kotlinx.coroutines.flow.* import javax.inject.Inject @@ -68,12 +68,12 @@ class AutoConnect @Inject constructor( val conditionFulfilled = when (condition) { AutoConnectCondition.WHEN_SEEN -> true AutoConnectCondition.CASE_OPEN -> when (mainDevice) { - is DualApplePods -> mainDevice.caseLidState == DualApplePods.LidState.OPEN + is DualAirPods -> mainDevice.caseLidState == DualAirPods.LidState.OPEN else -> true } AutoConnectCondition.IN_EAR -> when (mainDevice) { is HasEarDetection -> { - if (mainDevice is HasDualEarDetection && reactionSettings.onePodMode.value) { + if (mainDevice is HasEarDetectionDual && reactionSettings.onePodMode.value) { mainDevice.isEitherPodInEar } else { mainDevice.isBeingWorn diff --git a/app/src/main/java/eu/darken/capod/reaction/playpause/PlayPause.kt b/app/src/main/java/eu/darken/capod/reaction/playpause/PlayPause.kt index bcdab882..a4bbd576 100644 --- a/app/src/main/java/eu/darken/capod/reaction/playpause/PlayPause.kt +++ b/app/src/main/java/eu/darken/capod/reaction/playpause/PlayPause.kt @@ -9,8 +9,8 @@ import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.common.flow.setupCommonEventHandlers import eu.darken.capod.common.flow.withPrevious import eu.darken.capod.monitor.core.PodMonitor -import eu.darken.capod.pods.core.HasDualEarDetection import eu.darken.capod.pods.core.HasEarDetection +import eu.darken.capod.pods.core.HasEarDetectionDual import eu.darken.capod.reaction.settings.ReactionSettings import kotlinx.coroutines.flow.* import javax.inject.Inject @@ -48,7 +48,7 @@ class PlayPause @Inject constructor( return@onEach } - if (previous is HasDualEarDetection && current is HasDualEarDetection && reactionSettings.onePodMode.value) { + if (previous is HasEarDetectionDual && current is HasEarDetectionDual && reactionSettings.onePodMode.value) { log(TAG) { "Ear status changed for dual pod device in single pod mode." } val previousWorn = previous.isEitherPodInEar val currentWorn = current.isEitherPodInEar diff --git a/app/src/main/java/eu/darken/capod/reaction/popup/PopUpReaction.kt b/app/src/main/java/eu/darken/capod/reaction/popup/PopUpReaction.kt index 6d6d842d..04a1d40b 100644 --- a/app/src/main/java/eu/darken/capod/reaction/popup/PopUpReaction.kt +++ b/app/src/main/java/eu/darken/capod/reaction/popup/PopUpReaction.kt @@ -8,7 +8,7 @@ import eu.darken.capod.common.flow.setupCommonEventHandlers import eu.darken.capod.common.flow.withPrevious import eu.darken.capod.monitor.core.PodMonitor import eu.darken.capod.pods.core.PodDevice -import eu.darken.capod.pods.core.apple.DualApplePods +import eu.darken.capod.pods.core.apple.DualAirPods import eu.darken.capod.reaction.popup.ui.PopUpWindow import eu.darken.capod.reaction.settings.ReactionSettings import kotlinx.coroutines.flow.* @@ -39,7 +39,7 @@ class PopUpReaction @Inject constructor( .withPrevious() .setupCommonEventHandlers(TAG) { "monitor" } .map { (previous, current) -> - if (previous is DualApplePods? && current is DualApplePods) { + if (previous is DualAirPods? && current is DualAirPods) { log(TAG, VERBOSE) { val prev = previous?.rawCaseLidState?.let { String.format("%02X", it.toByte()) } val cur = current.rawCaseLidState.let { String.format("%02X", it.toByte()) } @@ -60,8 +60,8 @@ class PopUpReaction @Inject constructor( } } - private suspend fun tryPopWindow(current: DualApplePods) { - if (current.caseLidState == DualApplePods.LidState.OPEN) { + private suspend fun tryPopWindow(current: DualAirPods) { + if (current.caseLidState == DualAirPods.LidState.OPEN) { log(TAG, INFO) { "Show popup" } val now = Instant.now() @@ -78,9 +78,9 @@ class PopUpReaction @Inject constructor( withContext(dispatcherProvider.Main) { popupWindow.show(current) } - } else if (current.caseLidState != DualApplePods.LidState.OPEN) { + } else if (current.caseLidState != DualAirPods.LidState.OPEN) { when (current.caseLidState) { - DualApplePods.LidState.CLOSED -> { + DualAirPods.LidState.CLOSED -> { log(TAG, INFO) { "Lid was actively closed, resetting cooldown." } coolDowns.remove(current.identifier) } diff --git a/app/src/main/java/eu/darken/capod/reaction/popup/ui/PopUpPodViewFactory.kt b/app/src/main/java/eu/darken/capod/reaction/popup/ui/PopUpPodViewFactory.kt index d59718b7..620119cf 100644 --- a/app/src/main/java/eu/darken/capod/reaction/popup/ui/PopUpPodViewFactory.kt +++ b/app/src/main/java/eu/darken/capod/reaction/popup/ui/PopUpPodViewFactory.kt @@ -10,11 +10,9 @@ import dagger.hilt.android.qualifiers.ApplicationContext import eu.darken.capod.R import eu.darken.capod.common.debug.autoreport.DebugSettings import eu.darken.capod.databinding.PopupNotificationDualPodsBinding -import eu.darken.capod.databinding.PopupNotificationSinglePodsBasicBinding import eu.darken.capod.databinding.PopupNotificationSinglePodsBinding import eu.darken.capod.pods.core.* -import eu.darken.capod.pods.core.apple.BasicSingleApplePods -import eu.darken.capod.pods.core.apple.DualApplePods +import eu.darken.capod.pods.core.apple.DualAirPods import eu.darken.capod.pods.core.apple.SingleApplePods import javax.inject.Inject @@ -28,16 +26,13 @@ class PopUpPodViewFactory @Inject constructor( private val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater fun createContentView(parent: ViewGroup, device: PodDevice): View = when (device) { - is DualApplePods -> createDualApplePods(parent, device) - is SingleApplePods -> createSingleApplePods(parent, device) // Unused, has no case to trigger reaction? - is BasicSingleApplePods -> createSingleBasicApplePods( - parent, - device - ) // Unused, has no case to trigger reaction? + is DualAirPods -> createDualApplePods(parent, device) + // Unused, has no case to trigger reaction? + is SingleApplePods -> createSingleApplePods(parent, device) else -> throw IllegalArgumentException("Unexpected device: $device") } - private fun createDualApplePods(parent: ViewGroup, device: DualApplePods): View = + private fun createDualApplePods(parent: ViewGroup, device: DualAirPods): View = PopupNotificationDualPodsBinding.inflate(layoutInflater, parent, false).apply { device.apply { podIcon.setImageResource(iconRes) @@ -72,17 +67,4 @@ class PopUpPodViewFactory @Inject constructor( } }.root - private fun createSingleBasicApplePods(parent: ViewGroup, device: BasicSingleApplePods): View = - PopupNotificationSinglePodsBasicBinding.inflate(layoutInflater, parent, false).apply { - device.apply { - headphonesIcon.setImageResource(iconRes) - headphonesLabel.text = getLabel(context) - signal.text = getSignalQuality(context) - signal.isInvisible = debugSettings.isDebugModeEnabled.value - - headphonesBatteryIcon.setImageResource(getBatteryDrawable(batteryHeadsetPercent)) - headphonesBatteryLabel.text = getBatteryLevelHeadset(context) - } - }.root - } \ No newline at end of file diff --git a/app/src/main/res/layout/main_fragment.xml b/app/src/main/res/layout/main_fragment.xml index f59de350..f812bf12 100644 --- a/app/src/main/res/layout/main_fragment.xml +++ b/app/src/main/res/layout/main_fragment.xml @@ -22,7 +22,7 @@ android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - tools:listitem="@layout/overview_pods_apple_dual_item" + tools:listitem="@layout/overview_pods_dual_item" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/toolbar" /> diff --git a/app/src/main/res/layout/monitor_notification_single_pods_basic_small.xml b/app/src/main/res/layout/monitor_notification_single_pods_basic_small.xml deleted file mode 100644 index 00f46d63..00000000 --- a/app/src/main/res/layout/monitor_notification_single_pods_basic_small.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/overview_pods_apple_single_basic_item.xml b/app/src/main/res/layout/overview_pods_apple_single_basic_item.xml deleted file mode 100644 index d4fd465b..00000000 --- a/app/src/main/res/layout/overview_pods_apple_single_basic_item.xml +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/overview_pods_apple_dual_item.xml b/app/src/main/res/layout/overview_pods_dual_item.xml similarity index 99% rename from app/src/main/res/layout/overview_pods_apple_dual_item.xml rename to app/src/main/res/layout/overview_pods_dual_item.xml index 1b987ecb..69181d2d 100644 --- a/app/src/main/res/layout/overview_pods_apple_dual_item.xml +++ b/app/src/main/res/layout/overview_pods_dual_item.xml @@ -118,6 +118,7 @@ android:id="@+id/pod_left_icon" style="@style/PodInfoItemIcon" android:src="@drawable/ic_airpod_left_24" + android:contentDescription="@string/pods_dual_left_label" app:layout_constraintBottom_toBottomOf="@id/pod_left_label" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@id/pod_left_label" /> @@ -322,6 +323,7 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/test/java/eu/darken/capod/pods/core/apple/AirPodsFactoryTest.kt b/app/src/test/java/eu/darken/capod/pods/core/apple/AirPodsFactoryTest.kt index 78260aae..3a47582f 100644 --- a/app/src/test/java/eu/darken/capod/pods/core/apple/AirPodsFactoryTest.kt +++ b/app/src/test/java/eu/darken/capod/pods/core/apple/AirPodsFactoryTest.kt @@ -3,6 +3,7 @@ package eu.darken.capod.pods.core.apple import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.airpods.AirPodsGen1 import eu.darken.capod.pods.core.apple.airpods.AirPodsPro +import eu.darken.capod.pods.core.apple.misc.UnknownAppleDevice import io.kotest.matchers.shouldBe import io.kotest.matchers.types.instanceOf import kotlinx.coroutines.test.runBlockingTest @@ -12,14 +13,14 @@ class AirPodsFactoryTest : BaseAirPodsTest() { @Test fun `create AirPodsGen1`() = runBlockingTest { - create("07 19 01 02 20 55 AA 56 31 00 00 6F E4 DF 10 AF 10 60 81 03 3B 76 D9 C7 11 22 88") { + create("07 19 01 02 20 55 AA 56 31 00 00 6F E4 DF 10 AF 10 60 81 03 3B 76 D9 C7 11 22 88") { this shouldBe instanceOf() } } @Test fun `create AirPodsPro`() = runBlockingTest { - create("07 19 01 0E 20 2B 99 8F 01 00 >09< 10 30 EE F3 41 B5 D8 9F A3 B0 B4 17 9F 85 97 5F") { + create("07 19 01 0E 20 2B 99 8F 01 00 >09< 10 30 EE F3 41 B5 D8 9F A3 B0 B4 17 9F 85 97 5F") { this shouldBe instanceOf() } } diff --git a/app/src/test/java/eu/darken/capod/pods/core/apple/BasicSingleApplePodsTest.kt b/app/src/test/java/eu/darken/capod/pods/core/apple/BasicSingleApplePodsTest.kt index 4df26400..82be7ae0 100644 --- a/app/src/test/java/eu/darken/capod/pods/core/apple/BasicSingleApplePodsTest.kt +++ b/app/src/test/java/eu/darken/capod/pods/core/apple/BasicSingleApplePodsTest.kt @@ -8,7 +8,7 @@ class BasicSingleApplePodsTest : BaseAirPodsTest() { @Test fun `test mapping`() = runBlockingTest { - create("07 19 01 05 20 00 F5 0F 01 01 00 6D CE C0 04 22 0A 85 31 2D 82 6B 42 80 01 20 1A") { + create("07 19 01 05 20 00 F5 0F 01 01 00 6D CE C0 04 22 0A 85 31 2D 82 6B 42 80 01 20 1A") { rawPrefix shouldBe 0x01.toUByte() rawDeviceModel shouldBe 0x0520.toUShort() rawStatus shouldBe 0x00.toUByte() @@ -23,7 +23,7 @@ class BasicSingleApplePodsTest : BaseAirPodsTest() { @Test fun `test battery headset percent`() = runBlockingTest { - create("07 19 01 05 20 00 F5 0F 01 01 00 6D CE C0 04 22 0A 85 31 2D 82 6B 42 80 01 20 1A") { + create("07 19 01 05 20 00 F5 0F 01 01 00 6D CE C0 04 22 0A 85 31 2D 82 6B 42 80 01 20 1A") { batteryHeadsetPercent shouldBe 0.5f } } diff --git a/app/src/test/java/eu/darken/capod/pods/core/apple/DualApplePodsTest.kt b/app/src/test/java/eu/darken/capod/pods/core/apple/DualApplePodsTest.kt index a414ad0e..79b05533 100644 --- a/app/src/test/java/eu/darken/capod/pods/core/apple/DualApplePodsTest.kt +++ b/app/src/test/java/eu/darken/capod/pods/core/apple/DualApplePodsTest.kt @@ -8,7 +8,7 @@ class DualApplePodsTest : BaseAirPodsTest() { @Test fun `test bit mapping`() = runBlockingTest { - create("07 19 01 0E 20 54 AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { + create("07 19 01 0E 20 54 AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { rawPrefix shouldBe 0x01.toUByte() rawDeviceModel shouldBe 0x0e20.toUShort() @@ -24,13 +24,13 @@ class DualApplePodsTest : BaseAirPodsTest() { @Test fun `test AirPodDevice - active microphone`() = runBlockingTest { - create("07 19 01 0E 20 >2B< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { + create("07 19 01 0E 20 >2B< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { // 00101011 // --^----- isLeftPodMicrophone shouldBe true isRightPodMicrophone shouldBe false } - create("07 19 01 0E 20 >0B< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { + create("07 19 01 0E 20 >0B< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { // 00001011 // --^----- isLeftPodMicrophone shouldBe false @@ -41,24 +41,24 @@ class DualApplePodsTest : BaseAirPodsTest() { @Test fun `test AirPodDevice - left pod ear status`() = runBlockingTest { // Left Pod primary - create("07 19 01 0E 20 >22< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { + create("07 19 01 0E 20 >22< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { // 00100010 // 765432¹0 isLeftPodInEar shouldBe true } - create("07 19 01 0E 20 >20< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { + create("07 19 01 0E 20 >20< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { // 00100000 // 765432¹0 isLeftPodInEar shouldBe false } // Right Pod is primary - create("07 19 01 0E 20 >09< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { + create("07 19 01 0E 20 >09< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { // 00001001 // 7654³210 isLeftPodInEar shouldBe true } - create("07 19 01 0E 20 >20< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { + create("07 19 01 0E 20 >20< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { // 00000001 // 7654³210 isLeftPodInEar shouldBe false @@ -68,24 +68,24 @@ class DualApplePodsTest : BaseAirPodsTest() { @Test fun `test AirPodDevice - right pod ear status`() = runBlockingTest { // Left Pod primary - create("07 19 01 0E 20 >29< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { + create("07 19 01 0E 20 >29< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { // 00101001 // 7654³210 isRightPodInEar shouldBe true } - create("07 19 01 0E 20 >21< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { + create("07 19 01 0E 20 >21< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { // 00100001 // 7654³210 isRightPodInEar shouldBe false } // Right Pod is primary - create("07 19 01 0E 20 >03< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { + create("07 19 01 0E 20 >03< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { // 00000011 // 765432¹0 isRightPodInEar shouldBe true } - create("07 19 01 0E 20 >01< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { + create("07 19 01 0E 20 >01< AA B5 31 00 00 E0 0C A7 8A 60 4B D3 7D F4 60 4F 2C 73 E9 A7 F4") { // 00000001 // 765432¹0 isRightPodInEar shouldBe false @@ -95,13 +95,13 @@ class DualApplePodsTest : BaseAirPodsTest() { @Test fun `test AirPodDevice - battery status`() = runBlockingTest { // Right Pod is primary - create("07 19 01 0E 20 0B >98< 94 52 00 05 09 73 3C 3D F9 2C 3E B3 DD 76 02 DD 4E 16 FD FB") { + create("07 19 01 0E 20 0B >98< 94 52 00 05 09 73 3C 3D F9 2C 3E B3 DD 76 02 DD 4E 16 FD FB") { // 88 10001000 batteryLeftPodPercent shouldBe 0.9f batteryRightPodPercent shouldBe 0.8f } // Left Pod primary - create("07 19 01 0E 20 2B >89< 94 52 00 05 09 73 3C 3D F9 2C 3E B3 DD 76 02 DD 4E 16 FD FB") { + create("07 19 01 0E 20 2B >89< 94 52 00 05 09 73 3C 3D F9 2C 3E B3 DD 76 02 DD 4E 16 FD FB") { // F8 11111000 batteryLeftPodPercent shouldBe 0.9f batteryRightPodPercent shouldBe 0.8f @@ -114,13 +114,13 @@ class DualApplePodsTest : BaseAirPodsTest() { * Right pod is charging */ // This is the left - create("07 19 01 0E 20 51 89 >94< 52 00 00 F4 89 82 6D 3E 27 7F 26 62 57 D0 E2 A6 49 E9 35") { + create("07 19 01 0E 20 51 89 >94< 52 00 00 F4 89 82 6D 3E 27 7F 26 62 57 D0 E2 A6 49 E9 35") { // 1001 0100 isLeftPodCharging shouldBe false isRightPodCharging shouldBe true } // This is the right - create("07 19 01 0E 20 31 98 >A4< 01 00 00 31 B9 A0 C4 80 CD D1 CF B9 3A 9A 6D 48 31 08 EB") { + create("07 19 01 0E 20 31 98 >A4< 01 00 00 31 B9 A0 C4 80 CD D1 CF B9 3A 9A 6D 48 31 08 EB") { // 1010 0100 isLeftPodCharging shouldBe false isRightPodCharging shouldBe true @@ -130,26 +130,26 @@ class DualApplePodsTest : BaseAirPodsTest() { * Left pod is charging */ // This is the left - create("07 19 01 0E 20 71 98 >94< 52 00 05 A5 37 31 B2 BD 42 68 0C 64 FD 00 99 4A E5 3E F4") { + create("07 19 01 0E 20 71 98 >94< 52 00 05 A5 37 31 B2 BD 42 68 0C 64 FD 00 99 4A E5 3E F4") { // 1001 0100 isLeftPodCharging shouldBe true isRightPodCharging shouldBe false } // This is the right - create("07 19 01 0E 20 11 89 >A4< 04 00 04 BA 79 1B C0 65 69 C6 9F 19 6E 37 7D 6D 86 8D D9") { + create("07 19 01 0E 20 11 89 >A4< 04 00 04 BA 79 1B C0 65 69 C6 9F 19 6E 37 7D 6D 86 8D D9") { // 1010 0100 isLeftPodCharging shouldBe true isRightPodCharging shouldBe false } // Both charging - create("07 19 01 0E 20 55 88 >B4< 59 00 05 4B FC DF 68 28 A5 45 52 65 9C FE 51 86 3A B5 DB") { + create("07 19 01 0E 20 55 88 >B4< 59 00 05 4B FC DF 68 28 A5 45 52 65 9C FE 51 86 3A B5 DB") { // 1011 0100 isLeftPodCharging shouldBe true isRightPodCharging shouldBe true } // Both not charging - create("07 19 01 0E 20 00 F8 >8F< 03 00 05 4C 0F A0 C4 05 24 DD EB AF 92 99 FD 54 B1 06 48") { + create("07 19 01 0E 20 00 F8 >8F< 03 00 05 4C 0F A0 C4 05 24 DD EB AF 92 99 FD 54 B1 06 48") { // 1000 1111 isLeftPodCharging shouldBe false isRightPodCharging shouldBe false @@ -158,11 +158,11 @@ class DualApplePodsTest : BaseAirPodsTest() { @Test fun `test AirPodDevice - case charging`() = runBlockingTest { - create("07 19 01 0E 20 75 99 >B4< 31 00 05 77 C8 BA 0C 4E 1F BE AD 70 C5 40 71 D2 E9 17 A2") { + create("07 19 01 0E 20 75 99 >B4< 31 00 05 77 C8 BA 0C 4E 1F BE AD 70 C5 40 71 D2 E9 17 A2") { // 0011 0011 isCaseCharging shouldBe false } - create("07 19 01 0E 20 75 A9 >F4< 51 00 05 A0 37 92 35 49 79 CC DC 27 94 8E FB 72 12 94 52") { + create("07 19 01 0E 20 75 A9 >F4< 51 00 05 A0 37 92 35 49 79 CC DC 27 94 8E FB 72 12 94 52") { // 0101 0011 isCaseCharging shouldBe true } @@ -171,75 +171,75 @@ class DualApplePodsTest : BaseAirPodsTest() { @Test fun `test AirPodDevice - case lid test`() = runBlockingTest { // Lid open - create("07 19 01 0E 20 55 AA B4 >31< 00 00 A1 D0 BD 82 D3 52 86 CA FC 11 62 DC 42 C6 92 8E") { + create("07 19 01 0E 20 55 AA B4 >31< 00 00 A1 D0 BD 82 D3 52 86 CA FC 11 62 DC 42 C6 92 8E") { // 31 0011 0001 - caseLidState shouldBe DualApplePods.LidState.OPEN + caseLidState shouldBe DualAirPods.LidState.OPEN } // Lid open, left pod in case - create("07 19 01 0E 20 51 9A 93 >31< 00 00 95 D0 A5 D7 E3 F4 F1 38 38 99 61 3B 57 95 37 B7") { + create("07 19 01 0E 20 51 9A 93 >31< 00 00 95 D0 A5 D7 E3 F4 F1 38 38 99 61 3B 57 95 37 B7") { // 31 0011 0001 - caseLidState shouldBe DualApplePods.LidState.OPEN + caseLidState shouldBe DualAirPods.LidState.OPEN } // Lid open, left pod in case - create("07 19 01 0E 20 71 A9 92 31 00 00 DB 48 7F 32 8C CE 80 6F D9 27 98 D6 76 45 9D 62") { + create("07 19 01 0E 20 71 A9 92 31 00 00 DB 48 7F 32 8C CE 80 6F D9 27 98 D6 76 45 9D 62") { // 31 0011 0001 - caseLidState shouldBe DualApplePods.LidState.OPEN + caseLidState shouldBe DualAirPods.LidState.OPEN } // Lid just closed - create("07 19 01 0E 20 55 AA B4 >39< 00 00 08 A6 DB 99 E0 5E 14 85 E5 C2 0B 68 D7 FF C3 A1") { + create("07 19 01 0E 20 55 AA B4 >39< 00 00 08 A6 DB 99 E0 5E 14 85 E5 C2 0B 68 D7 FF C3 A1") { // 39 0011 1001 - caseLidState shouldBe DualApplePods.LidState.CLOSED + caseLidState shouldBe DualAirPods.LidState.CLOSED } // Lid closed - create("07 19 01 0E 20 55 AA B4 38 00 00 F3 F7 08 3B 98 09 C0 DD E4 BD BD 84 55 56 8B 81") { + create("07 19 01 0E 20 55 AA B4 38 00 00 F3 F7 08 3B 98 09 C0 DD E4 BD BD 84 55 56 8B 81") { // 38 0011 1000 - caseLidState shouldBe DualApplePods.LidState.CLOSED + caseLidState shouldBe DualAirPods.LidState.CLOSED } // Lid closed, right pod in case - create("07 19 01 0E 20 51 9A 93 >38< 00 00 3A D8 85 76 B0 91 48 31 DA FF 6C 4A 2B C2 67 F4") { + create("07 19 01 0E 20 51 9A 93 >38< 00 00 3A D8 85 76 B0 91 48 31 DA FF 6C 4A 2B C2 67 F4") { // 38 0011 1000 - caseLidState shouldBe DualApplePods.LidState.CLOSED + caseLidState shouldBe DualAirPods.LidState.CLOSED } // Lid closed, left pod in case - create("07 19 01 0E 20 71 A9 92 38 00 00 44 91 C4 8B 85 98 DD 55 4E 6A CA BC B5 CA 8D 37") { + create("07 19 01 0E 20 71 A9 92 38 00 00 44 91 C4 8B 85 98 DD 55 4E 6A CA BC B5 CA 8D 37") { // 38 0011 1000 - caseLidState shouldBe DualApplePods.LidState.CLOSED + caseLidState shouldBe DualAirPods.LidState.CLOSED } } @Test fun `test AirPodDevice - connection state`() = runBlockingTest { // Disconnected - create("07 19 01 0E 20 2B AA 8F 01 00 >00< 62 D4 BB F1 A7 F8 64 98 D2 C8 BD 7B 3A EF 2E 15") { + create("07 19 01 0E 20 2B AA 8F 01 00 >00< 62 D4 BB F1 A7 F8 64 98 D2 C8 BD 7B 3A EF 2E 15") { // 31 0011 0001 - connectionState shouldBe DualApplePods.ConnectionState.DISCONNECTED + state shouldBe DualAirPods.ConnectionState.DISCONNECTED } // Connected idle - create("07 19 01 0E 20 2B AA 8F 01 00 >04< 1D 69 69 9C C2 51 F3 1F BF 6E 45 DA 90 4A A3 E3") { + create("07 19 01 0E 20 2B AA 8F 01 00 >04< 1D 69 69 9C C2 51 F3 1F BF 6E 45 DA 90 4A A3 E3") { // 39 0011 1001 - connectionState shouldBe DualApplePods.ConnectionState.IDLE + state shouldBe DualAirPods.ConnectionState.IDLE } // Connected and playing music - create("07 19 01 0E 20 2B A9 8F 01 00 >05< 14 F7 CB 49 9F D3 B3 22 77 D2 22 F1 74 8C AC A6") { + create("07 19 01 0E 20 2B A9 8F 01 00 >05< 14 F7 CB 49 9F D3 B3 22 77 D2 22 F1 74 8C AC A6") { // 38 0011 1000 - connectionState shouldBe DualApplePods.ConnectionState.MUSIC + state shouldBe DualAirPods.ConnectionState.MUSIC } // Connected and call active - create("07 19 01 0E 20 2B 99 8F 01 00 >06< 0F 4B 43 25 E0 4A 73 63 14 22 C2 3C 89 13 BD 97") { + create("07 19 01 0E 20 2B 99 8F 01 00 >06< 0F 4B 43 25 E0 4A 73 63 14 22 C2 3C 89 13 BD 97") { // 38 0011 1000 - connectionState shouldBe DualApplePods.ConnectionState.CALL + state shouldBe DualAirPods.ConnectionState.CALL } // Connected and call active - create("07 19 01 0E 20 2B 99 8F 01 00 >07< E7 DF 76 44 85 B5 30 F4 95 14 02 DC A1 A4 8A 09") { + create("07 19 01 0E 20 2B 99 8F 01 00 >07< E7 DF 76 44 85 B5 30 F4 95 14 02 DC A1 A4 8A 09") { // 38 0011 1000 - connectionState shouldBe DualApplePods.ConnectionState.RINGING + state shouldBe DualAirPods.ConnectionState.RINGING } // Switching? - create("07 19 01 0E 20 2B 99 8F 01 00 >09< 10 30 EE F3 41 B5 D8 9F A3 B0 B4 17 9F 85 97 5F") { + create("07 19 01 0E 20 2B 99 8F 01 00 >09< 10 30 EE F3 41 B5 D8 9F A3 B0 B4 17 9F 85 97 5F") { // 38 0011 1000 - connectionState shouldBe DualApplePods.ConnectionState.HANGING_UP + state shouldBe DualAirPods.ConnectionState.HANGING_UP } } diff --git a/app/src/test/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1Test.kt b/app/src/test/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1Test.kt index 897c02b1..037635d7 100644 --- a/app/src/test/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1Test.kt +++ b/app/src/test/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1Test.kt @@ -1,7 +1,8 @@ package eu.darken.capod.pods.core.apple.airpods import eu.darken.capod.pods.core.apple.BaseAirPodsTest -import eu.darken.capod.pods.core.apple.DualApplePods +import eu.darken.capod.pods.core.apple.DualAirPods +import eu.darken.capod.pods.core.apple.HasAppleColor import io.kotest.matchers.shouldBe import kotlinx.coroutines.test.runBlockingTest import org.junit.jupiter.api.Test @@ -33,11 +34,11 @@ class AirPodsGen1Test : BaseAirPodsTest() { isRightPodInEar shouldBe false batteryCasePercent shouldBe 0.6f - caseLidState shouldBe DualApplePods.LidState.OPEN + caseLidState shouldBe DualAirPods.LidState.OPEN - connectionState shouldBe DualApplePods.ConnectionState.DISCONNECTED + state shouldBe DualAirPods.ConnectionState.DISCONNECTED - deviceColor shouldBe DualApplePods.DeviceColor.WHITE + podStyle.identifier shouldBe HasAppleColor.DeviceColor.WHITE.name } } } \ No newline at end of file diff --git a/app/src/test/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2Test.kt b/app/src/test/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2Test.kt index e3dd1964..afe84fd6 100644 --- a/app/src/test/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2Test.kt +++ b/app/src/test/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2Test.kt @@ -1,7 +1,8 @@ package eu.darken.capod.pods.core.apple.airpods import eu.darken.capod.pods.core.apple.BaseAirPodsTest -import eu.darken.capod.pods.core.apple.DualApplePods +import eu.darken.capod.pods.core.apple.DualAirPods +import eu.darken.capod.pods.core.apple.HasAppleColor import io.kotest.matchers.shouldBe import kotlinx.coroutines.test.runBlockingTest import org.junit.jupiter.api.Test @@ -32,11 +33,11 @@ class AirPodsGen2Test : BaseAirPodsTest() { isRightPodInEar shouldBe true batteryCasePercent shouldBe null - caseLidState shouldBe DualApplePods.LidState.NOT_IN_CASE + caseLidState shouldBe DualAirPods.LidState.NOT_IN_CASE - connectionState shouldBe DualApplePods.ConnectionState.MUSIC + state shouldBe DualAirPods.ConnectionState.MUSIC - deviceColor shouldBe DualApplePods.DeviceColor.WHITE + podStyle.identifier shouldBe HasAppleColor.DeviceColor.WHITE.name } } } \ No newline at end of file diff --git a/app/src/test/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3Test.kt b/app/src/test/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3Test.kt index 3c60347c..c2978ed0 100644 --- a/app/src/test/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3Test.kt +++ b/app/src/test/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3Test.kt @@ -1,7 +1,8 @@ package eu.darken.capod.pods.core.apple.airpods import eu.darken.capod.pods.core.apple.BaseAirPodsTest -import eu.darken.capod.pods.core.apple.DualApplePods +import eu.darken.capod.pods.core.apple.DualAirPods +import eu.darken.capod.pods.core.apple.HasAppleColor import io.kotest.matchers.shouldBe import kotlinx.coroutines.test.runBlockingTest import org.junit.jupiter.api.Test @@ -32,11 +33,11 @@ class AirPodsGen3Test : BaseAirPodsTest() { isRightPodInEar shouldBe false batteryCasePercent shouldBe 0.9f - caseLidState shouldBe DualApplePods.LidState.OPEN + caseLidState shouldBe DualAirPods.LidState.OPEN - connectionState shouldBe DualApplePods.ConnectionState.IDLE + state shouldBe DualAirPods.ConnectionState.IDLE - deviceColor shouldBe DualApplePods.DeviceColor.WHITE + podStyle.identifier shouldBe HasAppleColor.DeviceColor.WHITE.name } } @@ -54,11 +55,11 @@ class AirPodsGen3Test : BaseAirPodsTest() { isRightPodInEar shouldBe true batteryCasePercent shouldBe null - caseLidState shouldBe DualApplePods.LidState.NOT_IN_CASE + caseLidState shouldBe DualAirPods.LidState.NOT_IN_CASE - connectionState shouldBe DualApplePods.ConnectionState.UNKNOWN + state shouldBe DualAirPods.ConnectionState.UNKNOWN - deviceColor shouldBe DualApplePods.DeviceColor.WHITE + podStyle.identifier shouldBe HasAppleColor.DeviceColor.WHITE.name } } } \ No newline at end of file diff --git a/app/src/test/java/eu/darken/capod/pods/core/apple/airpods/AirPodsProTest.kt b/app/src/test/java/eu/darken/capod/pods/core/apple/airpods/AirPodsProTest.kt index 0191baeb..4f5bf513 100644 --- a/app/src/test/java/eu/darken/capod/pods/core/apple/airpods/AirPodsProTest.kt +++ b/app/src/test/java/eu/darken/capod/pods/core/apple/airpods/AirPodsProTest.kt @@ -1,7 +1,7 @@ package eu.darken.capod.pods.core.apple.airpods import eu.darken.capod.pods.core.apple.BaseAirPodsTest -import eu.darken.capod.pods.core.apple.DualApplePods +import eu.darken.capod.pods.core.apple.HasAppleColor import io.kotest.matchers.shouldBe import kotlinx.coroutines.test.runBlockingTest import org.junit.jupiter.api.Test @@ -33,7 +33,7 @@ class AirPodsProTest : BaseAirPodsTest() { isLeftPodCharging shouldBe true batteryCasePercent shouldBe 0.5f - deviceColor shouldBe DualApplePods.DeviceColor.WHITE + podStyle.identifier shouldBe HasAppleColor.DeviceColor.WHITE.name } } @@ -50,8 +50,6 @@ class AirPodsProTest : BaseAirPodsTest() { isRightPodCharging shouldBe false isLeftPodCharging shouldBe false batteryCasePercent shouldBe null - - deviceColor shouldBe DualApplePods.DeviceColor.WHITE } } @@ -66,8 +64,6 @@ class AirPodsProTest : BaseAirPodsTest() { isRightPodCharging shouldBe false isLeftPodCharging shouldBe false batteryCasePercent shouldBe null - - deviceColor shouldBe DualApplePods.DeviceColor.WHITE } create("0719010e202b668f01000500000000000000000000000000000000") { @@ -78,8 +74,6 @@ class AirPodsProTest : BaseAirPodsTest() { isRightPodCharging shouldBe false isLeftPodCharging shouldBe false batteryCasePercent shouldBe null - - deviceColor shouldBe DualApplePods.DeviceColor.WHITE } create("0719010e202b668f01000400000000000000000000000000000000") { @@ -90,8 +84,6 @@ class AirPodsProTest : BaseAirPodsTest() { isRightPodCharging shouldBe false isLeftPodCharging shouldBe false batteryCasePercent shouldBe null - - deviceColor shouldBe DualApplePods.DeviceColor.WHITE } create("0719010e200b668f01000500000000000000000000000000000000") { @@ -102,8 +94,6 @@ class AirPodsProTest : BaseAirPodsTest() { isRightPodCharging shouldBe false isLeftPodCharging shouldBe false batteryCasePercent shouldBe null - - deviceColor shouldBe DualApplePods.DeviceColor.WHITE } create("0719010e2003668f01000500000000000000000000000000000000") { @@ -114,8 +104,6 @@ class AirPodsProTest : BaseAirPodsTest() { isRightPodCharging shouldBe false isLeftPodCharging shouldBe false batteryCasePercent shouldBe null - - deviceColor shouldBe DualApplePods.DeviceColor.WHITE } create("0719010e2001668f01000500000000000000000000000000000000") { @@ -126,8 +114,6 @@ class AirPodsProTest : BaseAirPodsTest() { isRightPodCharging shouldBe false isLeftPodCharging shouldBe false batteryCasePercent shouldBe null - - deviceColor shouldBe DualApplePods.DeviceColor.WHITE } create("0719010e2009668f01000500000000000000000000000000000000") { @@ -138,8 +124,6 @@ class AirPodsProTest : BaseAirPodsTest() { isRightPodCharging shouldBe false isLeftPodCharging shouldBe false batteryCasePercent shouldBe null - - deviceColor shouldBe DualApplePods.DeviceColor.WHITE } create("0719010e2053669653000500000000000000000000000000000000") { @@ -150,8 +134,6 @@ class AirPodsProTest : BaseAirPodsTest() { isRightPodCharging shouldBe true isLeftPodCharging shouldBe false batteryCasePercent shouldBe 0.6f - - deviceColor shouldBe DualApplePods.DeviceColor.WHITE } create("0719010e203366a602000500000000000000000000000000000000") { @@ -162,8 +144,6 @@ class AirPodsProTest : BaseAirPodsTest() { isRightPodCharging shouldBe true isLeftPodCharging shouldBe false batteryCasePercent shouldBe 0.6f - - deviceColor shouldBe DualApplePods.DeviceColor.WHITE } create("0719010e202b768f02000500000000000000000000000000000000") { @@ -174,8 +154,6 @@ class AirPodsProTest : BaseAirPodsTest() { isRightPodCharging shouldBe false isLeftPodCharging shouldBe false batteryCasePercent shouldBe 0.6f - - deviceColor shouldBe DualApplePods.DeviceColor.WHITE } } @@ -190,8 +168,6 @@ class AirPodsProTest : BaseAirPodsTest() { isRightPodCharging shouldBe true isLeftPodCharging shouldBe false batteryCasePercent shouldBe 0.6f - - deviceColor shouldBe DualApplePods.DeviceColor.WHITE } } } \ No newline at end of file diff --git a/app/src/test/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsProTest.kt b/app/src/test/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsProTest.kt index 36378b37..1d7c3e20 100644 --- a/app/src/test/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsProTest.kt +++ b/app/src/test/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsProTest.kt @@ -1,7 +1,7 @@ package eu.darken.capod.pods.core.apple.beats import eu.darken.capod.pods.core.apple.BaseAirPodsTest -import eu.darken.capod.pods.core.apple.DualApplePods +import eu.darken.capod.pods.core.apple.HasAppleColor import io.kotest.matchers.shouldBe import kotlinx.coroutines.test.runBlockingTest import org.junit.jupiter.api.Test @@ -33,7 +33,7 @@ class PowerBeatsProTest : BaseAirPodsTest() { isLeftPodCharging shouldBe true batteryCasePercent shouldBe 0.5f - deviceColor shouldBe DualApplePods.DeviceColor.WHITE + podStyle.identifier shouldBe HasAppleColor.DeviceColor.WHITE.name } } } \ No newline at end of file diff --git a/app/src/test/java/eu/darken/capod/pods/core/apple/fakes/Twsi99999Test.kt b/app/src/test/java/eu/darken/capod/pods/core/apple/misc/Twsi99999Test.kt similarity index 67% rename from app/src/test/java/eu/darken/capod/pods/core/apple/fakes/Twsi99999Test.kt rename to app/src/test/java/eu/darken/capod/pods/core/apple/misc/Twsi99999Test.kt index d69b4212..f9159c70 100644 --- a/app/src/test/java/eu/darken/capod/pods/core/apple/fakes/Twsi99999Test.kt +++ b/app/src/test/java/eu/darken/capod/pods/core/apple/misc/Twsi99999Test.kt @@ -1,7 +1,6 @@ -package eu.darken.capod.pods.core.apple.fakes +package eu.darken.capod.pods.core.apple.misc import eu.darken.capod.pods.core.apple.BaseAirPodsTest -import eu.darken.capod.pods.core.apple.DualApplePods import io.kotest.matchers.shouldBe import kotlinx.coroutines.test.runBlockingTest import org.junit.jupiter.api.Test @@ -25,18 +24,8 @@ class Twsi99999Test : BaseAirPodsTest() { batteryRightPodPercent shouldBe 1.0f isCaseCharging shouldBe false - isLeftPodCharging shouldBe true - isRightPodCharging shouldBe true - isLeftPodInEar shouldBe false - isRightPodInEar shouldBe false batteryCasePercent shouldBe 0.7f - - caseLidState shouldBe DualApplePods.LidState.OPEN - - connectionState shouldBe DualApplePods.ConnectionState.UNKNOWN - - deviceColor shouldBe DualApplePods.DeviceColor.WHITE } } } \ No newline at end of file