From 81d07a7795ee0c930dd1acffa81a77b4abac5b46 Mon Sep 17 00:00:00 2001 From: Kavish Devar Date: Fri, 11 Oct 2024 13:24:05 +0530 Subject: [PATCH] implement music play/pause from ear detection --- .../release/baselineProfiles/0/app-release.dm | Bin 0 -> 7736 bytes .../release/baselineProfiles/1/app-release.dm | Bin 0 -> 7656 bytes android/app/release/output-metadata.json | 37 +++++++++ .../java/me/kavishdevar/aln/AirPodsService.kt | 55 ++++++++++++- .../java/me/kavishdevar/aln/MainActivity.kt | 72 +++++++++++++++--- .../me/kavishdevar/aln/MediaController.kt | 36 +++++++++ .../main/java/me/kavishdevar/aln/Packets.kt | 6 +- android/gradle/libs.versions.toml | 8 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- 9 files changed, 196 insertions(+), 20 deletions(-) create mode 100644 android/app/release/baselineProfiles/0/app-release.dm create mode 100644 android/app/release/baselineProfiles/1/app-release.dm create mode 100644 android/app/release/output-metadata.json create mode 100644 android/app/src/main/java/me/kavishdevar/aln/MediaController.kt diff --git a/android/app/release/baselineProfiles/0/app-release.dm b/android/app/release/baselineProfiles/0/app-release.dm new file mode 100644 index 0000000000000000000000000000000000000000..7d4a3cce5276e6418e12d532f25cf95bc903b08c GIT binary patch literal 7736 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 Bw*3GA literal 0 HcmV?d00001 diff --git a/android/app/release/baselineProfiles/1/app-release.dm b/android/app/release/baselineProfiles/1/app-release.dm new file mode 100644 index 0000000000000000000000000000000000000000..9a2b1b23b4fe3cda20bc5a289eada20cffc9b572 GIT binary patch literal 7656 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 Bq$L0V literal 0 HcmV?d00001 diff --git a/android/app/release/output-metadata.json b/android/app/release/output-metadata.json new file mode 100644 index 0000000..490f98c --- /dev/null +++ b/android/app/release/output-metadata.json @@ -0,0 +1,37 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "me.kavishdevar.aln", + "variantName": "release", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 1, + "versionName": "1.0", + "outputFile": "app-release.apk" + } + ], + "elementType": "File", + "baselineProfiles": [ + { + "minApi": 28, + "maxApi": 30, + "baselineProfiles": [ + "baselineProfiles/1/app-release.dm" + ] + }, + { + "minApi": 31, + "maxApi": 2147483647, + "baselineProfiles": [ + "baselineProfiles/0/app-release.dm" + ] + } + ], + "minSdkVersionForDexing": 28 +} \ No newline at end of file diff --git a/android/app/src/main/java/me/kavishdevar/aln/AirPodsService.kt b/android/app/src/main/java/me/kavishdevar/aln/AirPodsService.kt index bc94c8a..336296e 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/AirPodsService.kt +++ b/android/app/src/main/java/me/kavishdevar/aln/AirPodsService.kt @@ -4,16 +4,22 @@ import android.annotation.SuppressLint import android.app.Service import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothSocket +import android.content.BroadcastReceiver +import android.content.Context import android.content.Intent +import android.content.IntentFilter +import android.media.AudioManager import android.os.Binder import android.os.Build import android.os.IBinder import android.os.ParcelUuid import android.util.Log +import androidx.compose.runtime.mutableStateOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.lsposed.hiddenapibypass.HiddenApiBypass +import kotlin.experimental.or class AirPodsService : Service() { inner class LocalBinder : Binder() { @@ -27,6 +33,12 @@ class AirPodsService : Service() { var isRunning: Boolean = false private var socket: BluetoothSocket? = null + fun sendPacket(packet: String) { + val fromHex = packet.split(" ").map { it.toInt(16).toByte() } + socket?.outputStream?.write(fromHex.toByteArray()) + socket?.outputStream?.flush() + } + fun setANCMode(mode: Int) { when (mode) { 1 -> { @@ -50,14 +62,12 @@ class AirPodsService : Service() { } fun setAdaptiveStrength(strength: Int) { - val bytes = byteArrayOf(0x00, 0x04, 0x00, 0x09, 0x00, 0x2E, strength.toByte(), 0x00, 0x00, 0x00) - val hexString = bytes.joinToString(" ") { "%02X".format(it) } - Log.d("AirPodsService", "Adaptive Strength: $hexString") + val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x2E, strength.toByte(), 0x00, 0x00, 0x00) socket?.outputStream?.write(bytes) socket?.outputStream?.flush() } - @SuppressLint("MissingPermission") + @SuppressLint("MissingPermission", "InlinedApi") override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { if (isRunning) { return START_STICKY @@ -107,6 +117,33 @@ class AirPodsService : Service() { putExtra("data", bytes) }) Log.d("AirPods Parser", "Ear Detection: ${earDetectionNotification.status[0]} ${earDetectionNotification.status[1]}") + val audioManager = this@AirPodsService.getSystemService(AUDIO_SERVICE) as AudioManager + val mediaController = MediaController(audioManager) + var inEar = false + val earReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val data = intent.getByteArrayExtra("data") + if (data != null) { + inEar = if (data.find { it == 0x02.toByte() } != null || data.find { it == 0x03.toByte() } != null) { + data[0] == 0x00.toByte() || data[1] == 0x00.toByte() + } else { + data[0] == 0x00.toByte() && data[1] == 0x00.toByte() + } + Log.d("AirPods Parser", "In Ear: $inEar") + if (inEar) { + mediaController.sendPlay() + } + else { + mediaController.sendPause() + } + } + } + } + + val earIntentFilter = IntentFilter(Notifications.EAR_DETECTION_DATA) + this@AirPodsService.registerReceiver(earReceiver, earIntentFilter, + RECEIVER_EXPORTED + ) } else if (ancNotification.isANCData(data)) { ancNotification.setStatus(data) @@ -129,6 +166,16 @@ class AirPodsService : Service() { sendBroadcast(Intent(Notifications.CA_DATA).apply { putExtra("data", conversationAwarenessNotification.status) }) + if (conversationAwarenessNotification.status == 1.toByte() or 2.toByte()) { + val audioManager = this@AirPodsService.getSystemService(AUDIO_SERVICE) as AudioManager + val mediaController = MediaController(audioManager) + mediaController.startSpeaking() + } + else if (conversationAwarenessNotification.status == 9.toByte() or 8.toByte()) { + val audioManager = this@AirPodsService.getSystemService(AUDIO_SERVICE) as AudioManager + val mediaController = MediaController(audioManager) + mediaController.stopSpeaking() + } Log.d("AirPods Parser", "Conversation Awareness: ${conversationAwarenessNotification.status}") } else { } diff --git a/android/app/src/main/java/me/kavishdevar/aln/MainActivity.kt b/android/app/src/main/java/me/kavishdevar/aln/MainActivity.kt index 7d3d2e9..b96992a 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/MainActivity.kt +++ b/android/app/src/main/java/me/kavishdevar/aln/MainActivity.kt @@ -10,6 +10,7 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.ServiceConnection +import android.media.AudioManager import android.os.Build import android.os.Bundle import android.os.IBinder @@ -45,12 +46,14 @@ import androidx.compose.foundation.text.BasicTextField import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Person import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Slider import androidx.compose.material3.SliderDefaults import androidx.compose.material3.Text +import androidx.compose.material3.TextField import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -63,6 +66,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.scale +import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.SolidColor @@ -86,6 +90,7 @@ import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.rememberPermissionState import com.google.accompanist.permissions.shouldShowRationale import me.kavishdevar.aln.ui.theme.ALNTheme +import kotlin.math.roundToInt class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -397,11 +402,15 @@ fun BatteryView() { } else { Row { - Text(text = "\uDBC6\uDCE5", fontFamily = FontFamily(Font(R.font.sf_pro))) - BatteryIndicator(left?.level ?: 0, left?.status == BatteryStatus.CHARGING) - Spacer(modifier = Modifier.width(16.dp)) - Text(text = "\uDBC6\uDCE8", fontFamily = FontFamily(Font(R.font.sf_pro))) - BatteryIndicator(right?.level ?: 0, right?.status == BatteryStatus.CHARGING) + if (left?.status != BatteryStatus.DISCONNECTED) { + Text(text = "\uDBC6\uDCE5", fontFamily = FontFamily(Font(R.font.sf_pro))) + BatteryIndicator(left?.level ?: 0, left?.status == BatteryStatus.CHARGING) + Spacer(modifier = Modifier.width(16.dp)) + } + if (right?.status != BatteryStatus.DISCONNECTED) { + Text(text = "\uDBC6\uDCE8", fontFamily = FontFamily(Font(R.font.sf_pro))) + BatteryIndicator(right?.level ?: 0, right?.status == BatteryStatus.CHARGING) + } } } } @@ -420,7 +429,6 @@ fun BatteryView() { .fillMaxWidth() ) BatteryIndicator(case?.level ?: 0) - } } } @@ -454,6 +462,7 @@ fun AirPodsSettingsScreen(paddingValues: PaddingValues, device: BluetoothDevice? } } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun NoiseControlSlider(service: AirPodsService) { val sliderValue = remember { mutableStateOf(0f) } @@ -475,17 +484,37 @@ fun NoiseControlSlider(service: AirPodsService) { value = sliderValue.value, onValueChange = { sliderValue.value = it - service.setAdaptiveStrength(it.toInt()) + service.setAdaptiveStrength(100 - it.toInt()) }, valueRange = 0f..100f, - steps = 3, + onValueChangeFinished = { + // Round the value when the user stops sliding + sliderValue.value = sliderValue.value.roundToInt().toFloat() + }, modifier = Modifier - .fillMaxWidth(), + .fillMaxWidth() + .height(36.dp), // Adjust height to ensure thumb fits well colors = SliderDefaults.colors( thumbColor = thumbColor, activeTrackColor = activeTrackColor, inactiveTrackColor = trackColor - ) + ), + thumb = { + Box( + modifier = Modifier + .size(24.dp) // Circular thumb size + .shadow(4.dp, CircleShape) // Apply shadow to the thumb + .background(thumbColor, CircleShape) // Circular thumb + ) + }, + track = { + Box( + modifier = Modifier + .fillMaxWidth() + .height(12.dp) + .background(trackColor, RoundedCornerShape(6.dp)) + ) + } ) // Labels @@ -599,7 +628,30 @@ fun AudioSettings(service: AirPodsService) { color = textColor.copy(alpha = 0.6f) ) ) + NoiseControlSlider(service = service) + val packet = remember { mutableStateOf ("") } + + Row ( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp) + ) { + TextField( + value = packet.value, + onValueChange = { packet.value = it }, + modifier = Modifier.fillMaxWidth(0.75f), + ) + Button(onClick = { + service.sendPacket(packet.value) + }, + modifier = Modifier + .padding(start = 8.dp) + .fillMaxWidth() + ) { + Text(text = "Send") + } + } } } } diff --git a/android/app/src/main/java/me/kavishdevar/aln/MediaController.kt b/android/app/src/main/java/me/kavishdevar/aln/MediaController.kt new file mode 100644 index 0000000..3d5e53b --- /dev/null +++ b/android/app/src/main/java/me/kavishdevar/aln/MediaController.kt @@ -0,0 +1,36 @@ +package me.kavishdevar.aln + +import android.media.AudioManager +import android.view.KeyEvent + +class MediaController (private val audioManager: AudioManager){ + fun sendPause() { + if (audioManager.isMusicActive) { + audioManager.dispatchMediaKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PAUSE)) + audioManager.dispatchMediaKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PAUSE)) + } + } + + fun sendPlay() { + if (!audioManager.isMusicActive) { + audioManager.dispatchMediaKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY)) + audioManager.dispatchMediaKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY)) + } + } + var initialVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) + fun startSpeaking() { + if (!audioManager.isMusicActive) { + // reduce volume to 10% of initial volume + initialVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) + audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (initialVolume * 0.1).toInt(), 0) + } + } + + fun stopSpeaking() { + if (!audioManager.isMusicActive) { + // restore initial volume + audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, initialVolume, 0) + } + + } +} \ No newline at end of file diff --git a/android/app/src/main/java/me/kavishdevar/aln/Packets.kt b/android/app/src/main/java/me/kavishdevar/aln/Packets.kt index a1f4978..b6ba1a4 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/Packets.kt +++ b/android/app/src/main/java/me/kavishdevar/aln/Packets.kt @@ -135,7 +135,11 @@ class Notifications { fun setBattery(data: ByteArray) { first = Battery(data[7].toInt(), data[9].toInt(), data[10].toInt()) second = Battery(data[12].toInt(), data[14].toInt(), data[15].toInt()) - case = Battery(data[17].toInt(), data[19].toInt(), data[20].toInt()) + case = if (data[20].toInt() == BatteryStatus.DISCONNECTED && case.status != BatteryStatus.DISCONNECTED) { + Battery(data[17].toInt(), case.level, data[20].toInt()) + } else { + Battery(data[17].toInt(), data[19].toInt(), data[20].toInt()) + } } fun getBattery(): List { diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 1efa466..c47be30 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -1,16 +1,16 @@ [versions] accompanistPermissions = "0.36.0" -agp = "8.7.0-rc01" +agp = "8.7.0" hiddenapibypass = "4.3" kotlin = "2.0.0" coreKtx = "1.13.1" junit = "4.13.2" junitVersion = "1.2.1" espressoCore = "3.6.1" -lifecycleRuntimeKtx = "2.8.5" +lifecycleRuntimeKtx = "2.8.6" activityCompose = "1.9.2" -composeBom = "2024.04.01" -annotations = "15.0" +composeBom = "2024.09.03" +annotations = "26.0.0" [libraries] accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index e901423..b4dd5bf 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Oct 07 22:30:36 IST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists