mirror of
https://github.com/Boof2015/astra-mobile.git
synced 2026-08-18 19:54:26 +02:00
better eq profile assigning
This commit is contained in:
+12
-24
@@ -131,7 +131,7 @@ class AstraAudioRouteModule : Module() {
|
||||
val device = selectOutputDevice()
|
||||
val kind = device?.let { kindForType(it.type) } ?: "unknown"
|
||||
val label = displayLabel(kind, device, selectedRouteName)
|
||||
val key = routeKey(kind, label)
|
||||
val key = buildOutputRouteKey(kind, label, deviceAddress(device))
|
||||
|
||||
return mapOf(
|
||||
"key" to key,
|
||||
@@ -214,6 +214,17 @@ class AstraAudioRouteModule : Module() {
|
||||
return routeName ?: deviceName ?: defaultLabel(kind)
|
||||
}
|
||||
|
||||
private fun deviceAddress(device: AudioDeviceInfo?): String? =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
try {
|
||||
device?.address?.trim()?.ifEmpty { null }
|
||||
} catch (_: Throwable) {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
private fun defaultLabel(kind: String): String =
|
||||
when (kind) {
|
||||
"speaker" -> "Phone speaker"
|
||||
@@ -244,27 +255,4 @@ class AstraAudioRouteModule : Module() {
|
||||
return kind == "bluetooth" || kind == "usb" || kind == "hdmi"
|
||||
}
|
||||
|
||||
private fun routeKey(kind: String, label: String): String {
|
||||
if (kind == "bluetooth") {
|
||||
val slug = slug(label)
|
||||
if (slug.isNotEmpty() && slug != "bluetooth" && slug != "bluetooth-audio") {
|
||||
return "bluetooth:$slug"
|
||||
}
|
||||
}
|
||||
return when (kind) {
|
||||
"speaker" -> "speaker"
|
||||
"wired" -> "wired"
|
||||
"bluetooth" -> "bluetooth"
|
||||
"usb" -> "usb"
|
||||
"hdmi" -> "hdmi"
|
||||
else -> "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
private fun slug(value: String): String =
|
||||
value
|
||||
.trim()
|
||||
.lowercase()
|
||||
.replace(Regex("[^a-z0-9]+"), "-")
|
||||
.trim('-')
|
||||
}
|
||||
|
||||
+40
@@ -1,5 +1,7 @@
|
||||
package expo.modules.astraaudioroute
|
||||
|
||||
import java.security.MessageDigest
|
||||
|
||||
/** Prefer Android's media-attribute prediction, then retain the legacy fallback. */
|
||||
internal fun <T> selectPredictedOutputDevice(
|
||||
predicted: List<T>,
|
||||
@@ -15,3 +17,41 @@ internal fun <T> selectPredictedOutputDevice(
|
||||
?: connected.firstOrNull { kindFor(it) == "speaker" }
|
||||
?: connected.first()
|
||||
}
|
||||
|
||||
private fun slug(value: String): String =
|
||||
value
|
||||
.trim()
|
||||
.lowercase()
|
||||
.replace(Regex("[^a-z0-9]+"), "-")
|
||||
.trim('-')
|
||||
|
||||
private fun isGenericExternalLabel(kind: String, label: String): Boolean {
|
||||
val normalized = slug(label)
|
||||
if (normalized.isEmpty()) return true
|
||||
return when (kind) {
|
||||
"bluetooth" -> normalized in setOf("bluetooth", "bluetooth-audio", "headphones", "headset")
|
||||
"usb" -> normalized in setOf("usb", "usb-audio")
|
||||
"hdmi" -> normalized in setOf("hdmi", "hdmi-audio")
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
private fun addressToken(address: String?): String? {
|
||||
val normalized = address?.trim()?.lowercase()?.ifEmpty { null } ?: return null
|
||||
return MessageDigest
|
||||
.getInstance("SHA-256")
|
||||
.digest(normalized.toByteArray(Charsets.UTF_8))
|
||||
.take(8)
|
||||
.joinToString("") { byte -> "%02x".format(byte.toInt() and 0xff) }
|
||||
}
|
||||
|
||||
/** Builds a stable local identity without exposing a raw hardware address to JS/storage. */
|
||||
internal fun buildOutputRouteKey(kind: String, label: String, address: String?): String {
|
||||
if (kind == "speaker") return "speaker"
|
||||
if (kind == "wired") return "wired"
|
||||
if (kind !in setOf("bluetooth", "usb", "hdmi")) return "unknown"
|
||||
|
||||
addressToken(address)?.let { return "$kind:id:$it" }
|
||||
if (!isGenericExternalLabel(kind, label)) return "$kind:name:${slug(label)}"
|
||||
return kind
|
||||
}
|
||||
|
||||
+31
@@ -1,6 +1,8 @@
|
||||
package expo.modules.astraaudioroute
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class OutputDeviceSelectorTest {
|
||||
@@ -34,4 +36,33 @@ class OutputDeviceSelectorTest {
|
||||
|
||||
assertEquals(bluetooth, selected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun externalAddressProducesStablePrivacySafeKey() {
|
||||
val first = buildOutputRouteKey("bluetooth", "Sony WH-1000XM5", "AA:BB:CC:DD:EE:FF")
|
||||
val same = buildOutputRouteKey("bluetooth", "Renamed headphones", "aa:bb:cc:dd:ee:ff")
|
||||
val other = buildOutputRouteKey("bluetooth", "Sony WH-1000XM5", "11:22:33:44:55:66")
|
||||
|
||||
assertTrue(first.startsWith("bluetooth:id:"))
|
||||
assertEquals(first, same)
|
||||
assertNotEquals(first, other)
|
||||
assertTrue(!first.contains("aa:bb"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun namedExternalDevicesFallBackToLabelIdentity() {
|
||||
assertEquals("bluetooth:name:sony-wh-1000xm5", buildOutputRouteKey("bluetooth", "Sony WH-1000XM5", null))
|
||||
assertEquals("usb:name:fiio-k7", buildOutputRouteKey("usb", "FiiO K7", null))
|
||||
assertEquals("hdmi:name:living-room-tv", buildOutputRouteKey("hdmi", "Living Room TV", null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun genericAndBuiltInOutputsKeepClassKeys() {
|
||||
assertEquals("speaker", buildOutputRouteKey("speaker", "Pixel speaker", "internal"))
|
||||
assertEquals("wired", buildOutputRouteKey("wired", "3.5mm", "jack"))
|
||||
assertEquals("bluetooth", buildOutputRouteKey("bluetooth", "Bluetooth audio", null))
|
||||
assertEquals("usb", buildOutputRouteKey("usb", "USB audio", null))
|
||||
assertEquals("hdmi", buildOutputRouteKey("hdmi", "HDMI audio", null))
|
||||
assertEquals("unknown", buildOutputRouteKey("unknown", "Unknown output", null))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user