fix background connections, add manual force connection button

This commit is contained in:
Kavish Devar
2024-12-06 11:56:07 +05:30
parent 0c1f9464ad
commit 2679205dc3
8 changed files with 597 additions and 537 deletions

View File

@@ -35,8 +35,6 @@
android:label="@string/title_activity_custom_device" android:label="@string/title_activity_custom_device"
android:theme="@style/Theme.ALN"> android:theme="@style/Theme.ALN">
<intent-filter> <intent-filter>
<!-- <action android:name="android.intent.action.MAIN" />-->
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
@@ -46,17 +44,10 @@
android:theme="@style/Theme.ALN"> android:theme="@style/Theme.ALN">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<service
android:name=".OldAirPodsService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="connectedDevice"
android:permission="android.permission.BLUETOOTH_CONNECT" />
<service <service
android:name=".AirPodsService" android:name=".AirPodsService"
android:enabled="true" android:enabled="true"
@@ -73,6 +64,16 @@
<action android:name="android.service.quicksettings.action.QS_TILE" /> <action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter> </intent-filter>
</service> </service>
<receiver
android:name=".BootReceiver"
android:enabled="true"
android:exported="true"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
</application> </application>
</manifest> </manifest>

View File

@@ -10,20 +10,17 @@ import android.service.quicksettings.TileService
import android.util.Log import android.util.Log
class AirPodsQSService: TileService() { class AirPodsQSService: TileService() {
private val sharedPreferences = ServiceManager.getService()?.getSharedPreferences("me.kavishdevar.aln", Context.MODE_PRIVATE) private val ancModes = listOf(NoiseControlMode.NOISE_CANCELLATION.name, NoiseControlMode.TRANSPARENCY.name, NoiseControlMode.ADAPTIVE.name)
private val offListeningModeEnabled = sharedPreferences?.getBoolean("off_listening_mode", false) == true private var currentModeIndex = 2
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 lateinit var ancStatusReceiver: BroadcastReceiver private lateinit var ancStatusReceiver: BroadcastReceiver
private lateinit var availabilityReceiver: BroadcastReceiver private lateinit var availabilityReceiver: BroadcastReceiver
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
override fun onStartListening() { override fun onStartListening() {
Log.d("AirPodsQSService", "off mode: $offListeningModeEnabled")
super.onStartListening() 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) { if (currentModeIndex == -1) {
currentModeIndex = if (offListeningModeEnabled) 3 else 2 currentModeIndex = 2
} }
if (ServiceManager.getService() == null) { if (ServiceManager.getService() == null) {
@@ -42,7 +39,7 @@ class AirPodsQSService: TileService() {
ancStatusReceiver = object : BroadcastReceiver() { ancStatusReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
val ancStatus = intent.getIntExtra("data", 4) val ancStatus = intent.getIntExtra("data", 4)
currentModeIndex = ancStatus - if (offListeningModeEnabled) 1 else 2 currentModeIndex = ancStatus - 1
updateTile() updateTile()
} }
} }
@@ -89,10 +86,9 @@ class AirPodsQSService: TileService() {
override fun onClick() { override fun onClick() {
super.onClick() super.onClick()
Log.d("QuickSettingTileService", "ANC tile clicked") Log.d("QuickSettingTileService", "ANC tile clicked")
Log.d("QuickSettingTileService", "Current mode index: $currentModeIndex, ancModes size: ${ancModes.size}")
currentModeIndex = (currentModeIndex + 1) % ancModes.size currentModeIndex = (currentModeIndex + 1) % ancModes.size
Log.d("QuickSettingTileService", "New mode index: $currentModeIndex") Log.d("QuickSettingTileService", "New mode index: $currentModeIndex, would be set to ${currentModeIndex + 1}")
switchAncMode(if (offListeningModeEnabled) currentModeIndex + 1 else currentModeIndex + 2) switchAncMode()
} }
private fun updateTile() { private fun updateTile() {
@@ -102,10 +98,11 @@ class AirPodsQSService: TileService() {
qsTile.updateTile() qsTile.updateTile()
} }
private fun switchAncMode(modeIndex: Int) { private fun switchAncMode() {
currentModeIndex = if (offListeningModeEnabled) modeIndex else modeIndex - 1
val airPodsService = ServiceManager.getService() 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() updateTile()
} }
} }

View File

@@ -23,10 +23,21 @@ import android.widget.RemoteViews
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.lsposed.hiddenapibypass.HiddenApiBypass 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") @Suppress("unused")
class AirPodsService: Service() { class AirPodsService: Service() {
inner class LocalBinder : Binder() { inner class LocalBinder : Binder() {
@@ -185,6 +196,7 @@ class AirPodsService: Service() {
@SuppressLint("InlinedApi", "MissingPermission") @SuppressLint("InlinedApi", "MissingPermission")
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d("AirPodsService", "Service started") Log.d("AirPodsService", "Service started")
ServiceManager.setService(this)
startForegroundNotification() startForegroundNotification()
registerReceiver(bluetoothReceiver, BluetoothReceiver.buildFilter(), RECEIVER_EXPORTED) registerReceiver(bluetoothReceiver, BluetoothReceiver.buildFilter(), RECEIVER_EXPORTED)
@@ -215,7 +227,7 @@ class AirPodsService: Service() {
registerReceiver(connectionReceiver, intentFilter, RECEIVER_EXPORTED) registerReceiver(connectionReceiver, intentFilter, RECEIVER_EXPORTED)
val bluetoothAdapter = getSystemService(BluetoothManager::class.java).adapter 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"))) { if (device.uuids.contains(ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a"))) {
bluetoothAdapter.getProfileProxy(this, object : BluetoothProfile.ServiceListener { bluetoothAdapter.getProfileProxy(this, object : BluetoothProfile.ServiceListener {
override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
@@ -223,6 +235,9 @@ class AirPodsService: Service() {
val connectedDevices = proxy.connectedDevices val connectedDevices = proxy.connectedDevices
if (connectedDevices.isNotEmpty()) { if (connectedDevices.isNotEmpty()) {
connectToSocket(device) connectToSocket(device)
this@AirPodsService.sendBroadcast(
Intent(AirPodsNotifications.AIRPODS_CONNECTED)
)
} }
} }
bluetoothAdapter.closeProfileProxy(profile, proxy) bluetoothAdapter.closeProfileProxy(profile, proxy)
@@ -243,6 +258,12 @@ class AirPodsService: Service() {
HiddenApiBypass.addHiddenApiExemptions("Landroid/bluetooth/BluetoothSocket;") HiddenApiBypass.addHiddenApiExemptions("Landroid/bluetooth/BluetoothSocket;")
val uuid: ParcelUuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a") val uuid: ParcelUuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a")
try {
socket.close()
} catch (e: Exception) {
e.printStackTrace()
}
try { try {
socket = HiddenApiBypass.newInstance( socket = HiddenApiBypass.newInstance(
BluetoothSocket::class.java, BluetoothSocket::class.java,
@@ -282,21 +303,17 @@ class AirPodsService: Service() {
this@AirPodsService.device = device this@AirPodsService.device = device
isConnected = true isConnected = true
socket.let { it -> socket.let { it ->
CoroutineScope(Dispatchers.IO).launch { it.outputStream.write(Enums.HANDSHAKE.value)
it.outputStream.write(Enums.HANDSHAKE.value) it.outputStream.flush()
it.outputStream.flush() it.outputStream.write(Enums.SET_SPECIFIC_FEATURES.value)
delay(500) it.outputStream.flush()
it.outputStream.write(Enums.SET_SPECIFIC_FEATURES.value) it.outputStream.write(Enums.REQUEST_NOTIFICATIONS.value)
it.outputStream.flush() it.outputStream.flush()
delay(500)
it.outputStream.write(Enums.REQUEST_NOTIFICATIONS.value)
it.outputStream.flush()
}
sendBroadcast( sendBroadcast(
Intent(AirPodsNotifications.AIRPODS_CONNECTED) Intent(AirPodsNotifications.AIRPODS_CONNECTED)
.putExtra("device", device) .putExtra("device", device)
) )
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
while (socket.isConnected == true) { while (socket.isConnected == true) {
socket.let { socket.let {
@@ -316,7 +333,7 @@ class AirPodsService: Service() {
} }
else if (bytesRead == -1) { else if (bytesRead == -1) {
Log.d("AirPods Service", "Socket closed (bytesRead = -1)") Log.d("AirPods Service", "Socket closed (bytesRead = -1)")
socket.close() // socket.close()
sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED)) sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED))
return@launch return@launch
} }

View File

@@ -2,6 +2,8 @@ package me.kavishdevar.aln
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Context.MODE_PRIVATE import android.content.Context.MODE_PRIVATE
@@ -9,6 +11,8 @@ import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Build import android.os.Build
import android.os.ParcelUuid
import android.util.Log
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
@@ -37,6 +41,7 @@ import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowRight import androidx.compose.material.icons.filled.KeyboardArrowRight
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@@ -84,6 +89,7 @@ import androidx.compose.ui.unit.sp
import androidx.navigation.NavController import androidx.navigation.NavController
import com.primex.core.ExperimentalToolkitApi import com.primex.core.ExperimentalToolkitApi
import com.primex.core.blur.newBackgroundBlur import com.primex.core.blur.newBackgroundBlur
import me.kavishdevar.aln.AirPodsService
import kotlin.math.roundToInt import kotlin.math.roundToInt
@@ -157,38 +163,19 @@ fun BatteryView(service: AirPodsService, preview: Boolean = false) {
horizontalArrangement = Arrangement.Center horizontalArrangement = Arrangement.Center
) { ) {
if (left?.status != BatteryStatus.DISCONNECTED) { if (left?.status != BatteryStatus.DISCONNECTED) {
Row ( BatteryIndicator(
horizontalArrangement = Arrangement.SpaceEvenly, left?.level ?: 0,
modifier = Modifier left?.status == BatteryStatus.CHARGING
.weight(1f) )
) { }
Text( if (left?.status != BatteryStatus.DISCONNECTED && right?.status != BatteryStatus.DISCONNECTED) {
text = "\uDBC6\uDCE5", Spacer(modifier = Modifier.width(16.dp))
fontFamily = FontFamily(Font(R.font.sf_pro)),
)
BatteryIndicator(
left?.level ?: 0,
left?.status == BatteryStatus.CHARGING
)
}
} }
if (right?.status != BatteryStatus.DISCONNECTED) { if (right?.status != BatteryStatus.DISCONNECTED) {
Row ( BatteryIndicator(
horizontalArrangement = Arrangement.Center, right?.level ?: 0,
modifier = Modifier right?.status == BatteryStatus.CHARGING
.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
)
}
} }
} }
} }
@@ -556,7 +543,41 @@ fun AirPodsSettingsScreen(device: BluetoothDevice?, service: AirPodsService,
}, },
colors = TopAppBarDefaults.centerAlignedTopAppBarColors( colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
containerColor = if (MaterialTheme.colorScheme.surface.luminance() < 0.5) Color.Black.copy(0.3f) else Color(0xFFF2F2F7).copy(0.2f), 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 -> ) { paddingValues ->
@@ -1467,8 +1488,8 @@ fun BatteryIndicator(batteryPercentage: Int, charging: Boolean = false) {
val batteryWidth = 40.dp val batteryWidth = 40.dp
val batteryHeight = 15.dp val batteryHeight = 15.dp
val batteryCornerRadius = 4.dp val batteryCornerRadius = 4.dp
val tipWidth = 4.dp val tipWidth = 5.dp
val tipHeight = batteryHeight * 0.3f val tipHeight = batteryHeight * 0.375f
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally

View File

@@ -1,4 +1,14 @@
package me.kavishdevar.aln 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() }
}
}
} }

View File

@@ -70,6 +70,22 @@ class MainActivity : ComponentActivity() {
} }
super.onDestroy() 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") @SuppressLint("MissingPermission", "InlinedApi")
@@ -86,7 +102,6 @@ fun Main() {
val airPodsService = remember { mutableStateOf<AirPodsService?>(null) } val airPodsService = remember { mutableStateOf<AirPodsService?>(null) }
if (permissionState.allPermissionsGranted) { if (permissionState.allPermissionsGranted) {
Log.d("MainActivity", "HIIIIIIIIIIIIIII")
val context = LocalContext.current val context = LocalContext.current
val navController = rememberNavController() val navController = rememberNavController()

View File

@@ -1,463 +1,463 @@
@file:Suppress("unused") //@file:Suppress("unused")
//
package me.kavishdevar.aln //package me.kavishdevar.aln
//
import android.annotation.SuppressLint //import android.annotation.SuppressLint
import android.app.Notification //import android.app.Notification
import android.app.NotificationChannel //import android.app.NotificationChannel
import android.app.NotificationManager //import android.app.NotificationManager
import android.app.Service //import android.app.Service
import android.bluetooth.BluetoothDevice //import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager //import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile //import android.bluetooth.BluetoothProfile
import android.bluetooth.BluetoothSocket //import android.bluetooth.BluetoothSocket
import android.content.BroadcastReceiver //import android.content.BroadcastReceiver
import android.content.Context //import android.content.Context
import android.content.Intent //import android.content.Intent
import android.content.IntentFilter //import android.content.IntentFilter
import android.media.AudioManager //import android.media.AudioManager
import android.os.Binder //import android.os.Binder
import android.os.Build //import android.os.Build
import android.os.IBinder //import android.os.IBinder
import android.os.ParcelUuid //import android.os.ParcelUuid
import android.util.Log //import android.util.Log
import androidx.core.app.NotificationCompat //import androidx.core.app.NotificationCompat
import kotlinx.coroutines.CoroutineScope //import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers //import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch //import kotlinx.coroutines.launch
import org.lsposed.hiddenapibypass.HiddenApiBypass //import org.lsposed.hiddenapibypass.HiddenApiBypass
//
object ServiceManager { //object ServiceManager {
private var service: OldAirPodsService? = null // private var service: OldAirPodsService? = null
@Synchronized // @Synchronized
fun getService(): OldAirPodsService? { // fun getService(): OldAirPodsService? {
return service // return service
} // }
@Synchronized // @Synchronized
fun setService(service: OldAirPodsService?) { // fun setService(service: OldAirPodsService?) {
this.service = service // this.service = service
} // }
} //}
//
class OldAirPodsService : Service() { //class OldAirPodsService : Service() {
inner class LocalBinder : Binder() { // inner class LocalBinder : Binder() {
fun getService(): OldAirPodsService = this@OldAirPodsService // fun getService(): OldAirPodsService = this@OldAirPodsService
} // }
//
override fun onBind(intent: Intent?): IBinder { // override fun onBind(intent: Intent?): IBinder {
return LocalBinder() // return LocalBinder()
} // }
//
var isConnected: Boolean = false // var isConnected: Boolean = false
private var socket: BluetoothSocket? = null // private var socket: BluetoothSocket? = null
//
fun sendPacket(packet: String) { // fun sendPacket(packet: String) {
val fromHex = packet.split(" ").map { it.toInt(16).toByte() } // val fromHex = packet.split(" ").map { it.toInt(16).toByte() }
socket?.outputStream?.write(fromHex.toByteArray()) // socket?.outputStream?.write(fromHex.toByteArray())
socket?.outputStream?.flush() // socket?.outputStream?.flush()
} // }
//
fun setANCMode(mode: Int) { // fun setANCMode(mode: Int) {
when (mode) { // when (mode) {
1 -> { // 1 -> {
socket?.outputStream?.write(Enums.NOISE_CANCELLATION_OFF.value) // socket?.outputStream?.write(Enums.NOISE_CANCELLATION_OFF.value)
} // }
2 -> { // 2 -> {
socket?.outputStream?.write(Enums.NOISE_CANCELLATION_ON.value) // socket?.outputStream?.write(Enums.NOISE_CANCELLATION_ON.value)
} // }
3 -> { // 3 -> {
socket?.outputStream?.write(Enums.NOISE_CANCELLATION_TRANSPARENCY.value) // socket?.outputStream?.write(Enums.NOISE_CANCELLATION_TRANSPARENCY.value)
} // }
4 -> { // 4 -> {
socket?.outputStream?.write(Enums.NOISE_CANCELLATION_ADAPTIVE.value) // socket?.outputStream?.write(Enums.NOISE_CANCELLATION_ADAPTIVE.value)
} // }
} // }
socket?.outputStream?.flush() // socket?.outputStream?.flush()
} // }
//
fun setCAEnabled(enabled: Boolean) { // fun setCAEnabled(enabled: Boolean) {
socket?.outputStream?.write(if (enabled) Enums.SET_CONVERSATION_AWARENESS_ON.value else Enums.SET_CONVERSATION_AWARENESS_OFF.value) // socket?.outputStream?.write(if (enabled) Enums.SET_CONVERSATION_AWARENESS_ON.value else Enums.SET_CONVERSATION_AWARENESS_OFF.value)
} // }
//
fun setOffListeningMode(enabled: Boolean) { // fun setOffListeningMode(enabled: Boolean) {
socket?.outputStream?.write(byteArrayOf(0x04, 0x00 ,0x04, 0x00, 0x09, 0x00, 0x34, if (enabled) 0x01 else 0x02, 0x00, 0x00, 0x00)) // socket?.outputStream?.write(byteArrayOf(0x04, 0x00 ,0x04, 0x00, 0x09, 0x00, 0x34, if (enabled) 0x01 else 0x02, 0x00, 0x00, 0x00))
} // }
//
fun setAdaptiveStrength(strength: Int) { // fun setAdaptiveStrength(strength: Int) {
val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x2E, strength.toByte(), 0x00, 0x00, 0x00) // val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x2E, strength.toByte(), 0x00, 0x00, 0x00)
socket?.outputStream?.write(bytes) // socket?.outputStream?.write(bytes)
socket?.outputStream?.flush() // socket?.outputStream?.flush()
} // }
//
fun setPressSpeed(speed: Int) { // fun setPressSpeed(speed: Int) {
val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x17, speed.toByte(), 0x00, 0x00, 0x00) // val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x17, speed.toByte(), 0x00, 0x00, 0x00)
socket?.outputStream?.write(bytes) // socket?.outputStream?.write(bytes)
socket?.outputStream?.flush() // socket?.outputStream?.flush()
} // }
//
fun setPressAndHoldDuration(speed: Int) { // fun setPressAndHoldDuration(speed: Int) {
val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x18, speed.toByte(), 0x00, 0x00, 0x00) // val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x18, speed.toByte(), 0x00, 0x00, 0x00)
socket?.outputStream?.write(bytes) // socket?.outputStream?.write(bytes)
socket?.outputStream?.flush() // socket?.outputStream?.flush()
} // }
//
fun setNoiseCancellationWithOnePod(enabled: Boolean) { // fun setNoiseCancellationWithOnePod(enabled: Boolean) {
val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x1B, if (enabled) 0x01 else 0x02, 0x00, 0x00, 0x00) // val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x1B, if (enabled) 0x01 else 0x02, 0x00, 0x00, 0x00)
socket?.outputStream?.write(bytes) // socket?.outputStream?.write(bytes)
socket?.outputStream?.flush() // socket?.outputStream?.flush()
} // }
//
fun setVolumeControl(enabled: Boolean) { // fun setVolumeControl(enabled: Boolean) {
val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x25, if (enabled) 0x01 else 0x02, 0x00, 0x00, 0x00) // val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x25, if (enabled) 0x01 else 0x02, 0x00, 0x00, 0x00)
socket?.outputStream?.write(bytes) // socket?.outputStream?.write(bytes)
socket?.outputStream?.flush() // socket?.outputStream?.flush()
} // }
//
fun setVolumeSwipeSpeed(speed: Int) { // fun setVolumeSwipeSpeed(speed: Int) {
val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x23, speed.toByte(), 0x00, 0x00, 0x00) // val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x23, speed.toByte(), 0x00, 0x00, 0x00)
socket?.outputStream?.write(bytes) // socket?.outputStream?.write(bytes)
socket?.outputStream?.flush() // socket?.outputStream?.flush()
} // }
//
fun setToneVolume(volume: Int) { // fun setToneVolume(volume: Int) {
val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x1F, volume.toByte(), 0x50, 0x00, 0x00) // val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x09, 0x00, 0x1F, volume.toByte(), 0x50, 0x00, 0x00)
socket?.outputStream?.write(bytes) // socket?.outputStream?.write(bytes)
socket?.outputStream?.flush() // socket?.outputStream?.flush()
} // }
//
val earDetectionNotification = AirPodsNotifications.EarDetection() // val earDetectionNotification = AirPodsNotifications.EarDetection()
val ancNotification = AirPodsNotifications.ANC() // val ancNotification = AirPodsNotifications.ANC()
val batteryNotification = AirPodsNotifications.BatteryNotification() // val batteryNotification = AirPodsNotifications.BatteryNotification()
val conversationAwarenessNotification = AirPodsNotifications.ConversationalAwarenessNotification() // val conversationAwarenessNotification = AirPodsNotifications.ConversationalAwarenessNotification()
//
var earDetectionEnabled = true // var earDetectionEnabled = true
//
fun setCaseChargingSounds(enabled: Boolean) { // fun setCaseChargingSounds(enabled: Boolean) {
val bytes = byteArrayOf(0x12, 0x3a, 0x00, 0x01, 0x00, 0x08, if (enabled) 0x00 else 0x01) // val bytes = byteArrayOf(0x12, 0x3a, 0x00, 0x01, 0x00, 0x08, if (enabled) 0x00 else 0x01)
socket?.outputStream?.write(bytes) // socket?.outputStream?.write(bytes)
socket?.outputStream?.flush() // socket?.outputStream?.flush()
} // }
//
fun setEarDetection(enabled: Boolean) { // fun setEarDetection(enabled: Boolean) {
earDetectionEnabled = enabled // earDetectionEnabled = enabled
} // }
//
fun getBattery(): List<Battery> { // fun getBattery(): List<Battery> {
return batteryNotification.getBattery() // return batteryNotification.getBattery()
} // }
//
fun getANC(): Int { // fun getANC(): Int {
return ancNotification.status // return ancNotification.status
} // }
//
private fun createNotification(): Notification { // private fun createNotification(): Notification {
val channelId = "battery" // val channelId = "battery"
val notificationBuilder = NotificationCompat.Builder(this, channelId) // val notificationBuilder = NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.pro_2_buds) // .setSmallIcon(R.drawable.pro_2_buds)
.setContentTitle("AirPods Connected") // .setContentTitle("AirPods Connected")
.setOngoing(true) // .setOngoing(true)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) // .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
//
val channel = // val channel =
NotificationChannel(channelId, "Battery Notification", NotificationManager.IMPORTANCE_LOW) // NotificationChannel(channelId, "Battery Notification", NotificationManager.IMPORTANCE_LOW)
//
val notificationManager = getSystemService(NotificationManager::class.java) // val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(channel) // notificationManager.createNotificationChannel(channel)
return notificationBuilder.build() // return notificationBuilder.build()
} // }
//
fun disconnectAudio(context: Context, device: BluetoothDevice?) { // fun disconnectAudio(context: Context, device: BluetoothDevice?) {
val bluetoothAdapter = context.getSystemService(BluetoothManager::class.java).adapter // val bluetoothAdapter = context.getSystemService(BluetoothManager::class.java).adapter
//
bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener { // bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener {
override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { // override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
if (profile == BluetoothProfile.A2DP) { // if (profile == BluetoothProfile.A2DP) {
try { // try {
val method = proxy.javaClass.getMethod("disconnect", BluetoothDevice::class.java) // val method = proxy.javaClass.getMethod("disconnect", BluetoothDevice::class.java)
method.invoke(proxy, device) // method.invoke(proxy, device)
} catch (e: Exception) { // } catch (e: Exception) {
e.printStackTrace() // e.printStackTrace()
} finally { // } finally {
bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, proxy) // bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, proxy)
} // }
} // }
} // }
//
override fun onServiceDisconnected(profile: Int) { } // override fun onServiceDisconnected(profile: Int) { }
}, BluetoothProfile.A2DP) // }, BluetoothProfile.A2DP)
//
bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener { // bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener {
override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { // override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
if (profile == BluetoothProfile.HEADSET) { // if (profile == BluetoothProfile.HEADSET) {
try { // try {
val method = proxy.javaClass.getMethod("disconnect", BluetoothDevice::class.java) // val method = proxy.javaClass.getMethod("disconnect", BluetoothDevice::class.java)
method.invoke(proxy, device) // method.invoke(proxy, device)
} catch (e: Exception) { // } catch (e: Exception) {
e.printStackTrace() // e.printStackTrace()
} finally { // } finally {
bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy) // bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy)
} // }
} // }
} // }
//
override fun onServiceDisconnected(profile: Int) { } // override fun onServiceDisconnected(profile: Int) { }
}, BluetoothProfile.HEADSET) // }, BluetoothProfile.HEADSET)
} // }
//
fun connectAudio(context: Context, device: BluetoothDevice?) { // fun connectAudio(context: Context, device: BluetoothDevice?) {
val bluetoothAdapter = context.getSystemService(BluetoothManager::class.java).adapter // val bluetoothAdapter = context.getSystemService(BluetoothManager::class.java).adapter
//
bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener { // bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener {
override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { // override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
if (profile == BluetoothProfile.A2DP) { // if (profile == BluetoothProfile.A2DP) {
try { // try {
val method = proxy.javaClass.getMethod("connect", BluetoothDevice::class.java) // val method = proxy.javaClass.getMethod("connect", BluetoothDevice::class.java)
method.invoke(proxy, device) // method.invoke(proxy, device)
} catch (e: Exception) { // } catch (e: Exception) {
e.printStackTrace() // e.printStackTrace()
} finally { // } finally {
bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, proxy) // bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, proxy)
} // }
} // }
} // }
//
override fun onServiceDisconnected(profile: Int) { } // override fun onServiceDisconnected(profile: Int) { }
}, BluetoothProfile.A2DP) // }, BluetoothProfile.A2DP)
//
bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener { // bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener {
override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { // override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
if (profile == BluetoothProfile.HEADSET) { // if (profile == BluetoothProfile.HEADSET) {
try { // try {
val method = proxy.javaClass.getMethod("connect", BluetoothDevice::class.java) // val method = proxy.javaClass.getMethod("connect", BluetoothDevice::class.java)
method.invoke(proxy, device) // method.invoke(proxy, device)
} catch (e: Exception) { // } catch (e: Exception) {
e.printStackTrace() // e.printStackTrace()
} finally { // } finally {
bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy) // bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy)
} // }
} // }
} // }
//
override fun onServiceDisconnected(profile: Int) { } // override fun onServiceDisconnected(profile: Int) { }
}, BluetoothProfile.HEADSET) // }, BluetoothProfile.HEADSET)
} // }
//
fun setName(name: String) { // fun setName(name: String) {
val nameBytes = name.toByteArray() // val nameBytes = name.toByteArray()
val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x01, // val bytes = byteArrayOf(0x04, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x01,
nameBytes.size.toByte(), 0x00) + nameBytes // nameBytes.size.toByte(), 0x00) + nameBytes
socket?.outputStream?.write(bytes) // socket?.outputStream?.write(bytes)
socket?.outputStream?.flush() // socket?.outputStream?.flush()
val hex = bytes.joinToString(" ") { "%02X".format(it) } // val hex = bytes.joinToString(" ") { "%02X".format(it) }
Log.d("OldAirPodsService", "setName: $name, sent packet: $hex") // Log.d("OldAirPodsService", "setName: $name, sent packet: $hex")
} // }
//
@SuppressLint("MissingPermission", "InlinedApi") // @SuppressLint("MissingPermission", "InlinedApi")
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { // override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
//
val notification = createNotification() // val notification = createNotification()
startForeground(1, notification) // startForeground(1, notification)
//
ServiceManager.setService(this) // ServiceManager.setService(this)
//
if (isConnected) { // if (isConnected) {
return START_STICKY // return START_STICKY
} // }
isConnected = true // 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") // @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;") // HiddenApiBypass.addHiddenApiExemptions("Landroid/bluetooth/BluetoothSocket;")
val uuid: ParcelUuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a") // val uuid: ParcelUuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a")
//
socket = HiddenApiBypass.newInstance(BluetoothSocket::class.java, 3, true, true, device, 0x1001, uuid) as BluetoothSocket? // socket = HiddenApiBypass.newInstance(BluetoothSocket::class.java, 3, true, true, device, 0x1001, uuid) as BluetoothSocket?
try { // try {
socket?.connect() // socket?.connect()
socket?.let { it -> // socket?.let { it ->
it.outputStream.write(Enums.HANDSHAKE.value) // it.outputStream.write(Enums.HANDSHAKE.value)
it.outputStream.write(Enums.SET_SPECIFIC_FEATURES.value) // it.outputStream.write(Enums.SET_SPECIFIC_FEATURES.value)
it.outputStream.write(Enums.REQUEST_NOTIFICATIONS.value) // it.outputStream.write(Enums.REQUEST_NOTIFICATIONS.value)
sendBroadcast(Intent(AirPodsNotifications.AIRPODS_CONNECTED)) // sendBroadcast(Intent(AirPodsNotifications.AIRPODS_CONNECTED))
it.outputStream.flush() // it.outputStream.flush()
//
CoroutineScope(Dispatchers.IO).launch { // CoroutineScope(Dispatchers.IO).launch {
while (socket?.isConnected == true) { // while (socket?.isConnected == true) {
socket?.let { // socket?.let {
val audioManager = this@OldAirPodsService.getSystemService(AUDIO_SERVICE) as AudioManager // val audioManager = this@OldAirPodsService.getSystemService(AUDIO_SERVICE) as AudioManager
MediaController.initialize(audioManager) // MediaController.initialize(audioManager)
val buffer = ByteArray(1024) // val buffer = ByteArray(1024)
val bytesRead = it.inputStream.read(buffer) // val bytesRead = it.inputStream.read(buffer)
var data: ByteArray = byteArrayOf() // var data: ByteArray = byteArrayOf()
if (bytesRead > 0) { // if (bytesRead > 0) {
data = buffer.copyOfRange(0, bytesRead) // data = buffer.copyOfRange(0, bytesRead)
sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DATA).apply { // sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DATA).apply {
putExtra("data", buffer.copyOfRange(0, bytesRead)) // putExtra("data", buffer.copyOfRange(0, bytesRead))
}) // })
val bytes = buffer.copyOfRange(0, bytesRead) // val bytes = buffer.copyOfRange(0, bytesRead)
val formattedHex = bytes.joinToString(" ") { "%02X".format(it) } // val formattedHex = bytes.joinToString(" ") { "%02X".format(it) }
Log.d("AirPods Data", "Data received: $formattedHex") // Log.d("AirPods Data", "Data received: $formattedHex")
} // }
else if (bytesRead == -1) { // else if (bytesRead == -1) {
Log.d("AirPods Service", "Socket closed (bytesRead = -1)") // Log.d("AirPods Service", "Socket closed (bytesRead = -1)")
this@OldAirPodsService.stopForeground(STOP_FOREGROUND_REMOVE) // this@OldAirPodsService.stopForeground(STOP_FOREGROUND_REMOVE)
socket?.close() // socket?.close()
sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED)) // sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED))
return@launch // return@launch
} // }
var inEar = false // var inEar = false
var inEarData = listOf<Boolean>() // var inEarData = listOf<Boolean>()
if (earDetectionNotification.isEarDetectionData(data)) { // if (earDetectionNotification.isEarDetectionData(data)) {
earDetectionNotification.setStatus(data) // earDetectionNotification.setStatus(data)
sendBroadcast(Intent(AirPodsNotifications.EAR_DETECTION_DATA).apply { // sendBroadcast(Intent(AirPodsNotifications.EAR_DETECTION_DATA).apply {
val list = earDetectionNotification.status // val list = earDetectionNotification.status
val bytes = ByteArray(2) // val bytes = ByteArray(2)
bytes[0] = list[0] // bytes[0] = list[0]
bytes[1] = list[1] // bytes[1] = list[1]
putExtra("data", bytes) // putExtra("data", bytes)
}) // })
Log.d("AirPods Parser", "Ear Detection: ${earDetectionNotification.status[0]} ${earDetectionNotification.status[1]}") // Log.d("AirPods Parser", "Ear Detection: ${earDetectionNotification.status[0]} ${earDetectionNotification.status[1]}")
var justEnabledA2dp = false // var justEnabledA2dp = false
val earReceiver = object : BroadcastReceiver() { // val earReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { // override fun onReceive(context: Context, intent: Intent) {
val data = intent.getByteArrayExtra("data") // val data = intent.getByteArrayExtra("data")
if (data != null && earDetectionEnabled) { // if (data != null && earDetectionEnabled) {
inEar = if (data.find { it == 0x02.toByte() } != null || data.find { it == 0x03.toByte() } != null) { // inEar = if (data.find { it == 0x02.toByte() } != null || data.find { it == 0x03.toByte() } != null) {
data[0] == 0x00.toByte() || data[1] == 0x00.toByte() // data[0] == 0x00.toByte() || data[1] == 0x00.toByte()
} else { // } else {
data[0] == 0x00.toByte() && data[1] == 0x00.toByte() // data[0] == 0x00.toByte() && data[1] == 0x00.toByte()
} // }
//
val newInEarData = listOf(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)) { // if (newInEarData.contains(true) && inEarData == listOf(false, false)) {
connectAudio(this@OldAirPodsService, device) // connectAudio(this@OldAirPodsService, device)
justEnabledA2dp = true // justEnabledA2dp = true
val bluetoothAdapter = this@OldAirPodsService.getSystemService(BluetoothManager::class.java).adapter // val bluetoothAdapter = this@OldAirPodsService.getSystemService(BluetoothManager::class.java).adapter
bluetoothAdapter.getProfileProxy( // bluetoothAdapter.getProfileProxy(
this@OldAirPodsService, object : BluetoothProfile.ServiceListener { // this@OldAirPodsService, object : BluetoothProfile.ServiceListener {
override fun onServiceConnected( // override fun onServiceConnected(
profile: Int, // profile: Int,
proxy: BluetoothProfile // proxy: BluetoothProfile
) { // ) {
if (profile == BluetoothProfile.A2DP) { // if (profile == BluetoothProfile.A2DP) {
val connectedDevices = // val connectedDevices =
proxy.connectedDevices // proxy.connectedDevices
if (connectedDevices.isNotEmpty()) { // if (connectedDevices.isNotEmpty()) {
MediaController.sendPlay() // MediaController.sendPlay()
} // }
} // }
bluetoothAdapter.closeProfileProxy( // bluetoothAdapter.closeProfileProxy(
profile, // profile,
proxy // proxy
) // )
} // }
//
override fun onServiceDisconnected( // override fun onServiceDisconnected(
profile: Int // profile: Int
) { // ) {
} // }
} // }
,BluetoothProfile.A2DP // ,BluetoothProfile.A2DP
) // )
//
} // }
else if (newInEarData == listOf(false, false)){ // else if (newInEarData == listOf(false, false)){
disconnectAudio(this@OldAirPodsService, device) // disconnectAudio(this@OldAirPodsService, device)
} // }
//
inEarData = newInEarData // inEarData = newInEarData
//
if (inEar == true) { // if (inEar == true) {
if (!justEnabledA2dp) { // if (!justEnabledA2dp) {
justEnabledA2dp = false // justEnabledA2dp = false
MediaController.sendPlay() // MediaController.sendPlay()
} // }
} else { // } else {
MediaController.sendPause() // MediaController.sendPause()
} // }
} // }
} // }
} // }
//
val earIntentFilter = IntentFilter(AirPodsNotifications.EAR_DETECTION_DATA) // val earIntentFilter = IntentFilter(AirPodsNotifications.EAR_DETECTION_DATA)
this@OldAirPodsService.registerReceiver(earReceiver, earIntentFilter, // this@OldAirPodsService.registerReceiver(earReceiver, earIntentFilter,
RECEIVER_EXPORTED // RECEIVER_EXPORTED
) // )
} // }
else if (ancNotification.isANCData(data)) { // else if (ancNotification.isANCData(data)) {
ancNotification.setStatus(data) // ancNotification.setStatus(data)
sendBroadcast(Intent(AirPodsNotifications.ANC_DATA).apply { // sendBroadcast(Intent(AirPodsNotifications.ANC_DATA).apply {
putExtra("data", ancNotification.status) // putExtra("data", ancNotification.status)
}) // })
Log.d("AirPods Parser", "ANC: ${ancNotification.status}") // Log.d("AirPods Parser", "ANC: ${ancNotification.status}")
} // }
else if (batteryNotification.isBatteryData(data)) { // else if (batteryNotification.isBatteryData(data)) {
batteryNotification.setBattery(data) // batteryNotification.setBattery(data)
sendBroadcast(Intent(AirPodsNotifications.BATTERY_DATA).apply { // sendBroadcast(Intent(AirPodsNotifications.BATTERY_DATA).apply {
putParcelableArrayListExtra("data", ArrayList(batteryNotification.getBattery())) // putParcelableArrayListExtra("data", ArrayList(batteryNotification.getBattery()))
}) // })
for (battery in batteryNotification.getBattery()) { // for (battery in batteryNotification.getBattery()) {
Log.d("AirPods Parser", "${battery.getComponentName()}: ${battery.getStatusName()} at ${battery.level}% ") // Log.d("AirPods Parser", "${battery.getComponentName()}: ${battery.getStatusName()} at ${battery.level}% ")
} // }
// if both are charging, disconnect audio profiles //// if both are charging, disconnect audio profiles
if (batteryNotification.getBattery()[0].status == 1 && batteryNotification.getBattery()[1].status == 1) { // if (batteryNotification.getBattery()[0].status == 1 && batteryNotification.getBattery()[1].status == 1) {
disconnectAudio(this@OldAirPodsService, device) // disconnectAudio(this@OldAirPodsService, device)
} // }
else { // else {
connectAudio(this@OldAirPodsService, device) // connectAudio(this@OldAirPodsService, device)
} // }
// updatePodsStatus(device!!, batteryNotification.getBattery()) //// updatePodsStatus(device!!, batteryNotification.getBattery())
} // }
else if (conversationAwarenessNotification.isConversationalAwarenessData(data)) { // else if (conversationAwarenessNotification.isConversationalAwarenessData(data)) {
conversationAwarenessNotification.setData(data) // conversationAwarenessNotification.setData(data)
sendBroadcast(Intent(AirPodsNotifications.CA_DATA).apply { // sendBroadcast(Intent(AirPodsNotifications.CA_DATA).apply {
putExtra("data", conversationAwarenessNotification.status) // putExtra("data", conversationAwarenessNotification.status)
}) // })
//
//
if (conversationAwarenessNotification.status == 1.toByte() || conversationAwarenessNotification.status == 2.toByte()) { // if (conversationAwarenessNotification.status == 1.toByte() || conversationAwarenessNotification.status == 2.toByte()) {
MediaController.startSpeaking() // MediaController.startSpeaking()
} else if (conversationAwarenessNotification.status == 8.toByte() || conversationAwarenessNotification.status == 9.toByte()) { // } else if (conversationAwarenessNotification.status == 8.toByte() || conversationAwarenessNotification.status == 9.toByte()) {
MediaController.stopSpeaking() // MediaController.stopSpeaking()
} // }
//
Log.d("AirPods Parser", "Conversation Awareness: ${conversationAwarenessNotification.status}") // Log.d("AirPods Parser", "Conversation Awareness: ${conversationAwarenessNotification.status}")
} // }
else { } // else { }
} // }
} // }
Log.d("AirPods Service", "Socket closed") // Log.d("AirPods Service", "Socket closed")
isConnected = false // isConnected = false
this@OldAirPodsService.stopForeground(STOP_FOREGROUND_REMOVE) // this@OldAirPodsService.stopForeground(STOP_FOREGROUND_REMOVE)
socket?.close() // socket?.close()
sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED)) // sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED))
} // }
} // }
} // }
catch (e: Exception) { // catch (e: Exception) {
Log.e("AirPodsSettingsScreen", "Error connecting to device: ${e.message}") // Log.e("AirPodsSettingsScreen", "Error connecting to device: ${e.message}")
} // }
return START_STICKY // return START_STICKY
} // }
//
override fun onDestroy() { // override fun onDestroy() {
super.onDestroy() // super.onDestroy()
socket?.close() // socket?.close()
isConnected = false // isConnected = false
ServiceManager.setService(null) // ServiceManager.setService(null)
} // }
//
fun setPVEnabled(enabled: Boolean) { // fun setPVEnabled(enabled: Boolean) {
var hex = "04 00 04 00 09 00 26 ${if (enabled) "01" else "02"} 00 00 00" // 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() // var bytes = hex.split(" ").map { it.toInt(16).toByte() }.toByteArray()
socket?.outputStream?.write(bytes) // 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" // 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() // bytes = hex.split(" ").map { it.toInt(16).toByte() }.toByteArray()
socket?.outputStream?.write(bytes) // socket?.outputStream?.write(bytes)
} // }
//
fun setLoudSoundReduction(enabled: Boolean) { // fun setLoudSoundReduction(enabled: Boolean) {
val hex = "52 1B 00 0${if (enabled) "1" else "0"}" // val hex = "52 1B 00 0${if (enabled) "1" else "0"}"
val bytes = hex.split(" ").map { it.toInt(16).toByte() }.toByteArray() // val bytes = hex.split(" ").map { it.toInt(16).toByte() }.toByteArray()
socket?.outputStream?.write(bytes) // socket?.outputStream?.write(bytes)
} // }
} //}

View File

@@ -1,5 +1,4 @@
<resources> <resources>
<string name="app_name">ALN</string> <string name="app_name">ALN</string>
<string name="title_activity_debug">DebugActivity</string> <string name="title_activity_custom_device">GATT Testing</string>
<string name="title_activity_custom_device">CustomDevice</string>
</resources> </resources>