add persistent notification for battery; bug fixes

This commit is contained in:
Kavish Devar
2024-12-05 10:02:19 +05:30
parent 7a246b3800
commit 0c1f9464ad
13 changed files with 536 additions and 230 deletions
@@ -20,23 +20,25 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.google.accompanist.permissions.shouldShowRationale
import com.google.accompanist.permissions.rememberMultiplePermissionsState
import com.primex.core.ExperimentalToolkitApi
import me.kavishdevar.aln.ui.theme.ALNTheme
lateinit var serviceConnection: ServiceConnection
lateinit var connectionStatusReceiver: BroadcastReceiver
@ExperimentalMaterial3Api
class MainActivity : ComponentActivity() {
@@ -46,72 +48,94 @@ class MainActivity : ComponentActivity() {
enableEdgeToEdge()
setContent {
ALNTheme {
getSharedPreferences("settings", MODE_PRIVATE).edit().putLong("textColor",
MaterialTheme.colorScheme.onSurface.toArgb().toLong()).apply()
Main()
startService(Intent(this, AirPodsService::class.java))
}
}
}
override fun onDestroy() {
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.onDestroy()
}
}
@SuppressLint("MissingPermission", "InlinedApi")
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun Main() {
val bluetoothConnectPermissionState = rememberPermissionState(
permission = "android.permission.BLUETOOTH_CONNECT"
val isConnected = remember { mutableStateOf(false) }
val permissionState = rememberMultiplePermissionsState(
permissions = listOf(
"android.permission.BLUETOOTH_CONNECT",
"android.permission.POST_NOTIFICATIONS"
)
)
if (bluetoothConnectPermissionState.status.isGranted) {
val airPodsService = remember { mutableStateOf<AirPodsService?>(null) }
if (permissionState.allPermissionsGranted) {
Log.d("MainActivity", "HIIIIIIIIIIIIIII")
val context = LocalContext.current
val airPodsService = remember { mutableStateOf<AirPodsService?>(null) }
val navController = rememberNavController()
val disconnectReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.d("MainActivity", "Received DISCONNECTED broadcast")
navController.navigate("notConnected")
connectionStatusReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == AirPodsNotifications.AIRPODS_CONNECTED) {
Log.d("MainActivity", "AirPods Connected intent received")
isConnected.value = true
}
else if (intent.action == AirPodsNotifications.AIRPODS_DISCONNECTED) {
Log.d("MainActivity", "AirPods Disconnected intent received")
isConnected.value = false
}
}
}
context.registerReceiver(disconnectReceiver, IntentFilter(AirPodsNotifications.AIRPODS_DISCONNECTED),
Context.RECEIVER_NOT_EXPORTED)
val filter = IntentFilter().apply {
addAction(AirPodsNotifications.AIRPODS_CONNECTED)
addAction(AirPodsNotifications.AIRPODS_DISCONNECTED)
addAction(AirPodsNotifications.AIRPODS_CONNECTION_DETECTED)
}
Log.d("MainActivity", "Registering Receiver")
context.registerReceiver(connectionStatusReceiver, filter, Context.RECEIVER_EXPORTED)
Log.d("MainActivity", "Registered Receiver")
// UI logic
NavHost(
navController = navController,
startDestination = "notConnected",
startDestination = "settings",
enterTransition = { slideInHorizontally(initialOffsetX = { it }, animationSpec = tween(300)) },
exitTransition = { slideOutHorizontally(targetOffsetX = { -it }, animationSpec = tween(300)) },
popEnterTransition = { slideInHorizontally(initialOffsetX = { -it }, animationSpec = tween(300)) },
popExitTransition = { slideOutHorizontally(targetOffsetX = { it }, animationSpec = tween(300)) }
) {
composable("notConnected") {
Text("Not Connected...")
}
composable("settings") {
AirPodsSettingsScreen(
device = airPodsService.value?.device,
service = airPodsService.value,
navController = navController
)
if (airPodsService.value != null) {
AirPodsSettingsScreen(
device = airPodsService.value?.device,
service = airPodsService.value!!,
navController = navController,
isConnected = isConnected.value
)
}
}
composable("debug") {
DebugScreen(navController = navController)
}
}
val receiver = object: BroadcastReceiver() {
override fun onReceive(p0: Context?, p1: Intent?) {
navController.navigate("settings")
navController.popBackStack("notConnected", inclusive = true)
}
}
context.registerReceiver(receiver, IntentFilter(AirPodsNotifications.AIRPODS_CONNECTED),
Context.RECEIVER_EXPORTED)
val serviceConnection = remember {
serviceConnection = remember {
object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val binder = service as AirPodsService.LocalBinder
@@ -126,28 +150,23 @@ fun Main() {
context.bindService(Intent(context, AirPodsService::class.java), serviceConnection, Context.BIND_AUTO_CREATE)
val alreadyConnected = remember { mutableStateOf(false) }
if (airPodsService.value?.isConnected == true && !alreadyConnected.value) {
Log.d("ALN", "Connected")
navController.navigate("settings")
} else {
Log.d("ALN", "Not connected")
navController.navigate("notConnected")
if (airPodsService.value?.isConnected == true) {
isConnected.value = true
}
} else {
// Permission is not granted, request it
Column (
modifier = Modifier.padding(24.dp),
){
val textToShow = if (bluetoothConnectPermissionState.status.shouldShowRationale) {
val textToShow = if (permissionState.shouldShowRationale) {
// If the user has denied the permission but not permanently, explain why it's needed.
"The BLUETOOTH_CONNECT permission is important for this app. Please grant it to proceed."
"Please enable Bluetooth and Notification permissions to use the app. The Nearby Devices is required to connect to your AirPods, and the notification is required to show the AirPods battery status."
} else {
// If the user has permanently denied the permission, inform them to enable it in settings.
"BLUETOOTH_CONNECT permission required for this feature. Please enable it in settings."
"Please enable Bluetooth and Notification permissions in the app settings to use the app."
}
Text(textToShow)
Button(onClick = { bluetoothConnectPermissionState.launchPermissionRequest() }) {
Button(onClick = { permissionState.launchMultiplePermissionRequest() }) {
Text("Request permission")
}
}