mirror of
https://github.com/kavishdevar/librepods.git
synced 2026-01-28 22:01:50 +00:00
android(fix): do not require phone's MAC for service start (#253)
This makes the app run without issues on OxygenOS/ColorOS16 without root. * android(fix): add missing HEAD_GESTURES capability on app2 * android(fix): catch att initial read exceptions in toggle * android(refactor): remove navcontroller from head gestures screen * android(fix): do not crash when connected devices list is sent empty had never seen this before, this was the first time airpods saying zero connected devices * android(fix): do not crash if phone's MAC not available also removed crossdevice code * android: skip sdp hook check if setup skipped
This commit is contained in:
@@ -381,7 +381,7 @@ fun Main() {
|
|||||||
TroubleshootingScreen(navController)
|
TroubleshootingScreen(navController)
|
||||||
}
|
}
|
||||||
composable("head_tracking") {
|
composable("head_tracking") {
|
||||||
HeadTrackingScreen(navController)
|
HeadTrackingScreen()
|
||||||
}
|
}
|
||||||
composable("onboarding") {
|
composable("onboarding") {
|
||||||
Onboarding(navController, context)
|
Onboarding(navController, context)
|
||||||
|
|||||||
@@ -472,7 +472,12 @@ fun StyledToggle(
|
|||||||
val attManager = ServiceManager.getService()?.attManager ?: return
|
val attManager = ServiceManager.getService()?.attManager ?: return
|
||||||
val isDarkTheme = isSystemInDarkTheme()
|
val isDarkTheme = isSystemInDarkTheme()
|
||||||
val textColor = if (isDarkTheme) Color.White else Color.Black
|
val textColor = if (isDarkTheme) Color.White else Color.Black
|
||||||
val checkedValue = attManager.read(attHandle).getOrNull(0)?.toInt()
|
val checkedValue = try {
|
||||||
|
attManager.read(attHandle).getOrNull(0)?.toInt()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w("StyledToggle", "Error reading initial value for $label: ${e.message}")
|
||||||
|
null
|
||||||
|
} ?: 0
|
||||||
var checked by remember { mutableStateOf(checkedValue !=0) }
|
var checked by remember { mutableStateOf(checkedValue !=0) }
|
||||||
var backgroundColor by remember { mutableStateOf(if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)) }
|
var backgroundColor by remember { mutableStateOf(if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)) }
|
||||||
val animatedBackgroundColor by animateColorAsState(targetValue = backgroundColor, animationSpec = tween(durationMillis = 500))
|
val animatedBackgroundColor by animateColorAsState(targetValue = backgroundColor, animationSpec = tween(durationMillis = 500))
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// this is absolutely unnecessary, why did I make this. a simple toggle would've sufficed
|
||||||
|
|
||||||
@file:OptIn(ExperimentalEncodingApi::class)
|
@file:OptIn(ExperimentalEncodingApi::class)
|
||||||
|
|
||||||
package me.kavishdevar.librepods.screens
|
package me.kavishdevar.librepods.screens
|
||||||
@@ -83,7 +86,6 @@ import androidx.compose.ui.text.style.TextAlign
|
|||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.navigation.NavController
|
|
||||||
import com.kyant.backdrop.backdrops.layerBackdrop
|
import com.kyant.backdrop.backdrops.layerBackdrop
|
||||||
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
|
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
|
||||||
import dev.chrisbanes.haze.hazeSource
|
import dev.chrisbanes.haze.hazeSource
|
||||||
@@ -108,7 +110,7 @@ import kotlin.random.Random
|
|||||||
@RequiresApi(Build.VERSION_CODES.Q)
|
@RequiresApi(Build.VERSION_CODES.Q)
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun HeadTrackingScreen(navController: NavController) {
|
fun HeadTrackingScreen() {
|
||||||
DisposableEffect(Unit) {
|
DisposableEffect(Unit) {
|
||||||
ServiceManager.getService()?.startHeadTracking()
|
ServiceManager.getService()?.startHeadTracking()
|
||||||
onDispose {
|
onDispose {
|
||||||
@@ -743,5 +745,5 @@ private fun AccelerationPlot() {
|
|||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun HeadTrackingScreenPreview() {
|
fun HeadTrackingScreenPreview() {
|
||||||
HeadTrackingScreen(navController = NavController(LocalContext.current))
|
HeadTrackingScreen()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,8 +93,8 @@ import me.kavishdevar.librepods.utils.AirPodsInstance
|
|||||||
import me.kavishdevar.librepods.utils.AirPodsModels
|
import me.kavishdevar.librepods.utils.AirPodsModels
|
||||||
import me.kavishdevar.librepods.utils.BLEManager
|
import me.kavishdevar.librepods.utils.BLEManager
|
||||||
import me.kavishdevar.librepods.utils.BluetoothConnectionManager
|
import me.kavishdevar.librepods.utils.BluetoothConnectionManager
|
||||||
import me.kavishdevar.librepods.utils.CrossDevice
|
//import me.kavishdevar.librepods.utils.CrossDevice
|
||||||
import me.kavishdevar.librepods.utils.CrossDevicePackets
|
//import me.kavishdevar.librepods.utils.CrossDevicePackets
|
||||||
import me.kavishdevar.librepods.utils.GestureDetector
|
import me.kavishdevar.librepods.utils.GestureDetector
|
||||||
import me.kavishdevar.librepods.utils.HeadTracking
|
import me.kavishdevar.librepods.utils.HeadTracking
|
||||||
import me.kavishdevar.librepods.utils.IslandType
|
import me.kavishdevar.librepods.utils.IslandType
|
||||||
@@ -193,7 +193,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
var leftLongPressAction: StemAction = StemAction.defaultActions[StemPressType.LONG_PRESS]!!,
|
var leftLongPressAction: StemAction = StemAction.defaultActions[StemPressType.LONG_PRESS]!!,
|
||||||
var rightLongPressAction: StemAction = StemAction.defaultActions[StemPressType.LONG_PRESS]!!,
|
var rightLongPressAction: StemAction = StemAction.defaultActions[StemPressType.LONG_PRESS]!!,
|
||||||
|
|
||||||
var cameraAction: AACPManager.Companion.StemPressType? = null,
|
var cameraAction: StemPressType? = null,
|
||||||
|
|
||||||
// AirPods device information
|
// AirPods device information
|
||||||
var airpodsName: String = "",
|
var airpodsName: String = "",
|
||||||
@@ -207,6 +207,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
var airpodsVersion3: String = "",
|
var airpodsVersion3: String = "",
|
||||||
var airpodsHardwareRevision: String = "",
|
var airpodsHardwareRevision: String = "",
|
||||||
var airpodsUpdaterIdentifier: String = "",
|
var airpodsUpdaterIdentifier: String = "",
|
||||||
|
|
||||||
|
// phone's mac, needed for tipi
|
||||||
|
var selfMacAddress: String = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
private lateinit var config: ServiceConfig
|
private lateinit var config: ServiceConfig
|
||||||
@@ -368,9 +371,29 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
|
|
||||||
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
|
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
|
||||||
|
|
||||||
val process = Runtime.getRuntime().exec(arrayOf("su", "-c", "settings", "get", "secure", "bluetooth_address"))
|
localMac = config.selfMacAddress
|
||||||
val output = process.inputStream.bufferedReader().use { it.readLine() }
|
if (localMac.isEmpty()) {
|
||||||
localMac = output.trim()
|
localMac = try {
|
||||||
|
val process = Runtime.getRuntime().exec(
|
||||||
|
arrayOf("su", "-c", "settings get secure bluetooth_address")
|
||||||
|
)
|
||||||
|
|
||||||
|
val exitCode = process.waitFor()
|
||||||
|
|
||||||
|
if (exitCode == 0) {
|
||||||
|
process.inputStream.bufferedReader().use { it.readLine()?.trim().orEmpty() }
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error retrieving local MAC address: ${e.message}. We probably aren't rooted.")
|
||||||
|
""
|
||||||
|
}
|
||||||
|
config.selfMacAddress = localMac
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putString("self_mac_address", localMac)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ServiceManager.setService(this)
|
ServiceManager.setService(this)
|
||||||
startForegroundNotification()
|
startForegroundNotification()
|
||||||
@@ -556,11 +579,11 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
MODE_PRIVATE
|
MODE_PRIVATE
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
Log.d(TAG, "Initializing CrossDevice")
|
// Log.d(TAG, "Initializing CrossDevice")
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
// CoroutineScope(Dispatchers.IO).launch {
|
||||||
CrossDevice.init(this@AirPodsService)
|
// CrossDevice.init(this@AirPodsService)
|
||||||
Log.d(TAG, "CrossDevice initialized")
|
// Log.d(TAG, "CrossDevice initialized")
|
||||||
}
|
// }
|
||||||
|
|
||||||
sharedPreferences = getSharedPreferences("settings", MODE_PRIVATE)
|
sharedPreferences = getSharedPreferences("settings", MODE_PRIVATE)
|
||||||
macAddress = sharedPreferences.getString("mac_address", "") ?: ""
|
macAddress = sharedPreferences.getString("mac_address", "") ?: ""
|
||||||
@@ -573,7 +596,8 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
when (state) {
|
when (state) {
|
||||||
TelephonyManager.CALL_STATE_RINGING -> {
|
TelephonyManager.CALL_STATE_RINGING -> {
|
||||||
val leAvailableForAudio = bleManager.getMostRecentStatus()?.isLeftInEar == true || bleManager.getMostRecentStatus()?.isRightInEar == true
|
val leAvailableForAudio = bleManager.getMostRecentStatus()?.isLeftInEar == true || bleManager.getMostRecentStatus()?.isRightInEar == true
|
||||||
if ((CrossDevice.isAvailable && !isConnectedLocally && earDetectionNotification.status.contains(0x00)) || leAvailableForAudio) CoroutineScope(Dispatchers.IO).launch {
|
// if ((CrossDevice.isAvailable && !isConnectedLocally && earDetectionNotification.status.contains(0x00)) || leAvailableForAudio) CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
if (leAvailableForAudio) runBlocking {
|
||||||
takeOver("call")
|
takeOver("call")
|
||||||
}
|
}
|
||||||
if (config.headGestures) {
|
if (config.headGestures) {
|
||||||
@@ -583,7 +607,8 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
}
|
}
|
||||||
TelephonyManager.CALL_STATE_OFFHOOK -> {
|
TelephonyManager.CALL_STATE_OFFHOOK -> {
|
||||||
val leAvailableForAudio = bleManager.getMostRecentStatus()?.isLeftInEar == true || bleManager.getMostRecentStatus()?.isRightInEar == true
|
val leAvailableForAudio = bleManager.getMostRecentStatus()?.isLeftInEar == true || bleManager.getMostRecentStatus()?.isRightInEar == true
|
||||||
if ((CrossDevice.isAvailable && !isConnectedLocally && earDetectionNotification.status.contains(0x00)) || leAvailableForAudio) CoroutineScope(
|
// if ((CrossDevice.isAvailable && !isConnectedLocally && earDetectionNotification.status.contains(0x00)) || leAvailableForAudio) CoroutineScope(
|
||||||
|
if (leAvailableForAudio) CoroutineScope(
|
||||||
Dispatchers.IO).launch {
|
Dispatchers.IO).launch {
|
||||||
takeOver("call")
|
takeOver("call")
|
||||||
}
|
}
|
||||||
@@ -641,8 +666,8 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
sharedPreferences.edit { putString("name", config.deviceName) }
|
sharedPreferences.edit { putString("name", config.deviceName) }
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d("AirPodsCrossDevice", CrossDevice.isAvailable.toString())
|
// Log.d("AirPodsCrossDevice", CrossDevice.isAvailable.toString())
|
||||||
if (!CrossDevice.isAvailable) {
|
// if (!CrossDevice.isAvailable) {
|
||||||
Log.d(TAG, "${config.deviceName} connected")
|
Log.d(TAG, "${config.deviceName} connected")
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
connectToSocket(device!!)
|
connectToSocket(device!!)
|
||||||
@@ -654,7 +679,8 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
sharedPreferences.edit {
|
sharedPreferences.edit {
|
||||||
putString("mac_address", macAddress)
|
putString("mac_address", macAddress)
|
||||||
}
|
}
|
||||||
}
|
// }
|
||||||
|
|
||||||
} else if (intent?.action == AirPodsNotifications.AIRPODS_DISCONNECTED) {
|
} else if (intent?.action == AirPodsNotifications.AIRPODS_DISCONNECTED) {
|
||||||
device = null
|
device = null
|
||||||
isConnectedLocally = false
|
isConnectedLocally = false
|
||||||
@@ -719,7 +745,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
if (profile == BluetoothProfile.A2DP) {
|
if (profile == BluetoothProfile.A2DP) {
|
||||||
val connectedDevices = proxy.connectedDevices
|
val connectedDevices = proxy.connectedDevices
|
||||||
if (connectedDevices.isNotEmpty()) {
|
if (connectedDevices.isNotEmpty()) {
|
||||||
if (!CrossDevice.isAvailable) {
|
// if (!CrossDevice.isAvailable) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
connectToSocket(device)
|
connectToSocket(device)
|
||||||
}
|
}
|
||||||
@@ -728,7 +754,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
sharedPreferences.edit {
|
sharedPreferences.edit {
|
||||||
putString("mac_address", macAddress)
|
putString("mac_address", macAddress)
|
||||||
}
|
}
|
||||||
}
|
// }
|
||||||
this@AirPodsService.sendBroadcast(
|
this@AirPodsService.sendBroadcast(
|
||||||
Intent(AirPodsNotifications.AIRPODS_CONNECTED)
|
Intent(AirPodsNotifications.AIRPODS_CONNECTED)
|
||||||
)
|
)
|
||||||
@@ -745,9 +771,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isConnectedLocally && !CrossDevice.isAvailable) {
|
// if (!isConnectedLocally && !CrossDevice.isAvailable) {
|
||||||
clearPacketLogs()
|
// clearPacketLogs()
|
||||||
}
|
// }
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
bleManager.startScanning()
|
bleManager.startScanning()
|
||||||
@@ -819,8 +845,8 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
.getString("name", device?.name),
|
.getString("name", device?.name),
|
||||||
batteryNotification.getBattery()
|
batteryNotification.getBattery()
|
||||||
)
|
)
|
||||||
CrossDevice.sendRemotePacket(batteryInfo)
|
// CrossDevice.sendRemotePacket(batteryInfo)
|
||||||
CrossDevice.batteryBytes = batteryInfo
|
// CrossDevice.batteryBytes = batteryInfo
|
||||||
|
|
||||||
for (battery in batteryNotification.getBattery()) {
|
for (battery in batteryNotification.getBattery()) {
|
||||||
Log.d(
|
Log.d(
|
||||||
@@ -1229,7 +1255,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
leftLongPressAction = StemAction.fromString(sharedPreferences.getString("left_long_press_action", "CYCLE_NOISE_CONTROL_MODES") ?: "CYCLE_NOISE_CONTROL_MODES")!!,
|
leftLongPressAction = StemAction.fromString(sharedPreferences.getString("left_long_press_action", "CYCLE_NOISE_CONTROL_MODES") ?: "CYCLE_NOISE_CONTROL_MODES")!!,
|
||||||
rightLongPressAction = StemAction.fromString(sharedPreferences.getString("right_long_press_action", "DIGITAL_ASSISTANT") ?: "DIGITAL_ASSISTANT")!!,
|
rightLongPressAction = StemAction.fromString(sharedPreferences.getString("right_long_press_action", "DIGITAL_ASSISTANT") ?: "DIGITAL_ASSISTANT")!!,
|
||||||
|
|
||||||
cameraAction = sharedPreferences.getString("camera_action", null)?.let { AACPManager.Companion.StemPressType.valueOf(it) },
|
cameraAction = sharedPreferences.getString("camera_action", null)?.let { StemPressType.valueOf(it) },
|
||||||
|
|
||||||
// AirPods device information
|
// AirPods device information
|
||||||
airpodsName = sharedPreferences.getString("airpods_name", "") ?: "",
|
airpodsName = sharedPreferences.getString("airpods_name", "") ?: "",
|
||||||
@@ -1243,6 +1269,8 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
airpodsVersion3 = sharedPreferences.getString("airpods_version3", "") ?: "",
|
airpodsVersion3 = sharedPreferences.getString("airpods_version3", "") ?: "",
|
||||||
airpodsHardwareRevision = sharedPreferences.getString("airpods_hardware_revision", "") ?: "",
|
airpodsHardwareRevision = sharedPreferences.getString("airpods_hardware_revision", "") ?: "",
|
||||||
airpodsUpdaterIdentifier = sharedPreferences.getString("airpods_updater_identifier", "") ?: "",
|
airpodsUpdaterIdentifier = sharedPreferences.getString("airpods_updater_identifier", "") ?: "",
|
||||||
|
|
||||||
|
selfMacAddress = sharedPreferences.getString("self_mac_address", "") ?: ""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1251,6 +1279,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
|
|
||||||
when(key) {
|
when(key) {
|
||||||
"name" -> config.deviceName = preferences.getString(key, "AirPods") ?: "AirPods"
|
"name" -> config.deviceName = preferences.getString(key, "AirPods") ?: "AirPods"
|
||||||
|
"mac_address" -> macAddress = preferences.getString(key, "") ?: ""
|
||||||
"automatic_ear_detection" -> config.earDetectionEnabled = preferences.getBoolean(key, true)
|
"automatic_ear_detection" -> config.earDetectionEnabled = preferences.getBoolean(key, true)
|
||||||
"conversational_awareness_pause_music" -> config.conversationalAwarenessPauseMusic = preferences.getBoolean(key, false)
|
"conversational_awareness_pause_music" -> config.conversationalAwarenessPauseMusic = preferences.getBoolean(key, false)
|
||||||
"show_phone_battery_in_widget" -> {
|
"show_phone_battery_in_widget" -> {
|
||||||
@@ -1323,7 +1352,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
)!!
|
)!!
|
||||||
setupStemActions()
|
setupStemActions()
|
||||||
}
|
}
|
||||||
"camera_action" -> config.cameraAction = preferences.getString(key, null)?.let { AACPManager.Companion.StemPressType.valueOf(it) }
|
"camera_action" -> config.cameraAction = preferences.getString(key, null)?.let { StemPressType.valueOf(it) }
|
||||||
|
|
||||||
// AirPods device information
|
// AirPods device information
|
||||||
"airpods_name" -> config.airpodsName = preferences.getString(key, "") ?: ""
|
"airpods_name" -> config.airpodsName = preferences.getString(key, "") ?: ""
|
||||||
@@ -1337,10 +1366,8 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
"airpods_version3" -> config.airpodsVersion3 = preferences.getString(key, "") ?: ""
|
"airpods_version3" -> config.airpodsVersion3 = preferences.getString(key, "") ?: ""
|
||||||
"airpods_hardware_revision" -> config.airpodsHardwareRevision = preferences.getString(key, "") ?: ""
|
"airpods_hardware_revision" -> config.airpodsHardwareRevision = preferences.getString(key, "") ?: ""
|
||||||
"airpods_updater_identifier" -> config.airpodsUpdaterIdentifier = preferences.getString(key, "") ?: ""
|
"airpods_updater_identifier" -> config.airpodsUpdaterIdentifier = preferences.getString(key, "") ?: ""
|
||||||
}
|
|
||||||
|
|
||||||
if (key == "mac_address") {
|
"self_mac_address" -> config.selfMacAddress = preferences.getString(key, "") ?: ""
|
||||||
macAddress = preferences.getString(key, "") ?: ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2096,7 +2123,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
SystemApisUtils.setMetadata(
|
SystemApisUtils.setMetadata(
|
||||||
device,
|
device,
|
||||||
device.METADATA_COMPANION_APP,
|
device.METADATA_COMPANION_APP,
|
||||||
"me.kavisdevar.librepods".toByteArray()
|
"me.kavishdevar.librepods".toByteArray()
|
||||||
) &&
|
) &&
|
||||||
SystemApisUtils.setMetadata(
|
SystemApisUtils.setMetadata(
|
||||||
device,
|
device,
|
||||||
@@ -2266,14 +2293,19 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CrossDevice.isAvailable) {
|
// if (CrossDevice.isAvailable) {
|
||||||
Log.d(TAG, "CrossDevice is available, continuing")
|
// Log.d(TAG, "CrossDevice is available, continuing")
|
||||||
}
|
// }
|
||||||
else if (bleManager.getMostRecentStatus()?.isLeftInEar == true || bleManager.getMostRecentStatus()?.isRightInEar == true) {
|
// else if (bleManager.getMostRecentStatus()?.isLeftInEar == true || bleManager.getMostRecentStatus()?.isRightInEar == true) {
|
||||||
Log.d(TAG, "At least one AirPod is in ear, continuing")
|
// Log.d(TAG, "At least one AirPod is in ear, continuing")
|
||||||
}
|
// }
|
||||||
else {
|
// else {
|
||||||
Log.d(TAG, "CrossDevice not available and AirPods not in ear, skipping")
|
// Log.d(TAG, "CrossDevice not available and AirPods not in ear, skipping")
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (bleManager.getMostRecentStatus()?.isLeftInEar == false && bleManager.getMostRecentStatus()?.isRightInEar == false) {
|
||||||
|
Log.d(TAG, "Both AirPods are out of ear, not taking over audio")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2312,10 +2344,10 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
}
|
}
|
||||||
|
|
||||||
Log.d(TAG, "Taking over audio")
|
Log.d(TAG, "Taking over audio")
|
||||||
CrossDevice.sendRemotePacket(CrossDevicePackets.REQUEST_DISCONNECT.packet)
|
// CrossDevice.sendRemotePacket(CrossDevicePackets.REQUEST_DISCONNECT.packet)
|
||||||
Log.d(TAG, macAddress)
|
Log.d(TAG, macAddress)
|
||||||
|
|
||||||
sharedPreferences.edit { putBoolean("CrossDeviceIsAvailable", false) }
|
// sharedPreferences.edit { putBoolean("CrossDeviceIsAvailable", false) }
|
||||||
device = getSystemService(BluetoothManager::class.java).adapter.bondedDevices.find {
|
device = getSystemService(BluetoothManager::class.java).adapter.bondedDevices.find {
|
||||||
it.address == macAddress
|
it.address == macAddress
|
||||||
}
|
}
|
||||||
@@ -2340,7 +2372,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
showIsland(this, batteryNotification.getBattery().find { it.component == BatteryComponent.LEFT}?.level!!.coerceAtMost(batteryNotification.getBattery().find { it.component == BatteryComponent.RIGHT}?.level!!),
|
showIsland(this, batteryNotification.getBattery().find { it.component == BatteryComponent.LEFT}?.level!!.coerceAtMost(batteryNotification.getBattery().find { it.component == BatteryComponent.RIGHT}?.level!!),
|
||||||
IslandType.TAKING_OVER)
|
IslandType.TAKING_OVER)
|
||||||
|
|
||||||
CrossDevice.isAvailable = false
|
// CrossDevice.isAvailable = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createBluetoothSocket(device: BluetoothDevice, uuid: ParcelUuid): BluetoothSocket {
|
private fun createBluetoothSocket(device: BluetoothDevice, uuid: ParcelUuid): BluetoothSocket {
|
||||||
@@ -2385,7 +2417,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
Log.d(TAG, "<LogCollector:Start> Connecting to socket")
|
Log.d(TAG, "<LogCollector:Start> Connecting to socket")
|
||||||
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")
|
||||||
if (!isConnectedLocally && !CrossDevice.isAvailable) {
|
if (!isConnectedLocally) {
|
||||||
socket = try {
|
socket = try {
|
||||||
createBluetoothSocket(device, uuid)
|
createBluetoothSocket(device, uuid)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -2503,7 +2535,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
})
|
})
|
||||||
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) }
|
||||||
CrossDevice.sendReceivedPacket(bytes)
|
// CrossDevice.sendReceivedPacket(bytes)
|
||||||
updateNotificationContent(
|
updateNotificationContent(
|
||||||
true,
|
true,
|
||||||
sharedPreferences.getString("name", device.name),
|
sharedPreferences.getString("name", device.name),
|
||||||
@@ -2541,6 +2573,8 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
this@AirPodsService.device = device
|
this@AirPodsService.device = device
|
||||||
updateNotificationContent(false)
|
updateNotificationContent(false)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Already connected locally, skipping socket connection (isConnectedLocally = $isConnectedLocally, socket.isConnected = ${this::socket.isInitialized && socket.isConnected})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2566,7 +2600,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
override fun onServiceDisconnected(profile: Int) {}
|
override fun onServiceDisconnected(profile: Int) {}
|
||||||
}, BluetoothProfile.A2DP)
|
}, BluetoothProfile.A2DP)
|
||||||
isConnectedLocally = false
|
isConnectedLocally = false
|
||||||
CrossDevice.isAvailable = true
|
// CrossDevice.isAvailable = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun disconnectAirPods() {
|
fun disconnectAirPods() {
|
||||||
@@ -2611,16 +2645,16 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getBattery(): List<Battery> {
|
fun getBattery(): List<Battery> {
|
||||||
if (!isConnectedLocally && CrossDevice.isAvailable) {
|
// if (!isConnectedLocally && CrossDevice.isAvailable) {
|
||||||
batteryNotification.setBattery(CrossDevice.batteryBytes)
|
// batteryNotification.setBattery(CrossDevice.batteryBytes)
|
||||||
}
|
// }
|
||||||
return batteryNotification.getBattery()
|
return batteryNotification.getBattery()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getANC(): Int {
|
fun getANC(): Int {
|
||||||
if (!isConnectedLocally && CrossDevice.isAvailable) {
|
// if (!isConnectedLocally && CrossDevice.isAvailable) {
|
||||||
ancNotification.setStatus(CrossDevice.ancBytes)
|
// ancNotification.setStatus(CrossDevice.ancBytes)
|
||||||
}
|
// }
|
||||||
return ancNotification.status
|
return ancNotification.status
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2761,7 +2795,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
}
|
}
|
||||||
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
|
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
|
||||||
isConnectedLocally = false
|
isConnectedLocally = false
|
||||||
CrossDevice.isAvailable = true
|
// CrossDevice.isAvailable = true
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -596,7 +596,7 @@ class AACPManager {
|
|||||||
eqData = FloatArray(8) { i -> eq1.get(i) }
|
eqData = FloatArray(8) { i -> eq1.get(i) }
|
||||||
Log.d(TAG, "EQ Data set to: ${eqData.toList()}, eqOnPhone: $eqOnPhone, eqOnMedia: $eqOnMedia")
|
Log.d(TAG, "EQ Data set to: ${eqData.toList()}, eqOnPhone: $eqOnPhone, eqOnMedia: $eqOnMedia")
|
||||||
}
|
}
|
||||||
|
|
||||||
Opcodes.INFORMATION -> {
|
Opcodes.INFORMATION -> {
|
||||||
Log.e(TAG, "Parsing Information Packet")
|
Log.e(TAG, "Parsing Information Packet")
|
||||||
val information = parseInformationPacket(packet)
|
val information = parseInformationPacket(packet)
|
||||||
@@ -1201,7 +1201,8 @@ class AACPManager {
|
|||||||
var offset = 9
|
var offset = 9
|
||||||
for (i in 0 until deviceCount) {
|
for (i in 0 until deviceCount) {
|
||||||
if (offset + 8 > data.size) {
|
if (offset + 8 > data.size) {
|
||||||
throw IllegalArgumentException("Data array too short to parse all connected devices")
|
Log.w(TAG, "Data array too short to parse all connected devices, returning what we have")
|
||||||
|
break
|
||||||
}
|
}
|
||||||
val macBytes = data.sliceArray(offset until offset + 6)
|
val macBytes = data.sliceArray(offset until offset + 6)
|
||||||
val mac = macBytes.joinToString(":") { "%02X".format(it) }
|
val mac = macBytes.joinToString(":") { "%02X".format(it) }
|
||||||
|
|||||||
@@ -149,7 +149,8 @@ class AirPodsPro2Lightning: AirPodsBase(
|
|||||||
Capability.HEARING_AID,
|
Capability.HEARING_AID,
|
||||||
Capability.ADAPTIVE_AUDIO,
|
Capability.ADAPTIVE_AUDIO,
|
||||||
Capability.ADAPTIVE_VOLUME,
|
Capability.ADAPTIVE_VOLUME,
|
||||||
Capability.SWIPE_FOR_VOLUME
|
Capability.SWIPE_FOR_VOLUME,
|
||||||
|
Capability.HEAD_GESTURES
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -171,7 +172,8 @@ class AirPodsPro2USBC: AirPodsBase(
|
|||||||
Capability.HEARING_AID,
|
Capability.HEARING_AID,
|
||||||
Capability.ADAPTIVE_AUDIO,
|
Capability.ADAPTIVE_AUDIO,
|
||||||
Capability.ADAPTIVE_VOLUME,
|
Capability.ADAPTIVE_VOLUME,
|
||||||
Capability.SWIPE_FOR_VOLUME
|
Capability.SWIPE_FOR_VOLUME,
|
||||||
|
Capability.HEAD_GESTURES
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -230,4 +232,4 @@ object AirPodsModels {
|
|||||||
fun getModelByModelNumber(modelNumber: String): AirPodsBase? {
|
fun getModelByModelNumber(modelNumber: String): AirPodsBase? {
|
||||||
return models.find { modelNumber in it.modelNumber }
|
return models.find { modelNumber in it.modelNumber }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,6 +115,11 @@ class RadareOffsetFinder(context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun isSdpOffsetAvailable(): Boolean {
|
fun isSdpOffsetAvailable(): Boolean {
|
||||||
|
val sharedPreferences = ServiceManager.getService()?.applicationContext?.getSharedPreferences("settings", Context.MODE_PRIVATE) // ik not good practice- too lazy
|
||||||
|
if (sharedPreferences?.getBoolean("skip_setup", false) == true) {
|
||||||
|
Log.d(TAG, "Setup skipped, returning true for SDP offset.")
|
||||||
|
return true
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
val process = Runtime.getRuntime().exec(arrayOf("/system/bin/getprop", SDP_OFFSET_PROP))
|
val process = Runtime.getRuntime().exec(arrayOf("/system/bin/getprop", SDP_OFFSET_PROP))
|
||||||
val reader = BufferedReader(InputStreamReader(process.inputStream))
|
val reader = BufferedReader(InputStreamReader(process.inputStream))
|
||||||
@@ -462,7 +467,7 @@ class RadareOffsetFinder(context: Context) {
|
|||||||
// findAndSaveL2cuProcessCfgReqOffset(libraryPath, envSetup)
|
// findAndSaveL2cuProcessCfgReqOffset(libraryPath, envSetup)
|
||||||
// findAndSaveL2cCsmConfigOffset(libraryPath, envSetup)
|
// findAndSaveL2cCsmConfigOffset(libraryPath, envSetup)
|
||||||
// findAndSaveL2cuSendPeerInfoReqOffset(libraryPath, envSetup)
|
// findAndSaveL2cuSendPeerInfoReqOffset(libraryPath, envSetup)
|
||||||
|
|
||||||
// findAndSaveSdpOffset(libraryPath, envSetup) Should not be run by default, only when user asks for it.
|
// findAndSaveSdpOffset(libraryPath, envSetup) Should not be run by default, only when user asks for it.
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
Reference in New Issue
Block a user