From 2679205dc3ab8642a0249880b87b037bb8cee335 Mon Sep 17 00:00:00 2001 From: Kavish Devar Date: Fri, 6 Dec 2024 11:56:07 +0530 Subject: [PATCH] fix background connections, add manual force connection button --- android/app/src/main/AndroidManifest.xml | 19 +- .../me/kavishdevar/aln/AirPodsQSService.kt | 25 +- .../java/me/kavishdevar/aln/AirPodsService.kt | 45 +- .../kavishdevar/aln/AirPodsSettingsScreen.kt | 87 +- .../java/me/kavishdevar/aln/BootReceiver.kt | 12 +- .../java/me/kavishdevar/aln/MainActivity.kt | 17 +- .../me/kavishdevar/aln/OldAirPodsService.kt | 926 +++++++++--------- android/app/src/main/res/values/strings.xml | 3 +- 8 files changed, 597 insertions(+), 537 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 8494567..3ebe1af 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -35,8 +35,6 @@ android:label="@string/title_activity_custom_device" android:theme="@style/Theme.ALN"> - - @@ -46,17 +44,10 @@ android:theme="@style/Theme.ALN"> - - + + + + + + \ No newline at end of file diff --git a/android/app/src/main/java/me/kavishdevar/aln/AirPodsQSService.kt b/android/app/src/main/java/me/kavishdevar/aln/AirPodsQSService.kt index 4cc67af..6dc586d 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/AirPodsQSService.kt +++ b/android/app/src/main/java/me/kavishdevar/aln/AirPodsQSService.kt @@ -10,20 +10,17 @@ import android.service.quicksettings.TileService import android.util.Log class AirPodsQSService: TileService() { - private val sharedPreferences = ServiceManager.getService()?.getSharedPreferences("me.kavishdevar.aln", Context.MODE_PRIVATE) - private val offListeningModeEnabled = sharedPreferences?.getBoolean("off_listening_mode", false) == true - private val ancModes = if (offListeningModeEnabled) listOf(NoiseControlMode.OFF.name, NoiseControlMode.NOISE_CANCELLATION.name, NoiseControlMode.TRANSPARENCY.name, NoiseControlMode.ADAPTIVE.name) else listOf(NoiseControlMode.NOISE_CANCELLATION.name, NoiseControlMode.TRANSPARENCY.name, NoiseControlMode.ADAPTIVE.name) - private var currentModeIndex = if (offListeningModeEnabled) 3 else 2 + private val ancModes = listOf(NoiseControlMode.NOISE_CANCELLATION.name, NoiseControlMode.TRANSPARENCY.name, NoiseControlMode.ADAPTIVE.name) + private var currentModeIndex = 2 private lateinit var ancStatusReceiver: BroadcastReceiver private lateinit var availabilityReceiver: BroadcastReceiver @SuppressLint("InlinedApi") override fun onStartListening() { - Log.d("AirPodsQSService", "off mode: $offListeningModeEnabled") super.onStartListening() - currentModeIndex = (ServiceManager.getService()?.getANC()?.minus(if (offListeningModeEnabled) 1 else 2)) ?: if (offListeningModeEnabled) 3 else 2 + currentModeIndex = (ServiceManager.getService()?.getANC()?.minus(1)) ?: -1 if (currentModeIndex == -1) { - currentModeIndex = if (offListeningModeEnabled) 3 else 2 + currentModeIndex = 2 } if (ServiceManager.getService() == null) { @@ -42,7 +39,7 @@ class AirPodsQSService: TileService() { ancStatusReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val ancStatus = intent.getIntExtra("data", 4) - currentModeIndex = ancStatus - if (offListeningModeEnabled) 1 else 2 + currentModeIndex = ancStatus - 1 updateTile() } } @@ -89,10 +86,9 @@ class AirPodsQSService: TileService() { override fun onClick() { super.onClick() Log.d("QuickSettingTileService", "ANC tile clicked") - Log.d("QuickSettingTileService", "Current mode index: $currentModeIndex, ancModes size: ${ancModes.size}") currentModeIndex = (currentModeIndex + 1) % ancModes.size - Log.d("QuickSettingTileService", "New mode index: $currentModeIndex") - switchAncMode(if (offListeningModeEnabled) currentModeIndex + 1 else currentModeIndex + 2) + Log.d("QuickSettingTileService", "New mode index: $currentModeIndex, would be set to ${currentModeIndex + 1}") + switchAncMode() } private fun updateTile() { @@ -102,10 +98,11 @@ class AirPodsQSService: TileService() { qsTile.updateTile() } - private fun switchAncMode(modeIndex: Int) { - currentModeIndex = if (offListeningModeEnabled) modeIndex else modeIndex - 1 + private fun switchAncMode() { val airPodsService = ServiceManager.getService() - airPodsService?.setANCMode(if (offListeningModeEnabled) modeIndex + 1 else modeIndex) + Log.d("QuickSettingTileService", "Setting ANC mode to ${currentModeIndex + 2}") + airPodsService?.setANCMode(currentModeIndex + 2) + Log.d("QuickSettingTileService", "ANC mode set to ${currentModeIndex + 2}") updateTile() } } \ 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 1c6cafe..6426433 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/AirPodsService.kt +++ b/android/app/src/main/java/me/kavishdevar/aln/AirPodsService.kt @@ -23,10 +23,21 @@ import android.widget.RemoteViews import androidx.core.app.NotificationCompat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.lsposed.hiddenapibypass.HiddenApiBypass +object ServiceManager { + private var service: AirPodsService? = null + @Synchronized + fun getService(): AirPodsService? { + return service + } + @Synchronized + fun setService(service: AirPodsService?) { + this.service = service + } +} + @Suppress("unused") class AirPodsService: Service() { inner class LocalBinder : Binder() { @@ -185,6 +196,7 @@ class AirPodsService: Service() { @SuppressLint("InlinedApi", "MissingPermission") override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { Log.d("AirPodsService", "Service started") + ServiceManager.setService(this) startForegroundNotification() registerReceiver(bluetoothReceiver, BluetoothReceiver.buildFilter(), RECEIVER_EXPORTED) @@ -215,7 +227,7 @@ class AirPodsService: Service() { registerReceiver(connectionReceiver, intentFilter, RECEIVER_EXPORTED) val bluetoothAdapter = getSystemService(BluetoothManager::class.java).adapter - bluetoothAdapter.bondedDevices.forEach { device -> + bluetoothAdapter.bondedDevices.forEach { device -> if (device.uuids.contains(ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a"))) { bluetoothAdapter.getProfileProxy(this, object : BluetoothProfile.ServiceListener { override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { @@ -223,6 +235,9 @@ class AirPodsService: Service() { val connectedDevices = proxy.connectedDevices if (connectedDevices.isNotEmpty()) { connectToSocket(device) + this@AirPodsService.sendBroadcast( + Intent(AirPodsNotifications.AIRPODS_CONNECTED) + ) } } bluetoothAdapter.closeProfileProxy(profile, proxy) @@ -243,6 +258,12 @@ class AirPodsService: Service() { HiddenApiBypass.addHiddenApiExemptions("Landroid/bluetooth/BluetoothSocket;") val uuid: ParcelUuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a") + try { + socket.close() + } catch (e: Exception) { + e.printStackTrace() + } + try { socket = HiddenApiBypass.newInstance( BluetoothSocket::class.java, @@ -282,21 +303,17 @@ class AirPodsService: Service() { this@AirPodsService.device = device isConnected = true socket.let { it -> - CoroutineScope(Dispatchers.IO).launch { - it.outputStream.write(Enums.HANDSHAKE.value) - it.outputStream.flush() - delay(500) - it.outputStream.write(Enums.SET_SPECIFIC_FEATURES.value) - it.outputStream.flush() - delay(500) - it.outputStream.write(Enums.REQUEST_NOTIFICATIONS.value) - it.outputStream.flush() - } - + it.outputStream.write(Enums.HANDSHAKE.value) + it.outputStream.flush() + it.outputStream.write(Enums.SET_SPECIFIC_FEATURES.value) + it.outputStream.flush() + it.outputStream.write(Enums.REQUEST_NOTIFICATIONS.value) + it.outputStream.flush() sendBroadcast( Intent(AirPodsNotifications.AIRPODS_CONNECTED) .putExtra("device", device) ) + CoroutineScope(Dispatchers.IO).launch { while (socket.isConnected == true) { socket.let { @@ -316,7 +333,7 @@ class AirPodsService: Service() { } else if (bytesRead == -1) { Log.d("AirPods Service", "Socket closed (bytesRead = -1)") - socket.close() +// socket.close() sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED)) return@launch } diff --git a/android/app/src/main/java/me/kavishdevar/aln/AirPodsSettingsScreen.kt b/android/app/src/main/java/me/kavishdevar/aln/AirPodsSettingsScreen.kt index f3a901a..f7ab83a 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/AirPodsSettingsScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/aln/AirPodsSettingsScreen.kt @@ -2,6 +2,8 @@ package me.kavishdevar.aln import android.annotation.SuppressLint import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothManager +import android.bluetooth.BluetoothProfile import android.content.BroadcastReceiver import android.content.Context import android.content.Context.MODE_PRIVATE @@ -9,6 +11,8 @@ import android.content.Intent import android.content.IntentFilter import android.content.SharedPreferences import android.os.Build +import android.os.ParcelUuid +import android.util.Log import androidx.annotation.RequiresApi import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.Image @@ -37,6 +41,7 @@ import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.KeyboardArrowRight +import androidx.compose.material.icons.filled.Refresh import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -84,6 +89,7 @@ import androidx.compose.ui.unit.sp import androidx.navigation.NavController import com.primex.core.ExperimentalToolkitApi import com.primex.core.blur.newBackgroundBlur +import me.kavishdevar.aln.AirPodsService import kotlin.math.roundToInt @@ -157,38 +163,19 @@ fun BatteryView(service: AirPodsService, preview: Boolean = false) { horizontalArrangement = Arrangement.Center ) { if (left?.status != BatteryStatus.DISCONNECTED) { - Row ( - horizontalArrangement = Arrangement.SpaceEvenly, - modifier = Modifier - .weight(1f) - ) { - Text( - text = "\uDBC6\uDCE5", - fontFamily = FontFamily(Font(R.font.sf_pro)), - ) - BatteryIndicator( - left?.level ?: 0, - left?.status == BatteryStatus.CHARGING - ) - } + BatteryIndicator( + left?.level ?: 0, + left?.status == BatteryStatus.CHARGING + ) + } + if (left?.status != BatteryStatus.DISCONNECTED && right?.status != BatteryStatus.DISCONNECTED) { + Spacer(modifier = Modifier.width(16.dp)) } if (right?.status != BatteryStatus.DISCONNECTED) { - Row ( - horizontalArrangement = Arrangement.Center, - modifier = Modifier - .weight(1f) - ) { - Text( - text = "\uDBC6\uDCE8", - fontFamily = FontFamily(Font(R.font.sf_pro)), - modifier = Modifier - .fillMaxWidth(0.5f) - ) - BatteryIndicator( - right?.level ?: 0, - right?.status == BatteryStatus.CHARGING - ) - } + BatteryIndicator( + right?.level ?: 0, + right?.status == BatteryStatus.CHARGING + ) } } } @@ -556,7 +543,41 @@ fun AirPodsSettingsScreen(device: BluetoothDevice?, service: AirPodsService, }, colors = TopAppBarDefaults.centerAlignedTopAppBarColors( containerColor = if (MaterialTheme.colorScheme.surface.luminance() < 0.5) Color.Black.copy(0.3f) else Color(0xFFF2F2F7).copy(0.2f), - ) + ), + actions = { + val context = LocalContext.current + IconButton( + onClick = { + val bluetoothAdapter = context.getSystemService(BluetoothManager::class.java).adapter + bluetoothAdapter.bondedDevices.forEach { device -> + if (device.uuids.contains(ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a"))) { + bluetoothAdapter.getProfileProxy(context, object : BluetoothProfile.ServiceListener { + override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { + if (profile == BluetoothProfile.A2DP) { + val connectedDevices = proxy.connectedDevices + if (connectedDevices.isNotEmpty()) { + service.connectToSocket(device) + } + } + bluetoothAdapter.closeProfileProxy(profile, proxy) + } + + override fun onServiceDisconnected(profile: Int) { } + }, BluetoothProfile.A2DP) + } + } + }, + colors = IconButtonDefaults.iconButtonColors( + containerColor = Color.Transparent, + contentColor = if (MaterialTheme.colorScheme.surface.luminance() < 0.5) Color.White else Color.Black + ) + ) { + Icon( + imageVector = Icons.Default.Refresh, + contentDescription = "Settings", + ) + } + } ) } ) { paddingValues -> @@ -1467,8 +1488,8 @@ fun BatteryIndicator(batteryPercentage: Int, charging: Boolean = false) { val batteryWidth = 40.dp val batteryHeight = 15.dp val batteryCornerRadius = 4.dp - val tipWidth = 4.dp - val tipHeight = batteryHeight * 0.3f + val tipWidth = 5.dp + val tipHeight = batteryHeight * 0.375f Column( horizontalAlignment = Alignment.CenterHorizontally diff --git a/android/app/src/main/java/me/kavishdevar/aln/BootReceiver.kt b/android/app/src/main/java/me/kavishdevar/aln/BootReceiver.kt index 38c4f34..df514ac 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/BootReceiver.kt +++ b/android/app/src/main/java/me/kavishdevar/aln/BootReceiver.kt @@ -1,4 +1,14 @@ package me.kavishdevar.aln -class BootReceiver { +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent + +class BootReceiver: BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + when (intent?.action) { + Intent.ACTION_MY_PACKAGE_REPLACED -> try { context?.startForegroundService(Intent(context, AirPodsService::class.java)) } catch (e: Exception) { e.printStackTrace() } + Intent.ACTION_BOOT_COMPLETED -> try { context?.startForegroundService(Intent(context, AirPodsService::class.java)) } catch (e: Exception) { e.printStackTrace() } + } + } } \ No newline at end of file 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 3d024ea..36fd3fd 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/MainActivity.kt +++ b/android/app/src/main/java/me/kavishdevar/aln/MainActivity.kt @@ -70,6 +70,22 @@ class MainActivity : ComponentActivity() { } super.onDestroy() } + + override fun onStop() { + try { + unbindService(serviceConnection) + Log.d("MainActivity", "Unbound service") + } catch (e: Exception) { + Log.e("MainActivity", "Error while unbinding service: $e") + } + try { + unregisterReceiver(connectionStatusReceiver) + Log.d("MainActivity", "Unregistered receiver") + } catch (e: Exception) { + Log.e("MainActivity", "Error while unregistering receiver: $e") + } + super.onStop() + } } @SuppressLint("MissingPermission", "InlinedApi") @@ -86,7 +102,6 @@ fun Main() { val airPodsService = remember { mutableStateOf(null) } if (permissionState.allPermissionsGranted) { - Log.d("MainActivity", "HIIIIIIIIIIIIIII") val context = LocalContext.current val navController = rememberNavController() diff --git a/android/app/src/main/java/me/kavishdevar/aln/OldAirPodsService.kt b/android/app/src/main/java/me/kavishdevar/aln/OldAirPodsService.kt index deb8be0..6605f1c 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/OldAirPodsService.kt +++ b/android/app/src/main/java/me/kavishdevar/aln/OldAirPodsService.kt @@ -1,463 +1,463 @@ -@file:Suppress("unused") - -package me.kavishdevar.aln - -import android.annotation.SuppressLint -import android.app.Notification -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.Service -import android.bluetooth.BluetoothDevice -import android.bluetooth.BluetoothManager -import android.bluetooth.BluetoothProfile -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.core.app.NotificationCompat -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import org.lsposed.hiddenapibypass.HiddenApiBypass - -object ServiceManager { - private var service: OldAirPodsService? = null - @Synchronized - fun getService(): OldAirPodsService? { - return service - } - @Synchronized - fun setService(service: OldAirPodsService?) { - this.service = service - } -} - -class OldAirPodsService : Service() { - inner class LocalBinder : Binder() { - fun getService(): OldAirPodsService = this@OldAirPodsService - } - - override fun onBind(intent: Intent?): IBinder { - return LocalBinder() - } - - var isConnected: 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 -> { - socket?.outputStream?.write(Enums.NOISE_CANCELLATION_OFF.value) - } - 2 -> { - socket?.outputStream?.write(Enums.NOISE_CANCELLATION_ON.value) - } - 3 -> { - socket?.outputStream?.write(Enums.NOISE_CANCELLATION_TRANSPARENCY.value) - } - 4 -> { - socket?.outputStream?.write(Enums.NOISE_CANCELLATION_ADAPTIVE.value) - } - } - socket?.outputStream?.flush() - } - - fun setCAEnabled(enabled: Boolean) { - socket?.outputStream?.write(if (enabled) Enums.SET_CONVERSATION_AWARENESS_ON.value else Enums.SET_CONVERSATION_AWARENESS_OFF.value) - } - - fun setOffListeningMode(enabled: Boolean) { - socket?.outputStream?.write(byteArrayOf(0x04, 0x00 ,0x04, 0x00, 0x09, 0x00, 0x34, if (enabled) 0x01 else 0x02, 0x00, 0x00, 0x00)) - } - - fun setAdaptiveStrength(strength: Int) { - val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x2E, strength.toByte(), 0x00, 0x00, 0x00) - socket?.outputStream?.write(bytes) - socket?.outputStream?.flush() - } - - fun setPressSpeed(speed: Int) { - val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x17, speed.toByte(), 0x00, 0x00, 0x00) - socket?.outputStream?.write(bytes) - socket?.outputStream?.flush() - } - - fun setPressAndHoldDuration(speed: Int) { - val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x18, speed.toByte(), 0x00, 0x00, 0x00) - socket?.outputStream?.write(bytes) - socket?.outputStream?.flush() - } - - fun setNoiseCancellationWithOnePod(enabled: Boolean) { - val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x1B, if (enabled) 0x01 else 0x02, 0x00, 0x00, 0x00) - socket?.outputStream?.write(bytes) - socket?.outputStream?.flush() - } - - fun setVolumeControl(enabled: Boolean) { - val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x25, if (enabled) 0x01 else 0x02, 0x00, 0x00, 0x00) - socket?.outputStream?.write(bytes) - socket?.outputStream?.flush() - } - - fun setVolumeSwipeSpeed(speed: Int) { - val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x23, speed.toByte(), 0x00, 0x00, 0x00) - socket?.outputStream?.write(bytes) - socket?.outputStream?.flush() - } - - fun setToneVolume(volume: Int) { - val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x1F, volume.toByte(), 0x50, 0x00, 0x00) - socket?.outputStream?.write(bytes) - socket?.outputStream?.flush() - } - - val earDetectionNotification = AirPodsNotifications.EarDetection() - val ancNotification = AirPodsNotifications.ANC() - val batteryNotification = AirPodsNotifications.BatteryNotification() - val conversationAwarenessNotification = AirPodsNotifications.ConversationalAwarenessNotification() - - var earDetectionEnabled = true - - fun setCaseChargingSounds(enabled: Boolean) { - val bytes = byteArrayOf(0x12, 0x3a, 0x00, 0x01, 0x00, 0x08, if (enabled) 0x00 else 0x01) - socket?.outputStream?.write(bytes) - socket?.outputStream?.flush() - } - - fun setEarDetection(enabled: Boolean) { - earDetectionEnabled = enabled - } - - fun getBattery(): List { - return batteryNotification.getBattery() - } - - fun getANC(): Int { - return ancNotification.status - } - - private fun createNotification(): Notification { - val channelId = "battery" - val notificationBuilder = NotificationCompat.Builder(this, channelId) - .setSmallIcon(R.drawable.pro_2_buds) - .setContentTitle("AirPods Connected") - .setOngoing(true) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - - val channel = - NotificationChannel(channelId, "Battery Notification", NotificationManager.IMPORTANCE_LOW) - - val notificationManager = getSystemService(NotificationManager::class.java) - notificationManager.createNotificationChannel(channel) - return notificationBuilder.build() - } - - fun disconnectAudio(context: Context, device: BluetoothDevice?) { - val bluetoothAdapter = context.getSystemService(BluetoothManager::class.java).adapter - - bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener { - override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { - if (profile == BluetoothProfile.A2DP) { - try { - val method = proxy.javaClass.getMethod("disconnect", BluetoothDevice::class.java) - method.invoke(proxy, device) - } catch (e: Exception) { - e.printStackTrace() - } finally { - bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, proxy) - } - } - } - - override fun onServiceDisconnected(profile: Int) { } - }, BluetoothProfile.A2DP) - - bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener { - override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { - if (profile == BluetoothProfile.HEADSET) { - try { - val method = proxy.javaClass.getMethod("disconnect", BluetoothDevice::class.java) - method.invoke(proxy, device) - } catch (e: Exception) { - e.printStackTrace() - } finally { - bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy) - } - } - } - - override fun onServiceDisconnected(profile: Int) { } - }, BluetoothProfile.HEADSET) - } - - fun connectAudio(context: Context, device: BluetoothDevice?) { - val bluetoothAdapter = context.getSystemService(BluetoothManager::class.java).adapter - - bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener { - override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { - if (profile == BluetoothProfile.A2DP) { - try { - val method = proxy.javaClass.getMethod("connect", BluetoothDevice::class.java) - method.invoke(proxy, device) - } catch (e: Exception) { - e.printStackTrace() - } finally { - bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, proxy) - } - } - } - - override fun onServiceDisconnected(profile: Int) { } - }, BluetoothProfile.A2DP) - - bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener { - override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { - if (profile == BluetoothProfile.HEADSET) { - try { - val method = proxy.javaClass.getMethod("connect", BluetoothDevice::class.java) - method.invoke(proxy, device) - } catch (e: Exception) { - e.printStackTrace() - } finally { - bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy) - } - } - } - - override fun onServiceDisconnected(profile: Int) { } - }, BluetoothProfile.HEADSET) - } - - fun setName(name: String) { - val nameBytes = name.toByteArray() - val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x01, - nameBytes.size.toByte(), 0x00) + nameBytes - socket?.outputStream?.write(bytes) - socket?.outputStream?.flush() - val hex = bytes.joinToString(" ") { "%02X".format(it) } - Log.d("OldAirPodsService", "setName: $name, sent packet: $hex") - } - - @SuppressLint("MissingPermission", "InlinedApi") - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - - val notification = createNotification() - startForeground(1, notification) - - ServiceManager.setService(this) - - if (isConnected) { - return START_STICKY - } - isConnected = true - - @Suppress("DEPRECATION") val device = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) intent?.getParcelableExtra("device", BluetoothDevice::class.java) else intent?.getParcelableExtra("device") - - HiddenApiBypass.addHiddenApiExemptions("Landroid/bluetooth/BluetoothSocket;") - val uuid: ParcelUuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a") - - socket = HiddenApiBypass.newInstance(BluetoothSocket::class.java, 3, true, true, device, 0x1001, uuid) as BluetoothSocket? - try { - socket?.connect() - socket?.let { it -> - it.outputStream.write(Enums.HANDSHAKE.value) - it.outputStream.write(Enums.SET_SPECIFIC_FEATURES.value) - it.outputStream.write(Enums.REQUEST_NOTIFICATIONS.value) - sendBroadcast(Intent(AirPodsNotifications.AIRPODS_CONNECTED)) - it.outputStream.flush() - - CoroutineScope(Dispatchers.IO).launch { - while (socket?.isConnected == true) { - socket?.let { - val audioManager = this@OldAirPodsService.getSystemService(AUDIO_SERVICE) as AudioManager - MediaController.initialize(audioManager) - val buffer = ByteArray(1024) - val bytesRead = it.inputStream.read(buffer) - var data: ByteArray = byteArrayOf() - if (bytesRead > 0) { - data = buffer.copyOfRange(0, bytesRead) - sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DATA).apply { - putExtra("data", buffer.copyOfRange(0, bytesRead)) - }) - val bytes = buffer.copyOfRange(0, bytesRead) - val formattedHex = bytes.joinToString(" ") { "%02X".format(it) } - Log.d("AirPods Data", "Data received: $formattedHex") - } - else if (bytesRead == -1) { - Log.d("AirPods Service", "Socket closed (bytesRead = -1)") - this@OldAirPodsService.stopForeground(STOP_FOREGROUND_REMOVE) - socket?.close() - sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED)) - return@launch - } - var inEar = false - var inEarData = listOf() - if (earDetectionNotification.isEarDetectionData(data)) { - earDetectionNotification.setStatus(data) - sendBroadcast(Intent(AirPodsNotifications.EAR_DETECTION_DATA).apply { - val list = earDetectionNotification.status - val bytes = ByteArray(2) - bytes[0] = list[0] - bytes[1] = list[1] - putExtra("data", bytes) - }) - Log.d("AirPods Parser", "Ear Detection: ${earDetectionNotification.status[0]} ${earDetectionNotification.status[1]}") - var justEnabledA2dp = false - val earReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - val data = intent.getByteArrayExtra("data") - if (data != null && earDetectionEnabled) { - 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() - } - - val newInEarData = listOf(data[0] == 0x00.toByte(), data[1] == 0x00.toByte()) - if (newInEarData.contains(true) && inEarData == listOf(false, false)) { - connectAudio(this@OldAirPodsService, device) - justEnabledA2dp = true - val bluetoothAdapter = this@OldAirPodsService.getSystemService(BluetoothManager::class.java).adapter - bluetoothAdapter.getProfileProxy( - this@OldAirPodsService, object : BluetoothProfile.ServiceListener { - override fun onServiceConnected( - profile: Int, - proxy: BluetoothProfile - ) { - if (profile == BluetoothProfile.A2DP) { - val connectedDevices = - proxy.connectedDevices - if (connectedDevices.isNotEmpty()) { - MediaController.sendPlay() - } - } - bluetoothAdapter.closeProfileProxy( - profile, - proxy - ) - } - - override fun onServiceDisconnected( - profile: Int - ) { - } - } - ,BluetoothProfile.A2DP - ) - - } - else if (newInEarData == listOf(false, false)){ - disconnectAudio(this@OldAirPodsService, device) - } - - inEarData = newInEarData - - if (inEar == true) { - if (!justEnabledA2dp) { - justEnabledA2dp = false - MediaController.sendPlay() - } - } else { - MediaController.sendPause() - } - } - } - } - - val earIntentFilter = IntentFilter(AirPodsNotifications.EAR_DETECTION_DATA) - this@OldAirPodsService.registerReceiver(earReceiver, earIntentFilter, - RECEIVER_EXPORTED - ) - } - else if (ancNotification.isANCData(data)) { - ancNotification.setStatus(data) - sendBroadcast(Intent(AirPodsNotifications.ANC_DATA).apply { - putExtra("data", ancNotification.status) - }) - Log.d("AirPods Parser", "ANC: ${ancNotification.status}") - } - else if (batteryNotification.isBatteryData(data)) { - batteryNotification.setBattery(data) - sendBroadcast(Intent(AirPodsNotifications.BATTERY_DATA).apply { - putParcelableArrayListExtra("data", ArrayList(batteryNotification.getBattery())) - }) - for (battery in batteryNotification.getBattery()) { - Log.d("AirPods Parser", "${battery.getComponentName()}: ${battery.getStatusName()} at ${battery.level}% ") - } -// if both are charging, disconnect audio profiles - if (batteryNotification.getBattery()[0].status == 1 && batteryNotification.getBattery()[1].status == 1) { - disconnectAudio(this@OldAirPodsService, device) - } - else { - connectAudio(this@OldAirPodsService, device) - } -// updatePodsStatus(device!!, batteryNotification.getBattery()) - } - else if (conversationAwarenessNotification.isConversationalAwarenessData(data)) { - conversationAwarenessNotification.setData(data) - sendBroadcast(Intent(AirPodsNotifications.CA_DATA).apply { - putExtra("data", conversationAwarenessNotification.status) - }) - - - if (conversationAwarenessNotification.status == 1.toByte() || conversationAwarenessNotification.status == 2.toByte()) { - MediaController.startSpeaking() - } else if (conversationAwarenessNotification.status == 8.toByte() || conversationAwarenessNotification.status == 9.toByte()) { - MediaController.stopSpeaking() - } - - Log.d("AirPods Parser", "Conversation Awareness: ${conversationAwarenessNotification.status}") - } - else { } - } - } - Log.d("AirPods Service", "Socket closed") - isConnected = false - this@OldAirPodsService.stopForeground(STOP_FOREGROUND_REMOVE) - socket?.close() - sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED)) - } - } - } - catch (e: Exception) { - Log.e("AirPodsSettingsScreen", "Error connecting to device: ${e.message}") - } - return START_STICKY - } - - override fun onDestroy() { - super.onDestroy() - socket?.close() - isConnected = false - ServiceManager.setService(null) - } - - fun setPVEnabled(enabled: Boolean) { - var hex = "04 00 04 00 09 00 26 ${if (enabled) "01" else "02"} 00 00 00" - var bytes = hex.split(" ").map { it.toInt(16).toByte() }.toByteArray() - socket?.outputStream?.write(bytes) - hex = "04 00 04 00 17 00 00 00 10 00 12 00 08 E${if (enabled) "6" else "5"} 05 10 02 42 0B 08 50 10 02 1A 05 02 ${if (enabled) "32" else "00"} 00 00 00" - bytes = hex.split(" ").map { it.toInt(16).toByte() }.toByteArray() - socket?.outputStream?.write(bytes) - } - - fun setLoudSoundReduction(enabled: Boolean) { - val hex = "52 1B 00 0${if (enabled) "1" else "0"}" - val bytes = hex.split(" ").map { it.toInt(16).toByte() }.toByteArray() - socket?.outputStream?.write(bytes) - } -} \ No newline at end of file +//@file:Suppress("unused") +// +//package me.kavishdevar.aln +// +//import android.annotation.SuppressLint +//import android.app.Notification +//import android.app.NotificationChannel +//import android.app.NotificationManager +//import android.app.Service +//import android.bluetooth.BluetoothDevice +//import android.bluetooth.BluetoothManager +//import android.bluetooth.BluetoothProfile +//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.core.app.NotificationCompat +//import kotlinx.coroutines.CoroutineScope +//import kotlinx.coroutines.Dispatchers +//import kotlinx.coroutines.launch +//import org.lsposed.hiddenapibypass.HiddenApiBypass +// +//object ServiceManager { +// private var service: OldAirPodsService? = null +// @Synchronized +// fun getService(): OldAirPodsService? { +// return service +// } +// @Synchronized +// fun setService(service: OldAirPodsService?) { +// this.service = service +// } +//} +// +//class OldAirPodsService : Service() { +// inner class LocalBinder : Binder() { +// fun getService(): OldAirPodsService = this@OldAirPodsService +// } +// +// override fun onBind(intent: Intent?): IBinder { +// return LocalBinder() +// } +// +// var isConnected: 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 -> { +// socket?.outputStream?.write(Enums.NOISE_CANCELLATION_OFF.value) +// } +// 2 -> { +// socket?.outputStream?.write(Enums.NOISE_CANCELLATION_ON.value) +// } +// 3 -> { +// socket?.outputStream?.write(Enums.NOISE_CANCELLATION_TRANSPARENCY.value) +// } +// 4 -> { +// socket?.outputStream?.write(Enums.NOISE_CANCELLATION_ADAPTIVE.value) +// } +// } +// socket?.outputStream?.flush() +// } +// +// fun setCAEnabled(enabled: Boolean) { +// socket?.outputStream?.write(if (enabled) Enums.SET_CONVERSATION_AWARENESS_ON.value else Enums.SET_CONVERSATION_AWARENESS_OFF.value) +// } +// +// fun setOffListeningMode(enabled: Boolean) { +// socket?.outputStream?.write(byteArrayOf(0x04, 0x00 ,0x04, 0x00, 0x09, 0x00, 0x34, if (enabled) 0x01 else 0x02, 0x00, 0x00, 0x00)) +// } +// +// fun setAdaptiveStrength(strength: Int) { +// val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x2E, strength.toByte(), 0x00, 0x00, 0x00) +// socket?.outputStream?.write(bytes) +// socket?.outputStream?.flush() +// } +// +// fun setPressSpeed(speed: Int) { +// val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x17, speed.toByte(), 0x00, 0x00, 0x00) +// socket?.outputStream?.write(bytes) +// socket?.outputStream?.flush() +// } +// +// fun setPressAndHoldDuration(speed: Int) { +// val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x18, speed.toByte(), 0x00, 0x00, 0x00) +// socket?.outputStream?.write(bytes) +// socket?.outputStream?.flush() +// } +// +// fun setNoiseCancellationWithOnePod(enabled: Boolean) { +// val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x1B, if (enabled) 0x01 else 0x02, 0x00, 0x00, 0x00) +// socket?.outputStream?.write(bytes) +// socket?.outputStream?.flush() +// } +// +// fun setVolumeControl(enabled: Boolean) { +// val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x25, if (enabled) 0x01 else 0x02, 0x00, 0x00, 0x00) +// socket?.outputStream?.write(bytes) +// socket?.outputStream?.flush() +// } +// +// fun setVolumeSwipeSpeed(speed: Int) { +// val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x23, speed.toByte(), 0x00, 0x00, 0x00) +// socket?.outputStream?.write(bytes) +// socket?.outputStream?.flush() +// } +// +// fun setToneVolume(volume: Int) { +// val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x1F, volume.toByte(), 0x50, 0x00, 0x00) +// socket?.outputStream?.write(bytes) +// socket?.outputStream?.flush() +// } +// +// val earDetectionNotification = AirPodsNotifications.EarDetection() +// val ancNotification = AirPodsNotifications.ANC() +// val batteryNotification = AirPodsNotifications.BatteryNotification() +// val conversationAwarenessNotification = AirPodsNotifications.ConversationalAwarenessNotification() +// +// var earDetectionEnabled = true +// +// fun setCaseChargingSounds(enabled: Boolean) { +// val bytes = byteArrayOf(0x12, 0x3a, 0x00, 0x01, 0x00, 0x08, if (enabled) 0x00 else 0x01) +// socket?.outputStream?.write(bytes) +// socket?.outputStream?.flush() +// } +// +// fun setEarDetection(enabled: Boolean) { +// earDetectionEnabled = enabled +// } +// +// fun getBattery(): List { +// return batteryNotification.getBattery() +// } +// +// fun getANC(): Int { +// return ancNotification.status +// } +// +// private fun createNotification(): Notification { +// val channelId = "battery" +// val notificationBuilder = NotificationCompat.Builder(this, channelId) +// .setSmallIcon(R.drawable.pro_2_buds) +// .setContentTitle("AirPods Connected") +// .setOngoing(true) +// .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) +// +// val channel = +// NotificationChannel(channelId, "Battery Notification", NotificationManager.IMPORTANCE_LOW) +// +// val notificationManager = getSystemService(NotificationManager::class.java) +// notificationManager.createNotificationChannel(channel) +// return notificationBuilder.build() +// } +// +// fun disconnectAudio(context: Context, device: BluetoothDevice?) { +// val bluetoothAdapter = context.getSystemService(BluetoothManager::class.java).adapter +// +// bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener { +// override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { +// if (profile == BluetoothProfile.A2DP) { +// try { +// val method = proxy.javaClass.getMethod("disconnect", BluetoothDevice::class.java) +// method.invoke(proxy, device) +// } catch (e: Exception) { +// e.printStackTrace() +// } finally { +// bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, proxy) +// } +// } +// } +// +// override fun onServiceDisconnected(profile: Int) { } +// }, BluetoothProfile.A2DP) +// +// bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener { +// override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { +// if (profile == BluetoothProfile.HEADSET) { +// try { +// val method = proxy.javaClass.getMethod("disconnect", BluetoothDevice::class.java) +// method.invoke(proxy, device) +// } catch (e: Exception) { +// e.printStackTrace() +// } finally { +// bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy) +// } +// } +// } +// +// override fun onServiceDisconnected(profile: Int) { } +// }, BluetoothProfile.HEADSET) +// } +// +// fun connectAudio(context: Context, device: BluetoothDevice?) { +// val bluetoothAdapter = context.getSystemService(BluetoothManager::class.java).adapter +// +// bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener { +// override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { +// if (profile == BluetoothProfile.A2DP) { +// try { +// val method = proxy.javaClass.getMethod("connect", BluetoothDevice::class.java) +// method.invoke(proxy, device) +// } catch (e: Exception) { +// e.printStackTrace() +// } finally { +// bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, proxy) +// } +// } +// } +// +// override fun onServiceDisconnected(profile: Int) { } +// }, BluetoothProfile.A2DP) +// +// bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener { +// override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { +// if (profile == BluetoothProfile.HEADSET) { +// try { +// val method = proxy.javaClass.getMethod("connect", BluetoothDevice::class.java) +// method.invoke(proxy, device) +// } catch (e: Exception) { +// e.printStackTrace() +// } finally { +// bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy) +// } +// } +// } +// +// override fun onServiceDisconnected(profile: Int) { } +// }, BluetoothProfile.HEADSET) +// } +// +// fun setName(name: String) { +// val nameBytes = name.toByteArray() +// val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x01, +// nameBytes.size.toByte(), 0x00) + nameBytes +// socket?.outputStream?.write(bytes) +// socket?.outputStream?.flush() +// val hex = bytes.joinToString(" ") { "%02X".format(it) } +// Log.d("OldAirPodsService", "setName: $name, sent packet: $hex") +// } +// +// @SuppressLint("MissingPermission", "InlinedApi") +// override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { +// +// val notification = createNotification() +// startForeground(1, notification) +// +// ServiceManager.setService(this) +// +// if (isConnected) { +// return START_STICKY +// } +// isConnected = true +// +// @Suppress("DEPRECATION") val device = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) intent?.getParcelableExtra("device", BluetoothDevice::class.java) else intent?.getParcelableExtra("device") +// +// HiddenApiBypass.addHiddenApiExemptions("Landroid/bluetooth/BluetoothSocket;") +// val uuid: ParcelUuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a") +// +// socket = HiddenApiBypass.newInstance(BluetoothSocket::class.java, 3, true, true, device, 0x1001, uuid) as BluetoothSocket? +// try { +// socket?.connect() +// socket?.let { it -> +// it.outputStream.write(Enums.HANDSHAKE.value) +// it.outputStream.write(Enums.SET_SPECIFIC_FEATURES.value) +// it.outputStream.write(Enums.REQUEST_NOTIFICATIONS.value) +// sendBroadcast(Intent(AirPodsNotifications.AIRPODS_CONNECTED)) +// it.outputStream.flush() +// +// CoroutineScope(Dispatchers.IO).launch { +// while (socket?.isConnected == true) { +// socket?.let { +// val audioManager = this@OldAirPodsService.getSystemService(AUDIO_SERVICE) as AudioManager +// MediaController.initialize(audioManager) +// val buffer = ByteArray(1024) +// val bytesRead = it.inputStream.read(buffer) +// var data: ByteArray = byteArrayOf() +// if (bytesRead > 0) { +// data = buffer.copyOfRange(0, bytesRead) +// sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DATA).apply { +// putExtra("data", buffer.copyOfRange(0, bytesRead)) +// }) +// val bytes = buffer.copyOfRange(0, bytesRead) +// val formattedHex = bytes.joinToString(" ") { "%02X".format(it) } +// Log.d("AirPods Data", "Data received: $formattedHex") +// } +// else if (bytesRead == -1) { +// Log.d("AirPods Service", "Socket closed (bytesRead = -1)") +// this@OldAirPodsService.stopForeground(STOP_FOREGROUND_REMOVE) +// socket?.close() +// sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED)) +// return@launch +// } +// var inEar = false +// var inEarData = listOf() +// if (earDetectionNotification.isEarDetectionData(data)) { +// earDetectionNotification.setStatus(data) +// sendBroadcast(Intent(AirPodsNotifications.EAR_DETECTION_DATA).apply { +// val list = earDetectionNotification.status +// val bytes = ByteArray(2) +// bytes[0] = list[0] +// bytes[1] = list[1] +// putExtra("data", bytes) +// }) +// Log.d("AirPods Parser", "Ear Detection: ${earDetectionNotification.status[0]} ${earDetectionNotification.status[1]}") +// var justEnabledA2dp = false +// val earReceiver = object : BroadcastReceiver() { +// override fun onReceive(context: Context, intent: Intent) { +// val data = intent.getByteArrayExtra("data") +// if (data != null && earDetectionEnabled) { +// 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() +// } +// +// val newInEarData = listOf(data[0] == 0x00.toByte(), data[1] == 0x00.toByte()) +// if (newInEarData.contains(true) && inEarData == listOf(false, false)) { +// connectAudio(this@OldAirPodsService, device) +// justEnabledA2dp = true +// val bluetoothAdapter = this@OldAirPodsService.getSystemService(BluetoothManager::class.java).adapter +// bluetoothAdapter.getProfileProxy( +// this@OldAirPodsService, object : BluetoothProfile.ServiceListener { +// override fun onServiceConnected( +// profile: Int, +// proxy: BluetoothProfile +// ) { +// if (profile == BluetoothProfile.A2DP) { +// val connectedDevices = +// proxy.connectedDevices +// if (connectedDevices.isNotEmpty()) { +// MediaController.sendPlay() +// } +// } +// bluetoothAdapter.closeProfileProxy( +// profile, +// proxy +// ) +// } +// +// override fun onServiceDisconnected( +// profile: Int +// ) { +// } +// } +// ,BluetoothProfile.A2DP +// ) +// +// } +// else if (newInEarData == listOf(false, false)){ +// disconnectAudio(this@OldAirPodsService, device) +// } +// +// inEarData = newInEarData +// +// if (inEar == true) { +// if (!justEnabledA2dp) { +// justEnabledA2dp = false +// MediaController.sendPlay() +// } +// } else { +// MediaController.sendPause() +// } +// } +// } +// } +// +// val earIntentFilter = IntentFilter(AirPodsNotifications.EAR_DETECTION_DATA) +// this@OldAirPodsService.registerReceiver(earReceiver, earIntentFilter, +// RECEIVER_EXPORTED +// ) +// } +// else if (ancNotification.isANCData(data)) { +// ancNotification.setStatus(data) +// sendBroadcast(Intent(AirPodsNotifications.ANC_DATA).apply { +// putExtra("data", ancNotification.status) +// }) +// Log.d("AirPods Parser", "ANC: ${ancNotification.status}") +// } +// else if (batteryNotification.isBatteryData(data)) { +// batteryNotification.setBattery(data) +// sendBroadcast(Intent(AirPodsNotifications.BATTERY_DATA).apply { +// putParcelableArrayListExtra("data", ArrayList(batteryNotification.getBattery())) +// }) +// for (battery in batteryNotification.getBattery()) { +// Log.d("AirPods Parser", "${battery.getComponentName()}: ${battery.getStatusName()} at ${battery.level}% ") +// } +//// if both are charging, disconnect audio profiles +// if (batteryNotification.getBattery()[0].status == 1 && batteryNotification.getBattery()[1].status == 1) { +// disconnectAudio(this@OldAirPodsService, device) +// } +// else { +// connectAudio(this@OldAirPodsService, device) +// } +//// updatePodsStatus(device!!, batteryNotification.getBattery()) +// } +// else if (conversationAwarenessNotification.isConversationalAwarenessData(data)) { +// conversationAwarenessNotification.setData(data) +// sendBroadcast(Intent(AirPodsNotifications.CA_DATA).apply { +// putExtra("data", conversationAwarenessNotification.status) +// }) +// +// +// if (conversationAwarenessNotification.status == 1.toByte() || conversationAwarenessNotification.status == 2.toByte()) { +// MediaController.startSpeaking() +// } else if (conversationAwarenessNotification.status == 8.toByte() || conversationAwarenessNotification.status == 9.toByte()) { +// MediaController.stopSpeaking() +// } +// +// Log.d("AirPods Parser", "Conversation Awareness: ${conversationAwarenessNotification.status}") +// } +// else { } +// } +// } +// Log.d("AirPods Service", "Socket closed") +// isConnected = false +// this@OldAirPodsService.stopForeground(STOP_FOREGROUND_REMOVE) +// socket?.close() +// sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED)) +// } +// } +// } +// catch (e: Exception) { +// Log.e("AirPodsSettingsScreen", "Error connecting to device: ${e.message}") +// } +// return START_STICKY +// } +// +// override fun onDestroy() { +// super.onDestroy() +// socket?.close() +// isConnected = false +// ServiceManager.setService(null) +// } +// +// fun setPVEnabled(enabled: Boolean) { +// var hex = "04 00 04 00 09 00 26 ${if (enabled) "01" else "02"} 00 00 00" +// var bytes = hex.split(" ").map { it.toInt(16).toByte() }.toByteArray() +// socket?.outputStream?.write(bytes) +// hex = "04 00 04 00 17 00 00 00 10 00 12 00 08 E${if (enabled) "6" else "5"} 05 10 02 42 0B 08 50 10 02 1A 05 02 ${if (enabled) "32" else "00"} 00 00 00" +// bytes = hex.split(" ").map { it.toInt(16).toByte() }.toByteArray() +// socket?.outputStream?.write(bytes) +// } +// +// fun setLoudSoundReduction(enabled: Boolean) { +// val hex = "52 1B 00 0${if (enabled) "1" else "0"}" +// val bytes = hex.split(" ").map { it.toInt(16).toByte() }.toByteArray() +// socket?.outputStream?.write(bytes) +// } +//} \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 3e00038..aa643b1 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,5 +1,4 @@ ALN - DebugActivity - CustomDevice + GATT Testing \ No newline at end of file