Skip to content

Commit fa00620

Browse files
committed
android: clean up a lot of stuff
1 parent aecbb06 commit fa00620

39 files changed

+269
-263
lines changed

android/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@
3535
<uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS"
3636
tools:ignore="ProtectedPermissions" />
3737

38-
<protected-broadcast android:name="batterywidget.impl.action.update_bluetooth_data" />
39-
4038
<application
4139
android:allowBackup="true"
4240
android:dataExtractionRules="@xml/data_extraction_rules"
@@ -95,7 +93,7 @@
9593
<action android:name="android.intent.action.VIEW" />
9694
<category android:name="android.intent.category.DEFAULT" />
9795
<category android:name="android.intent.category.BROWSABLE" />
98-
<data android:scheme="librepods"
96+
<data android:scheme="librepods"
9997
android:host="add-magic-keys" />
10098
</intent-filter> -->
10199
</activity>

android/app/src/main/java/me/kavishdevar/librepods/CustomDeviceActivity.kt

Lines changed: 54 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
11
/*
22
* LibrePods - AirPods liberated from Apple’s ecosystem
3-
*
3+
*
44
* Copyright (C) 2025 LibrePods contributors
5-
*
5+
*
66
* This program is free software: you can redistribute it and/or modify
77
* it under the terms of the GNU Affero General Public License as published
88
* by the Free Software Foundation, either version 3 of the License.
9-
*
9+
*
1010
* This program is distributed in the hope that it will be useful,
1111
* but WITHOUT ANY WARRANTY; without even the implied warranty of
1212
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1313
* GNU Affero General Public License for more details.
14-
*
14+
*
1515
* You should have received a copy of the GNU Affero General Public License
1616
* along with this program. If not, see <https://www.gnu.org/licenses/>.
1717
*/
1818

1919
package me.kavishdevar.librepods
2020

21-
import android.Manifest
2221
import android.annotation.SuppressLint
2322
import android.bluetooth.BluetoothDevice
2423
import android.bluetooth.BluetoothManager
@@ -29,63 +28,53 @@ import android.util.Log
2928
import androidx.activity.ComponentActivity
3029
import androidx.activity.compose.setContent
3130
import androidx.activity.enableEdgeToEdge
32-
import androidx.annotation.RequiresPermission
33-
import androidx.compose.foundation.layout.Arrangement
34-
import androidx.compose.foundation.layout.Column
35-
import androidx.compose.foundation.layout.fillMaxSize
36-
import androidx.compose.foundation.layout.padding
37-
import androidx.compose.foundation.rememberScrollState
38-
import androidx.compose.foundation.verticalScroll
39-
import androidx.compose.runtime.Composable
31+
import androidx.compose.runtime.mutableFloatStateOf
32+
import androidx.compose.runtime.mutableIntStateOf
4033
import androidx.compose.runtime.mutableStateOf
41-
import androidx.compose.runtime.remember
42-
import androidx.compose.ui.Alignment
43-
import androidx.compose.ui.Modifier
44-
import androidx.compose.ui.unit.dp
4534
import androidx.navigation.compose.NavHost
4635
import androidx.navigation.compose.composable
4736
import androidx.navigation.compose.rememberNavController
48-
import me.kavishdevar.librepods.screens.AccessibilitySettingsScreen
49-
import me.kavishdevar.librepods.screens.EqualizerSettingsScreen
50-
import me.kavishdevar.librepods.ui.theme.LibrePodsTheme
51-
import org.lsposed.hiddenapibypass.HiddenApiBypass
5237
import kotlinx.coroutines.CoroutineScope
5338
import kotlinx.coroutines.Dispatchers
5439
import kotlinx.coroutines.Job
5540
import kotlinx.coroutines.delay
5641
import kotlinx.coroutines.launch
5742
import kotlinx.coroutines.withContext
5843
import kotlinx.coroutines.withTimeout
44+
import me.kavishdevar.librepods.screens.AccessibilitySettingsScreen
45+
import me.kavishdevar.librepods.screens.EqualizerSettingsScreen
46+
import me.kavishdevar.librepods.ui.theme.LibrePodsTheme
47+
import org.lsposed.hiddenapibypass.HiddenApiBypass
5948
import java.io.IOException
6049
import java.nio.ByteBuffer
6150
import java.nio.ByteOrder
6251

52+
@Suppress("PrivatePropertyName")
6353
class CustomDevice : ComponentActivity() {
6454
private val TAG = "AirPodsAccessibilitySettings"
6555
private var socket: BluetoothSocket? = null
6656
private val deviceAddress = "28:2D:7F:C2:05:5B"
67-
private val psm = 31
6857
private val uuid: ParcelUuid = ParcelUuid.fromString("00000000-0000-0000-0000-00000000000")
6958

7059
// Data states
7160
private val isConnected = mutableStateOf(false)
72-
private val leftAmplification = mutableStateOf(1.0f)
73-
private val leftTone = mutableStateOf(1.0f)
74-
private val leftAmbientNoiseReduction = mutableStateOf(0.5f)
61+
private val leftAmplification = mutableFloatStateOf(1.0f)
62+
private val leftTone = mutableFloatStateOf(1.0f)
63+
private val leftAmbientNoiseReduction = mutableFloatStateOf(0.5f)
7564
private val leftConversationBoost = mutableStateOf(false)
7665
private val leftEQ = mutableStateOf(FloatArray(8) { 50.0f })
7766

78-
private val rightAmplification = mutableStateOf(1.0f)
79-
private val rightTone = mutableStateOf(1.0f)
80-
private val rightAmbientNoiseReduction = mutableStateOf(0.5f)
67+
private val rightAmplification = mutableFloatStateOf(1.0f)
68+
private val rightTone = mutableFloatStateOf(1.0f)
69+
private val rightAmbientNoiseReduction = mutableFloatStateOf(0.5f)
8170
private val rightConversationBoost = mutableStateOf(false)
8271
private val rightEQ = mutableStateOf(FloatArray(8) { 50.0f })
8372

8473
private val singleMode = mutableStateOf(false)
85-
private val amplification = mutableStateOf(1.0f)
86-
private val balance = mutableStateOf(0.5f)
74+
private val amplification = mutableFloatStateOf(1.0f)
75+
private val balance = mutableFloatStateOf(0.5f)
8776

88-
private val retryCount = mutableStateOf(0)
77+
private val retryCount = mutableIntStateOf(0)
8978
private val showRetryButton = mutableStateOf(false)
9079
private val maxRetries = 3
9180

@@ -146,18 +135,19 @@ class CustomDevice : ComponentActivity() {
146135
socket?.close()
147136
}
148137

138+
@SuppressLint("MissingPermission")
149139
private suspend fun connectL2CAP() {
150-
retryCount.value = 0
140+
retryCount.intValue = 0
151141
// Close any existing socket
152142
socket?.close()
153143
socket = null
154-
while (retryCount.value < maxRetries) {
144+
while (retryCount.intValue < maxRetries) {
155145
try {
156-
Log.d(TAG, "Starting L2CAP connection setup, attempt ${retryCount.value + 1}")
146+
Log.d(TAG, "Starting L2CAP connection setup, attempt ${retryCount.intValue + 1}")
157147
HiddenApiBypass.addHiddenApiExemptions("Landroid/bluetooth/BluetoothSocket;")
158148
val manager = getSystemService(BLUETOOTH_SERVICE) as BluetoothManager
159149
val device: BluetoothDevice = manager.adapter.getRemoteDevice(deviceAddress)
160-
socket = createBluetoothSocket(device, psm)
150+
socket = createBluetoothSocket(device)
161151

162152
withTimeout(5000L) {
163153
socket?.connect()
@@ -177,9 +167,9 @@ class CustomDevice : ComponentActivity() {
177167

178168
return
179169
} catch (e: Exception) {
180-
Log.e(TAG, "Failed to connect, attempt ${retryCount.value + 1}: ${e.message}")
181-
retryCount.value++
182-
if (retryCount.value < maxRetries) {
170+
Log.e(TAG, "Failed to connect, attempt ${retryCount.intValue + 1}: ${e.message}")
171+
retryCount.intValue++
172+
if (retryCount.intValue < maxRetries) {
183173
delay(2000) // Wait 2 seconds before retry
184174
}
185175
}
@@ -193,7 +183,7 @@ class CustomDevice : ComponentActivity() {
193183
}
194184
}
195185

196-
private fun createBluetoothSocket(device: BluetoothDevice, psm: Int): BluetoothSocket {
186+
private fun createBluetoothSocket(device: BluetoothDevice): BluetoothSocket {
197187
val type = 3 // L2CAP
198188
val constructorSpecs = listOf(
199189
arrayOf(device, type, true, true, 31, uuid),
@@ -300,18 +290,18 @@ class CustomDevice : ComponentActivity() {
300290
leftEQ.value = newLeftEQ
301291
if (singleMode.value) rightEQ.value = newLeftEQ
302292

303-
leftAmplification.value = buffer.float
304-
Log.d(TAG, "Parsed left amplification: ${leftAmplification.value}")
305-
leftTone.value = buffer.float
306-
Log.d(TAG, "Parsed left tone: ${leftTone.value}")
307-
if (singleMode.value) rightTone.value = leftTone.value
293+
leftAmplification.floatValue = buffer.float
294+
Log.d(TAG, "Parsed left amplification: ${leftAmplification.floatValue}")
295+
leftTone.floatValue = buffer.float
296+
Log.d(TAG, "Parsed left tone: ${leftTone.floatValue}")
297+
if (singleMode.value) rightTone.floatValue = leftTone.floatValue
308298
val leftConvFloat = buffer.float
309299
leftConversationBoost.value = leftConvFloat > 0.5f
310300
Log.d(TAG, "Parsed left conversation boost: $leftConvFloat (${leftConversationBoost.value})")
311301
if (singleMode.value) rightConversationBoost.value = leftConversationBoost.value
312-
leftAmbientNoiseReduction.value = buffer.float
313-
Log.d(TAG, "Parsed left ambient noise reduction: ${leftAmbientNoiseReduction.value}")
314-
if (singleMode.value) rightAmbientNoiseReduction.value = leftAmbientNoiseReduction.value
302+
leftAmbientNoiseReduction.floatValue = buffer.float
303+
Log.d(TAG, "Parsed left ambient noise reduction: ${leftAmbientNoiseReduction.floatValue}")
304+
if (singleMode.value) rightAmbientNoiseReduction.floatValue = leftAmbientNoiseReduction.floatValue
315305

316306
// Right bud
317307
val newRightEQ = rightEQ.value.copyOf()
@@ -321,24 +311,24 @@ class CustomDevice : ComponentActivity() {
321311
}
322312
rightEQ.value = newRightEQ
323313

324-
rightAmplification.value = buffer.float
325-
Log.d(TAG, "Parsed right amplification: ${rightAmplification.value}")
326-
rightTone.value = buffer.float
327-
Log.d(TAG, "Parsed right tone: ${rightTone.value}")
314+
rightAmplification.floatValue = buffer.float
315+
Log.d(TAG, "Parsed right amplification: ${rightAmplification.floatValue}")
316+
rightTone.floatValue = buffer.float
317+
Log.d(TAG, "Parsed right tone: ${rightTone.floatValue}")
328318
val rightConvFloat = buffer.float
329319
rightConversationBoost.value = rightConvFloat > 0.5f
330320
Log.d(TAG, "Parsed right conversation boost: $rightConvFloat (${rightConversationBoost.value})")
331-
rightAmbientNoiseReduction.value = buffer.float
332-
Log.d(TAG, "Parsed right ambient noise reduction: ${rightAmbientNoiseReduction.value}")
321+
rightAmbientNoiseReduction.floatValue = buffer.float
322+
Log.d(TAG, "Parsed right ambient noise reduction: ${rightAmbientNoiseReduction.floatValue}")
333323

334324
Log.d(TAG, "Settings parsed successfully")
335325

336326
// Update single mode values if in single mode
337327
if (singleMode.value) {
338-
val avg = (leftAmplification.value + rightAmplification.value) / 2
339-
amplification.value = avg.coerceIn(0f, 1f)
340-
val diff = rightAmplification.value - leftAmplification.value
341-
balance.value = (0.5f + diff / (2 * avg)).coerceIn(0f, 1f)
328+
val avg = (leftAmplification.floatValue + rightAmplification.floatValue) / 2
329+
amplification.floatValue = avg.coerceIn(0f, 1f)
330+
val diff = rightAmplification.floatValue - leftAmplification.floatValue
331+
balance.floatValue = (0.5f + diff / (2 * avg)).coerceIn(0f, 1f)
342332
}
343333
}
344334

@@ -363,19 +353,19 @@ class CustomDevice : ComponentActivity() {
363353
for (eq in leftEQ.value) {
364354
buffer.putFloat(eq)
365355
}
366-
buffer.putFloat(leftAmplification.value)
367-
buffer.putFloat(leftTone.value)
356+
buffer.putFloat(leftAmplification.floatValue)
357+
buffer.putFloat(leftTone.floatValue)
368358
buffer.putFloat(if (leftConversationBoost.value) 1.0f else 0.0f)
369-
buffer.putFloat(leftAmbientNoiseReduction.value)
359+
buffer.putFloat(leftAmbientNoiseReduction.floatValue)
370360

371361
// Right bud
372362
for (eq in rightEQ.value) {
373363
buffer.putFloat(eq)
374364
}
375-
buffer.putFloat(rightAmplification.value)
376-
buffer.putFloat(rightTone.value)
365+
buffer.putFloat(rightAmplification.floatValue)
366+
buffer.putFloat(rightTone.floatValue)
377367
buffer.putFloat(if (rightConversationBoost.value) 1.0f else 0.0f)
378-
buffer.putFloat(rightAmbientNoiseReduction.value)
368+
buffer.putFloat(rightAmbientNoiseReduction.floatValue)
379369

380370
val packet = buffer.array()
381371
Log.d(TAG, "Packet length: ${packet.size}")
@@ -393,4 +383,4 @@ class CustomDevice : ComponentActivity() {
393383
}
394384
}
395385
}
396-
}
386+
}

android/app/src/main/java/me/kavishdevar/librepods/MainActivity.kt

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,16 @@ import androidx.compose.ui.text.font.FontWeight
9797
import androidx.compose.ui.text.style.TextAlign
9898
import androidx.compose.ui.unit.dp
9999
import androidx.compose.ui.unit.sp
100+
import androidx.core.content.edit
101+
import androidx.core.net.toUri
100102
import androidx.navigation.compose.NavHost
101103
import androidx.navigation.compose.composable
102104
import androidx.navigation.compose.rememberNavController
103105
import com.google.accompanist.permissions.ExperimentalPermissionsApi
104106
import com.google.accompanist.permissions.MultiplePermissionsState
105107
import com.google.accompanist.permissions.isGranted
106108
import com.google.accompanist.permissions.rememberMultiplePermissionsState
109+
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
107110
import me.kavishdevar.librepods.constants.AirPodsNotifications
108111
import me.kavishdevar.librepods.screens.AirPodsSettingsScreen
109112
import me.kavishdevar.librepods.screens.AppSettingsScreen
@@ -123,6 +126,7 @@ import kotlin.io.encoding.ExperimentalEncodingApi
123126
lateinit var serviceConnection: ServiceConnection
124127
lateinit var connectionStatusReceiver: BroadcastReceiver
125128

129+
@ExperimentalHazeMaterialsApi
126130
@ExperimentalMaterial3Api
127131
class MainActivity : ComponentActivity() {
128132
companion object {
@@ -137,8 +141,10 @@ class MainActivity : ComponentActivity() {
137141

138142
setContent {
139143
LibrePodsTheme {
140-
getSharedPreferences("settings", MODE_PRIVATE).edit().putLong("textColor",
141-
MaterialTheme.colorScheme.onSurface.toArgb().toLong()).apply()
144+
getSharedPreferences("settings", MODE_PRIVATE).edit {
145+
putLong(
146+
"textColor",
147+
MaterialTheme.colorScheme.onSurface.toArgb().toLong())}
142148
Main()
143149
}
144150
}
@@ -207,8 +213,7 @@ class MainActivity : ComponentActivity() {
207213
}
208214

209215
private fun handleAddMagicKeys(uri: Uri) {
210-
val context = this
211-
val sharedPreferences = getSharedPreferences("settings", Context.MODE_PRIVATE)
216+
val sharedPreferences = getSharedPreferences("settings", MODE_PRIVATE)
212217

213218
val irkHex = uri.getQueryParameter("irk")
214219
val encKeyHex = uri.getQueryParameter("enc_key")
@@ -217,13 +222,13 @@ class MainActivity : ComponentActivity() {
217222
if (irkHex != null && validateHexInput(irkHex)) {
218223
val irkBytes = hexStringToByteArray(irkHex)
219224
val irkBase64 = Base64.encode(irkBytes)
220-
sharedPreferences.edit().putString("IRK", irkBase64).apply()
225+
sharedPreferences.edit {putString("IRK", irkBase64)}
221226
}
222227

223228
if (encKeyHex != null && validateHexInput(encKeyHex)) {
224229
val encKeyBytes = hexStringToByteArray(encKeyHex)
225230
val encKeyBase64 = Base64.encode(encKeyBytes)
226-
sharedPreferences.edit().putString("ENC_KEY", encKeyBase64).apply()
231+
sharedPreferences.edit { putString("ENC_KEY", encKeyBase64)}
227232
}
228233

229234
Toast.makeText(this, "Magic keys added successfully!", Toast.LENGTH_SHORT).show()
@@ -247,6 +252,7 @@ class MainActivity : ComponentActivity() {
247252
}
248253
}
249254

255+
@ExperimentalHazeMaterialsApi
250256
@SuppressLint("MissingPermission", "InlinedApi", "UnspecifiedRegisterReceiverFlag")
251257
@OptIn(ExperimentalPermissionsApi::class)
252258
@Composable
@@ -404,6 +410,7 @@ fun Main() {
404410
}
405411
}
406412

413+
@ExperimentalHazeMaterialsApi
407414
@OptIn(ExperimentalPermissionsApi::class, ExperimentalMaterial3Api::class)
408415
@Composable
409416
fun PermissionsScreen(
@@ -586,7 +593,7 @@ fun PermissionsScreen(
586593
onClick = {
587594
val intent = Intent(
588595
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
589-
Uri.parse("package:${context.packageName}")
596+
"package:${context.packageName}".toUri()
590597
)
591598
context.startActivity(intent)
592599
onOverlaySettingsReturn()
@@ -616,9 +623,9 @@ fun PermissionsScreen(
616623

617624
Button(
618625
onClick = {
619-
val editor = context.getSharedPreferences("settings", MODE_PRIVATE).edit()
620-
editor.putBoolean("overlay_permission_skipped", true)
621-
editor.apply()
626+
context.getSharedPreferences("settings", MODE_PRIVATE).edit {
627+
putBoolean("overlay_permission_skipped", true)
628+
}
622629

623630
val intent = Intent(context, MainActivity::class.java)
624631
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)

android/app/src/main/java/me/kavishdevar/librepods/QuickSettingsDialogActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ class QuickSettingsDialogActivity : ComponentActivity() {
133133
window.setGravity(Gravity.BOTTOM)
134134

135135
Intent(this, AirPodsService::class.java).also { intent ->
136-
bindService(intent, connection, Context.BIND_AUTO_CREATE)
136+
bindService(intent, connection, BIND_AUTO_CREATE)
137137
}
138138

139139
setContent {

android/app/src/main/java/me/kavishdevar/librepods/composables/AccessibilitySettings.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ fun AccessibilitySettings() {
134134
textColor = textColor
135135
)
136136

137-
val volumeSwipeSpeedOptions = mapOf<Byte, String>(
137+
val volumeSwipeSpeedOptions = mapOf(
138138
1.toByte() to "Default",
139139
2.toByte() to "Longer",
140140
3.toByte() to "Longest"

0 commit comments

Comments
 (0)