Improved device cards

This commit is contained in:
darken
2022-01-04 01:46:56 +01:00
parent 703cd75870
commit 58ce28e91c
32 changed files with 644 additions and 113 deletions
@@ -26,7 +26,7 @@ class BleScanner @Inject constructor(
// TODO check Bluetooth enabled
fun scan(
filter: Set<ScanFilter> = ProximityPairing.getBleScanFilter(),
mode: Int = ScanSettings.SCAN_MODE_LOW_LATENCY,
mode: Int = ScanSettings.SCAN_MODE_LOW_POWER,
delay: Long = 1,
): Flow<List<ScanResult>> = callbackFlow {
val scanner = bluetoothManager.adapter.bluetoothLeScanner
@@ -1,7 +1,11 @@
package eu.darken.capod.common.debug.autoreport
import android.content.Context
import android.content.SharedPreferences
import androidx.preference.PreferenceDataStore
import dagger.hilt.android.qualifiers.ApplicationContext
import eu.darken.androidstarter.common.preferences.Settings
import eu.darken.capod.common.preferences.PreferenceStoreMapper
import eu.darken.capod.common.preferences.createFlowPreference
import javax.inject.Inject
import javax.inject.Singleton
@@ -9,12 +13,26 @@ import javax.inject.Singleton
@Singleton
class DebugSettings @Inject constructor(
@ApplicationContext private val context: Context,
) {
) : Settings() {
private val prefs by lazy {
context.getSharedPreferences("settings_debug", Context.MODE_PRIVATE)
override val preferences: SharedPreferences = context.getSharedPreferences("settings_debug", Context.MODE_PRIVATE)
val isAutoReportEnabled = preferences.createFlowPreference("debug.bugreport.automatic.enabled", true)
val isDebugModeEnabled = preferences.createFlowPreference("debug.mode.enabled", false)
override val preferenceDataStore: PreferenceDataStore = object : PreferenceStoreMapper() {
override fun getBoolean(key: String, defValue: Boolean): Boolean = when (key) {
isAutoReportEnabled.key -> isAutoReportEnabled.value
isDebugModeEnabled.key -> isDebugModeEnabled.value
else -> super.getBoolean(key, defValue)
}
override fun putBoolean(key: String, value: Boolean) = when (key) {
isAutoReportEnabled.key -> isAutoReportEnabled.update { value }
isDebugModeEnabled.key -> isDebugModeEnabled.update { value }
else -> super.putBoolean(key, value)
}
}
val isAutoReportEnabled = prefs.createFlowPreference("bugreport.automatic.enabled", true)
}
@@ -11,4 +11,16 @@ interface BindableVH<ItemT, ViewBindingT : ViewBinding> {
fun bind(item: ItemT, payloads: MutableList<Any> = mutableListOf()) = with(viewBinding.value) {
onBindData(item, payloads)
}
}
@Suppress("unused")
inline fun <reified ItemT, ViewBindingT : ViewBinding> BindableVH<ItemT, ViewBindingT>.binding(
payload: Boolean = true,
crossinline block: ViewBindingT.(ItemT) -> Unit,
): ViewBindingT.(item: ItemT, payloads: List<Any>) -> Unit = { item: ItemT, payloads: List<Any> ->
val newestItem = when (payload) {
true -> payloads.filterIsInstance<ItemT>().lastOrNull() ?: item
false -> item
}
block(newestItem)
}
@@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
class FlowPreference<T> constructor(
private val preferences: SharedPreferences,
private val key: String,
val key: String,
private val reader: SharedPreferences.(key: String) -> T,
private val writer: SharedPreferences.Editor.(key: String, value: T) -> Unit
) {
@@ -17,22 +17,20 @@ class GeneralSettings @Inject constructor(
private val debugSettings: DebugSettings,
) : Settings() {
override val preferences: SharedPreferences = context.getSharedPreferences("settings_general", Context.MODE_PRIVATE)
override val preferenceDataStore: PreferenceDataStore = object : PreferenceStoreMapper() {
override fun getBoolean(key: String, defValue: Boolean): Boolean = when (key) {
PK_BUGTRACKING_ENABLED -> debugSettings.isAutoReportEnabled.value
else -> super.getBoolean(key, defValue)
else -> debugSettings.preferenceDataStore.getBoolean(key, defValue)
}
override fun putBoolean(key: String, value: Boolean) = when (key) {
PK_BUGTRACKING_ENABLED -> debugSettings.isAutoReportEnabled.update { value }
else -> super.putBoolean(key, value)
else -> debugSettings.preferenceDataStore.putBoolean(key, value)
}
}
override val preferences: SharedPreferences = context.getSharedPreferences("settings_general", Context.MODE_PRIVATE)
companion object {
internal val TAG = logTag("Core", "Settings")
private const val PK_BUGTRACKING_ENABLED = "core.bugtracking.enabled"
}
}
@@ -12,7 +12,9 @@ import eu.darken.capod.common.lists.modular.ModularAdapter
import eu.darken.capod.common.lists.modular.mods.DataBinderMod
import eu.darken.capod.common.lists.modular.mods.TypedVHCreatorMod
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.UnknownPodDeviceCardVH
import javax.inject.Inject
@@ -26,6 +28,8 @@ class OverviewAdapter @Inject constructor() :
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 UnknownPodDeviceCardVH.Item }) { UnknownPodDeviceCardVH(it) })
}
@@ -6,6 +6,7 @@ import androidx.lifecycle.SavedStateHandle
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import eu.darken.capod.common.coroutine.DispatcherProvider
import eu.darken.capod.common.debug.autoreport.DebugSettings
import eu.darken.capod.common.debug.recording.core.RecorderModule
import eu.darken.capod.common.livedata.SingleLiveEvent
import eu.darken.capod.common.navigation.navVia
@@ -13,14 +14,20 @@ import eu.darken.capod.common.permissions.Permission
import eu.darken.capod.common.permissions.isGrantedOrNotRequired
import eu.darken.capod.common.uix.ViewModel3
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.UnknownPodDeviceCardVH
import eu.darken.capod.monitor.core.PodMonitor
import eu.darken.capod.monitor.core.worker.MonitorControl
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 kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.isActive
import java.util.*
import javax.inject.Inject
@@ -32,12 +39,19 @@ class OverviewFragmentVM @Inject constructor(
private val recorderModule: RecorderModule,
private val monitorControl: MonitorControl,
private val podMonitor: PodMonitor,
private val debugSettings: DebugSettings,
) : ViewModel3(dispatcherProvider = dispatcherProvider) {
private val enabledState: Flow<Boolean> = flow {
emit(false)
}
private val updateTicker = channelFlow<Unit> {
while (isActive) {
trySend(Unit)
delay(1000)
}
}
private val permissionCheckTrigger = MutableStateFlow(UUID.randomUUID())
private val requiredPermissions: Flow<List<Permission>> = permissionCheckTrigger
.map {
@@ -64,10 +78,12 @@ class OverviewFragmentVM @Inject constructor(
}
val listItems: LiveData<List<OverviewAdapter.Item>> = combine(
updateTicker,
enabledState,
requiredPermissions,
pods
) { state, permissions, pods ->
pods,
debugSettings.isDebugModeEnabled.flow
) { tick, state, permissions, pods, isDebugMode ->
val items = mutableListOf<OverviewAdapter.Item>()
permissions
@@ -82,7 +98,9 @@ class OverviewFragmentVM @Inject constructor(
pods
.map {
when (it) {
is DualApplePods -> DualApplePodsCardVH.Item(it)
is DualApplePods -> DualApplePodsCardVH.Item(it, showDebug = isDebugMode)
is SingleApplePods -> SingleApplePodsCardVH.Item(it, showDebug = isDebugMode)
is BasicSingleApplePods -> BasicSingleApplePodsCardVH.Item(it, showDebug = isDebugMode)
else -> UnknownPodDeviceCardVH.Item(it)
}
}
@@ -0,0 +1,60 @@
package eu.darken.capod.main.ui.overview.cards.pods
import android.icu.text.RelativeDateTimeFormatter
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.getBatteryLevelHeadset
import java.time.Duration
import java.time.Instant
class BasicSingleApplePodsCardVH(parent: ViewGroup) :
PodDeviceVH<BasicSingleApplePodsCardVH.Item, OverviewPodsAppleSingleBasicItemBinding>(
R.layout.overview_pods_apple_single_basic_item,
parent
) {
override val viewBinding = lazy { OverviewPodsAppleSingleBasicItemBinding.bind(itemView) }
private val lastSeenFormatter = RelativeDateTimeFormatter.getInstance()
override val onBindData = binding(payload = true) { item: Item ->
val device = item.device
name.text = device.getLabel(context)
deviceIcon.setImageResource(device.iconRes)
val duration = Duration.between(device.lastSeenAt, Instant.now())
lastSeen.text = lastSeenFormatter.format(
duration.seconds.toDouble(),
RelativeDateTimeFormatter.Direction.LAST,
RelativeDateTimeFormatter.RelativeUnit.SECONDS
)
reception.text = device.getSignalQuality(context)
headphones.apply {
val sb = StringBuilder(context.getString(R.string.pods_single_headphones_label))
sb.append("\n").append(device.getBatteryLevelHeadset(context))
text = sb
}
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 device: BasicSingleApplePods,
override val showDebug: Boolean,
) : PodDeviceVH.Item
}
@@ -3,15 +3,15 @@ package eu.darken.capod.main.ui.overview.cards.pods
import android.icu.text.RelativeDateTimeFormatter
import android.view.ViewGroup
import eu.darken.capod.R
import eu.darken.capod.common.BuildConfigWrap
import eu.darken.capod.common.lists.binding
import eu.darken.capod.databinding.OverviewPodsAppleDualItemBinding
import eu.darken.capod.pods.core.DualPods
import eu.darken.capod.pods.core.DualPodDevice.Pod
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 eu.darken.capod.pods.core.getBatteryCase
import eu.darken.capod.pods.core.getBatteryLeftPod
import eu.darken.capod.pods.core.getBatteryRightPod
import eu.darken.capod.pods.core.getBatteryLevelCase
import eu.darken.capod.pods.core.getBatteryLevelLeftPod
import eu.darken.capod.pods.core.getBatteryLevelRightPod
import java.time.Duration
import java.time.Instant
@@ -21,19 +21,13 @@ class DualApplePodsCardVH(parent: ViewGroup) :
parent
) {
override val viewBinding = lazy {
OverviewPodsAppleDualItemBinding.bind(itemView)
}
override val viewBinding = lazy { OverviewPodsAppleDualItemBinding.bind(itemView) }
private val lastSeenFormatter = RelativeDateTimeFormatter.getInstance()
override val onBindData: OverviewPodsAppleDualItemBinding.(
item: Item,
payloads: List<Any>
) -> Unit = { item, _ ->
override val onBindData = binding(payload = true) { item: Item ->
val device = item.device
name.apply {
val sb = StringBuilder(device.getLabel(context))
if (device.deviceColor != DeviceColor.UNKNOWN) {
@@ -54,7 +48,7 @@ class DualApplePodsCardVH(parent: ViewGroup) :
podLeft.apply {
val sb = StringBuilder(context.getString(R.string.pods_dual_left_label))
sb.append("\n").append(device.getBatteryLeftPod(context))
sb.append("\n").append(device.getBatteryLevelLeftPod(context))
when {
device.isLeftPodCharging -> sb.append("\n").append("Charging")
device.isLeftPodInEar -> sb.append("\n").append("In ear")
@@ -65,7 +59,7 @@ class DualApplePodsCardVH(parent: ViewGroup) :
podRight.apply {
val sb = StringBuilder("Right pod")
sb.append("\n").append(device.getBatteryRightPod(context))
sb.append("\n").append(device.getBatteryLevelRightPod(context))
when {
device.isRightPodCharging -> sb.append("\n").append("Charging")
device.isRightPodInEar -> sb.append("\n").append("In ear")
@@ -75,13 +69,13 @@ class DualApplePodsCardVH(parent: ViewGroup) :
}
when (device.microPhonePod) {
DualPods.Pod.LEFT -> podLeft.append("\n(Microphone)")
DualPods.Pod.RIGHT -> podRight.append("\n(Microphone)")
Pod.LEFT -> podLeft.append("\n(Microphone)")
Pod.RIGHT -> podRight.append("\n(Microphone)")
}
podCase.apply {
val sb = StringBuilder("Case")
sb.append("\n").append(device.getBatteryCase(context))
sb.append("\n").append(device.getBatteryLevelCase(context))
if (device.isCaseCharging) sb.append("\n").append("Charging")
when (device.caseLidState) {
LidState.OPEN -> sb.append("\n").append(context.getString(R.string.pods_case_status_open_label))
@@ -93,14 +87,18 @@ class DualApplePodsCardVH(parent: ViewGroup) :
text = sb
}
status.text = getString(R.string.pods_status_x_label, device.getConnectionStateLabel(context))
if (BuildConfigWrap.DEBUG) {
status.append("\n")
status.append(device.identifier.toString())
status.apply {
val sb = StringBuilder(getString(R.string.pods_status_x_label, 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 device: DualApplePods
override val device: DualApplePods,
override val showDebug: Boolean,
) : PodDeviceVH.Item
}
@@ -18,6 +18,8 @@ abstract class PodDeviceVH<D : PodDeviceVH.Item, B : ViewBinding>(
val device: PodDevice
val showDebug: Boolean
override val stableId: Long get() = device.identifier.hashCode().toLong()
override val payloadProvider: ((DifferItem, DifferItem) -> DifferItem?)?
@@ -0,0 +1,65 @@
package eu.darken.capod.main.ui.overview.cards.pods
import android.icu.text.RelativeDateTimeFormatter
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.OverviewPodsAppleSingleItemBinding
import eu.darken.capod.pods.core.apple.SingleApplePods
import eu.darken.capod.pods.core.getBatteryLevelHeadset
import java.time.Duration
import java.time.Instant
class SingleApplePodsCardVH(parent: ViewGroup) :
PodDeviceVH<SingleApplePodsCardVH.Item, OverviewPodsAppleSingleItemBinding>(
R.layout.overview_pods_apple_single_item,
parent
) {
override val viewBinding = lazy { OverviewPodsAppleSingleItemBinding.bind(itemView) }
private val lastSeenFormatter = RelativeDateTimeFormatter.getInstance()
override val onBindData = binding(payload = true) { item: Item ->
val device = item.device
name.text = device.getLabel(context)
deviceIcon.setImageResource(device.iconRes)
val duration = Duration.between(device.lastSeenAt, Instant.now())
lastSeen.text = lastSeenFormatter.format(
duration.seconds.toDouble(),
RelativeDateTimeFormatter.Direction.LAST,
RelativeDateTimeFormatter.RelativeUnit.SECONDS
)
reception.text = device.getSignalQuality(context)
headphones.apply {
val sb = StringBuilder(context.getString(R.string.pods_single_headphones_label))
sb.append("\n").append(device.getBatteryLevelHeadset(context))
when {
device.isHeadsetBeingCharged -> sb.append("\n").append("Charging")
device.isHeadsetBeingWorn -> sb.append("\n").append("In ear")
else -> {}
}
text = sb
}
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 device: SingleApplePods,
override val showDebug: Boolean,
) : PodDeviceVH.Item
}
@@ -3,6 +3,7 @@ package eu.darken.capod.main.ui.overview.cards.pods
import android.icu.text.RelativeDateTimeFormatter
import android.view.ViewGroup
import eu.darken.capod.R
import eu.darken.capod.common.lists.binding
import eu.darken.capod.databinding.OverviewPodsUnknownItemBinding
import eu.darken.capod.pods.core.PodDevice
import java.time.Duration
@@ -20,10 +21,7 @@ class UnknownPodDeviceCardVH(parent: ViewGroup) :
private val lastSeenFormatter = RelativeDateTimeFormatter.getInstance()
override val onBindData: OverviewPodsUnknownItemBinding.(
item: Item,
payloads: List<Any>
) -> Unit = { item, _ ->
override val onBindData = binding(payload = true) { item ->
val device = item.device
name.text = device.getLabel(context)
@@ -33,9 +31,12 @@ class UnknownPodDeviceCardVH(parent: ViewGroup) :
RelativeDateTimeFormatter.Direction.LAST,
RelativeDateTimeFormatter.RelativeUnit.SECONDS
)
rawdata.text = device.rawDataHex
}
data class Item(
override val device: PodDevice
override val device: PodDevice,
override val showDebug: Boolean = false
) : PodDeviceVH.Item
}
@@ -31,9 +31,39 @@ class PodMonitor @Inject constructor(
}
.onStart { emptyList<PodDevice>() }
.map { scanResults ->
scanResults
.sortedByDescending { it.rssi }
.mapNotNull { podFactory.createPod(it) }
val pods = scanResults
.sortedByDescending { it.rssi }
.mapNotNull { podFactory.createPod(it) }
// if (BuildConfigWrap.DEBUG && scanResults.isNotEmpty()) {
// val fake1 = AirPodsMax(
// identifier = UUID.fromString("63436171-5433-4506-8bf5-faf6c35ffa8a"),
// scanResult = scanResults.first(),
// proximityMessage = ProximityPairing.Decoder().decode(
// message = ContinuityProtocol.Message(
// type = 0x07.toUByte(),
// length = 25,
// data = arrayOf<Byte>(1, 10, 32, 98, 4, -128, 1, 15, 64, 13, 112, 80, 22, -14, 64, -125, 22, -65, 16, 22, 52, -101, 116, -124, -24).toByteArray().toUByteArray()
// )
// )!!
// )
// val fake2 = BeatsFlex(
// identifier = UUID.fromString("e563098d-ea24-4136-a169-5a59344317c3"),
// scanResult = scanResults.first(),
// proximityMessage = ProximityPairing.Decoder().decode(
// message = ContinuityProtocol.Message(
// type = 0x07.toUByte(),
// length = 25,
// data = arrayOf<Byte>(1, 16, 32, 10, -12, -113, 0, 1, 0, -60, 113, -97, -100, -17, -94, -29, -70, 102, -2, 29, 69, -97, -55, 47, -96).toByteArray().toUByteArray()
// )
// )!!
// )
// pods.plus(fake1).plus(fake2)
// } else {
// pods
// }
pods
}
companion object {
@@ -21,9 +21,9 @@ import eu.darken.capod.common.notifications.PendingIntentCompat
import eu.darken.capod.main.ui.MainActivity
import eu.darken.capod.pods.core.PodDevice
import eu.darken.capod.pods.core.apple.DualApplePods
import eu.darken.capod.pods.core.getBatteryCase
import eu.darken.capod.pods.core.getBatteryLeftPod
import eu.darken.capod.pods.core.getBatteryRightPod
import eu.darken.capod.pods.core.getBatteryLevelCase
import eu.darken.capod.pods.core.getBatteryLevelLeftPod
import eu.darken.capod.pods.core.getBatteryLevelRightPod
import javax.inject.Inject
@@ -70,11 +70,11 @@ class MonitorNotifications @Inject constructor(
val infoText = when (device) {
is DualApplePods -> {
val sb = StringBuilder()
sb.append("L: ${device.getBatteryLeftPod(context)}")
sb.append("L: ${device.getBatteryLevelLeftPod(context)}")
sb.append(" | ")
sb.append("C: ${device.getBatteryCase(context)}")
sb.append("C: ${device.getBatteryLevelCase(context)}")
sb.append(" | ")
sb.append("R: ${device.getBatteryRightPod(context)}")
sb.append("R: ${device.getBatteryLevelRightPod(context)}")
}
else -> {
"Unknown device"
@@ -1,8 +1,6 @@
package eu.darken.capod.pods.core
interface DualPods : PodDevice {
val microPhonePod: Pod
interface DualPodDevice : PodDevice {
enum class Pod {
LEFT,
@@ -4,14 +4,14 @@ import android.content.Context
import eu.darken.capod.R
import kotlin.math.roundToInt
fun DualPods.getBatteryLeftPod(context: Context): String =
fun DualPodDevice.getBatteryLevelLeftPod(context: Context): String =
batteryLeftPodPercent?.let { "${(it * 100).roundToInt()}%" }
?: context.getString(R.string.general_value_not_available_label)
fun DualPods.getBatteryRightPod(context: Context): String =
fun DualPodDevice.getBatteryLevelRightPod(context: Context): String =
batteryRightPodPercent?.let { "${(it * 100).roundToInt()}%" }
?: context.getString(R.string.general_value_not_available_label)
fun DualPods.getBatteryCase(context: Context): String =
fun DualPodDevice.getBatteryLevelCase(context: Context): String =
batteryCasePercent?.let { "${(it * 100).roundToInt()}%" }
?: context.getString(R.string.general_value_not_available_label)
@@ -12,13 +12,18 @@ interface PodDevice {
val identifier: UUID
val scanResult: ScanResult
val lastSeenAt: Instant
val scanResult: ScanResult
val rssi: Int
get() = scanResult.rssi
val rawData: ByteArray
val rawDataHex: String
get() = rawData.joinToString(separator = " ") { String.format("%02X", it) }
fun getSignalQuality(context: Context): String {
val percentage = (100 - abs(rssi))
return "~$percentage%"
@@ -0,0 +1,6 @@
package eu.darken.capod.pods.core
interface SinglePodDevice : PodDevice {
val batteryHeadsetPercent: Float?
}
@@ -0,0 +1,9 @@
package eu.darken.capod.pods.core
import android.content.Context
import eu.darken.capod.R
import kotlin.math.roundToInt
fun SinglePodDevice.getBatteryLevelHeadset(context: Context): String =
batteryHeadsetPercent?.let { "${(it * 100).roundToInt()}%" }
?: context.getString(R.string.general_value_not_available_label)
@@ -2,6 +2,7 @@ package eu.darken.capod.pods.core.apple
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.pods.core.PodDevice
import eu.darken.capod.pods.core.apple.protocol.ContinuityProtocol
import eu.darken.capod.pods.core.apple.protocol.ProximityPairing
interface ApplePods : PodDevice {
@@ -10,6 +11,9 @@ interface ApplePods : PodDevice {
val proximityMessage: ProximityPairing.Message
override val rawData: ByteArray
get() = scanResult.scanRecord!!.getManufacturerSpecificData(ContinuityProtocol.APPLE_COMPANY_IDENTIFIER)!!
// We start counting at the airpods prefix byte
val rawPrefix: UByte
get() = proximityMessage.data[0]
@@ -2,10 +2,11 @@ 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.SinglePodDevice
interface BasicSingleApplePods : ApplePods {
interface BasicSingleApplePods : ApplePods, SinglePodDevice {
val batteryHeadsetPercent: Float?
override val batteryHeadsetPercent: Float?
get() = when (val value = rawPodsBattery.lowerNibble.toInt()) {
15 -> null
else -> if (value > 10) {
@@ -7,12 +7,12 @@ 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.DualPods
import eu.darken.capod.pods.core.DualPods.Pod
import eu.darken.capod.pods.core.DualPodDevice
import eu.darken.capod.pods.core.DualPodDevice.Pod
interface DualApplePods : ApplePods, DualPods {
interface DualApplePods : ApplePods, DualPodDevice {
override val microPhonePod: Pod
val microPhonePod: Pod
get() = when (rawStatus.isBitSet(5)) {
true -> Pod.LEFT
false -> Pod.RIGHT
@@ -2,6 +2,7 @@ package eu.darken.capod.pods.core.apple.airpods
import android.bluetooth.le.ScanResult
import android.content.Context
import eu.darken.capod.R
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.pods.core.apple.DualApplePods
import eu.darken.capod.pods.core.apple.protocol.ProximityPairing
@@ -19,5 +20,8 @@ data class AirPodsPro constructor(
return "AirPods Pro"
}
override val iconRes: Int
get() = R.drawable.ic_device_airpods_gen2
override val tag: String = logTag("Pod", "Apple", "AirPods", "Pro")
}
@@ -33,31 +33,13 @@
android:layout_marginStart="4dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:gravity="center_vertical"
app:layout_constraintBottom_toTopOf="@id/barrier_top"
app:layout_constraintBottom_toTopOf="@+id/last_seen"
app:layout_constraintEnd_toStartOf="@id/reception"
app:layout_constraintStart_toEndOf="@id/device_icon"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
tools:text="Apple AirPods Pro" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/reception"
style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:gravity="center"
app:drawableRightCompat="@drawable/ic_baseline_signal_cellular_alt_24"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/name"
app:layout_constraintTop_toTopOf="parent"
tools:text="Yours (RSSI -61)" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/last_seen"
style="@style/TextAppearance.MaterialComponents.Caption"
@@ -70,6 +52,36 @@
app:layout_constraintTop_toBottomOf="@id/name"
tools:text="3s ago" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/reception"
style="@style/TextAppearance.MaterialComponents.Caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginStart="8dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="@id/name"
app:layout_constraintEnd_toStartOf="@id/reception_icon"
app:layout_constraintStart_toEndOf="@id/name"
app:layout_constraintTop_toTopOf="@+id/name"
tools:text="Yours (RSSI -61)" />
<ImageView
android:id="@+id/reception_icon"
style="@style/TextAppearance.MaterialComponents.Caption"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginHorizontal="16dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="16dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="@+id/reception"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/reception"
app:layout_constraintTop_toTopOf="@+id/reception"
app:srcCompat="@drawable/ic_baseline_signal_cellular_alt_24"
tools:text="Yours (RSSI -61)" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_top"
android:layout_width="wrap_content"
@@ -137,12 +149,9 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:gravity="center_vertical"
app:drawableLeftCompat="@drawable/ic_baseline_apps_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/card"
style="@style/MyCardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="8dp"
tools:context=".main.ui.MainActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/card_content"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/device_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="16dp"
app:layout_constraintBottom_toBottomOf="@id/name"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/name"
app:srcCompat="@drawable/ic_device_generic_headphones" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/name"
style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginStart="4dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:gravity="center_vertical"
app:layout_constraintBottom_toTopOf="@+id/last_seen"
app:layout_constraintEnd_toStartOf="@id/reception"
app:layout_constraintStart_toEndOf="@id/device_icon"
app:layout_constraintTop_toTopOf="parent"
tools:text="Power Beats 3" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/last_seen"
style="@style/TextAppearance.MaterialComponents.Caption"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@id/barrier_top"
app:layout_constraintEnd_toEndOf="@id/name"
app:layout_constraintStart_toStartOf="@id/name"
app:layout_constraintTop_toBottomOf="@id/name"
tools:text="3s ago" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/reception"
style="@style/TextAppearance.MaterialComponents.Caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginStart="8dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="@id/name"
app:layout_constraintEnd_toStartOf="@id/reception_icon"
app:layout_constraintStart_toEndOf="@id/name"
app:layout_constraintTop_toTopOf="@+id/name"
tools:text="Yours (RSSI -61)" />
<ImageView
android:id="@+id/reception_icon"
style="@style/TextAppearance.MaterialComponents.Caption"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginHorizontal="16dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="16dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="@+id/reception"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/reception"
app:layout_constraintTop_toTopOf="@+id/reception"
app:srcCompat="@drawable/ic_baseline_signal_cellular_alt_24"
tools:text="Yours (RSSI -61)" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="headphones"
tools:layout_editor_absoluteY="60dp" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/headphones"
style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:gravity="center_horizontal"
app:drawableTopCompat="@drawable/ic_device_generic_headphones"
app:layout_constraintBottom_toTopOf="@id/barrier_bottom"
app:layout_constraintEnd_toEndOf="parent"
app:layout_goneMarginBottom="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier_top"
app:layout_constraintVertical_bias="0.0"
tools:text="Headphones\n50%\nCharging" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="headphones" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/status"
style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:gravity="center_vertical"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier_bottom"
tools:text="Music Active, Case Closed" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/card"
style="@style/MyCardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="8dp"
tools:context=".main.ui.MainActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/card_content"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/device_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="16dp"
app:layout_constraintBottom_toBottomOf="@id/name"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/name"
app:srcCompat="@drawable/ic_device_generic_headphones" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/name"
style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginStart="4dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:gravity="center_vertical"
app:layout_constraintBottom_toTopOf="@+id/last_seen"
app:layout_constraintEnd_toStartOf="@id/reception"
app:layout_constraintStart_toEndOf="@id/device_icon"
app:layout_constraintTop_toTopOf="parent"
tools:text="Apple AirPods Max" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/last_seen"
style="@style/TextAppearance.MaterialComponents.Caption"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@id/barrier_top"
app:layout_constraintEnd_toEndOf="@id/name"
app:layout_constraintStart_toStartOf="@id/name"
app:layout_constraintTop_toBottomOf="@id/name"
tools:text="3s ago" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/reception"
style="@style/TextAppearance.MaterialComponents.Caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginStart="8dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="@id/name"
app:layout_constraintEnd_toStartOf="@id/reception_icon"
app:layout_constraintStart_toEndOf="@id/name"
app:layout_constraintTop_toTopOf="@+id/name"
tools:text="Yours (RSSI -61)" />
<ImageView
android:id="@+id/reception_icon"
style="@style/TextAppearance.MaterialComponents.Caption"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginHorizontal="16dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="16dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="@+id/reception"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/reception"
app:layout_constraintTop_toTopOf="@+id/reception"
app:srcCompat="@drawable/ic_baseline_signal_cellular_alt_24"
tools:text="Yours (RSSI -61)" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="headphones"
tools:layout_editor_absoluteY="60dp" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/headphones"
style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:gravity="center_horizontal"
app:drawableTopCompat="@drawable/ic_device_generic_headphones"
app:layout_constraintBottom_toTopOf="@id/barrier_bottom"
app:layout_constraintEnd_toEndOf="parent"
app:layout_goneMarginBottom="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier_top"
app:layout_constraintVertical_bias="0.0"
tools:text="Headphones\n50%\nCharging" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="headphones" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/status"
style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:gravity="center_vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier_bottom"
tools:text="Music Active, Case Closed" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
@@ -18,23 +18,22 @@
style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:gravity="center_vertical"
app:layout_constraintBottom_toTopOf="@+id/last_seen"
app:layout_constraintEnd_toStartOf="@id/reception"
app:layout_constraintHorizontal_bias="0.49"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Unknown Device" />
tools:text="Apple AirPods Max" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/last_seen"
style="@style/TextAppearance.MaterialComponents.Caption"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@id/rawdata_label"
app:layout_constraintEnd_toEndOf="@id/name"
app:layout_constraintStart_toStartOf="@id/name"
@@ -43,17 +42,33 @@
<com.google.android.material.textview.MaterialTextView
android:id="@+id/reception"
style="@style/TextAppearance.MaterialComponents.Body2"
style="@style/TextAppearance.MaterialComponents.Caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="@id/name"
app:layout_constraintEnd_toStartOf="@id/reception_icon"
app:layout_constraintStart_toEndOf="@id/name"
app:layout_constraintTop_toTopOf="parent"
tools:text="RSSI -100 dbM" />
app:layout_constraintTop_toTopOf="@+id/name"
tools:text="Yours (RSSI -61)" />
<ImageView
android:id="@+id/reception_icon"
style="@style/TextAppearance.MaterialComponents.Caption"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginHorizontal="16dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="16dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="@+id/reception"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/reception"
app:layout_constraintTop_toTopOf="@+id/reception"
app:srcCompat="@drawable/ic_baseline_signal_cellular_alt_24"
tools:text="Yours (RSSI -61)" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/rawdata_label"
@@ -72,7 +87,7 @@
<com.google.android.material.textview.MaterialTextView
android:id="@+id/rawdata"
style="@style/TextAppearance.MaterialComponents.Body1"
style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
+4 -1
View File
@@ -54,7 +54,7 @@
<string name="pods_connection_state_call_label">Call</string>
<string name="pods_connection_state_ringing_label">Ringing</string>
<string name="pods_connection_state_hanging_up_label">Hanging up</string>
<string name="pods_status_x_label">Status %s</string>
<string name="pods_status_x_label">Status: %s</string>
<string name="settings_label">Settings</string>
@@ -82,4 +82,7 @@
<string name="settings_debug_autoreports_label">Automatic bug reports</string>
<string name="settings_debug_autoreports_description">Automatically reports issues, e.g. details on an app crash so I can figure out how to fix it.</string>
<string name="device_unknown_label">Unknown device</string>
<string name="pods_single_headphones_label">Headphones</string>
<string name="settings_debug_mode_label">Debug mode</string>
<string name="settings_debug_mode_description">Show additional information to troubleshoot issues.</string>
</resources>
+7 -1
View File
@@ -5,10 +5,16 @@
<CheckBoxPreference
android:icon="@drawable/ic_spider_thread_onsurface"
android:key="core.bugtracking.enabled"
android:key="debug.bugreport.automatic.enabled"
android:summary="@string/settings_debug_autoreports_description"
android:title="@string/settings_debug_autoreports_label" />
<CheckBoxPreference
android:icon="@drawable/ic_baseline_bug_report_24"
android:key="debug.mode.enabled"
android:summary="@string/settings_debug_mode_description"
android:title="@string/settings_debug_mode_label" />
</PreferenceCategory>
</PreferenceScreen>
@@ -1,6 +1,6 @@
package eu.darken.capod.pods.core.apple
import eu.darken.capod.pods.core.DualPods
import eu.darken.capod.pods.core.DualPodDevice
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
@@ -16,12 +16,12 @@ class AirPodsFactoryTest : BaseAirPodsTest() {
create<DualApplePods>("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
// --^-----
microPhonePod shouldBe DualPods.Pod.LEFT
microPhonePod shouldBe DualPodDevice.Pod.LEFT
}
create<DualApplePods>("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
// --^-----
microPhonePod shouldBe DualPods.Pod.RIGHT
microPhonePod shouldBe DualPodDevice.Pod.RIGHT
}
}
@@ -1,6 +1,6 @@
package eu.darken.capod.pods.core.apple.airpods
import eu.darken.capod.pods.core.DualPods
import eu.darken.capod.pods.core.DualPodDevice
import eu.darken.capod.pods.core.apple.BaseAirPodsTest
import eu.darken.capod.pods.core.apple.DualApplePods
import io.kotest.matchers.shouldBe
@@ -22,7 +22,7 @@ class AirPodsProTest : BaseAirPodsTest() {
rawDeviceColor shouldBe 0x00.toUByte()
rawSuffix shouldBe 0x00.toUByte()
microPhonePod shouldBe DualPods.Pod.RIGHT
microPhonePod shouldBe DualPodDevice.Pod.RIGHT
batteryLeftPodPercent shouldBe 1.0f
batteryRightPodPercent shouldBe 1.0f
@@ -39,7 +39,7 @@ class AirPodsProTest : BaseAirPodsTest() {
@Test
fun `test AirPods from my downstairs neighbour`() = runBlockingTest {
create<AirPodsPro>("07 19 01 0E 20 00 F3 8F 02 00 04 79 C6 3F F9 C3 15 D9 11 A1 3C B1 58 66 B9 8B 67") {
microPhonePod shouldBe DualPods.Pod.RIGHT
microPhonePod shouldBe DualPodDevice.Pod.RIGHT
batteryLeftPodPercent shouldBe null
batteryRightPodPercent shouldBe 0.3f
@@ -1,6 +1,6 @@
package eu.darken.capod.pods.core.apple.beats
import eu.darken.capod.pods.core.DualPods
import eu.darken.capod.pods.core.DualPodDevice
import eu.darken.capod.pods.core.apple.BaseAirPodsTest
import eu.darken.capod.pods.core.apple.DualApplePods
import io.kotest.matchers.shouldBe
@@ -22,7 +22,7 @@ class PowerBeatsProTest : BaseAirPodsTest() {
rawDeviceColor shouldBe 0x00.toUByte()
rawSuffix shouldBe 0x00.toUByte()
microPhonePod shouldBe DualPods.Pod.RIGHT
microPhonePod shouldBe DualPodDevice.Pod.RIGHT
batteryLeftPodPercent shouldBe 1.0f
batteryRightPodPercent shouldBe 1.0f