try adding widget; add previews to each composable

This commit is contained in:
Kavish Devar
2024-12-16 16:13:52 +05:30
parent ff6c72ffa6
commit 34ace1fc6e
33 changed files with 302 additions and 571 deletions

View File

@@ -61,7 +61,7 @@
</activity> </activity>
<service <service
android:name=".AirPodsService" android:name=".services.AirPodsService"
android:enabled="true" android:enabled="true"
android:exported="true" android:exported="true"
android:foregroundServiceType="connectedDevice" android:foregroundServiceType="connectedDevice"

View File

@@ -3,11 +3,12 @@ package me.kavishdevar.aln
import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider import android.appwidget.AppWidgetProvider
import android.content.Context import android.content.Context
import android.util.Log
import android.widget.RemoteViews import android.widget.RemoteViews
import me.kavishdevar.aln.services.ServiceManager
import me.kavishdevar.aln.utils.BatteryComponent
import me.kavishdevar.aln.utils.BatteryStatus
/**
* Implementation of App Widget functionality.
*/
class BatteryWidget : AppWidgetProvider() { class BatteryWidget : AppWidgetProvider() {
override fun onUpdate( override fun onUpdate(
context: Context, context: Context,
@@ -34,11 +35,36 @@ internal fun updateAppWidget(
appWidgetManager: AppWidgetManager, appWidgetManager: AppWidgetManager,
appWidgetId: Int appWidgetId: Int
) { ) {
val widgetText = context.getString(R.string.appwidget_text) val service = ServiceManager.getService()
// Construct the RemoteViews object val batteryList = service?.batteryNotification?.getBattery()
val views = RemoteViews(context.packageName, R.layout.battery_widget)
views.setTextViewText(R.id.appwidget_text, widgetText) val views = RemoteViews(context.packageName, R.layout.battery_widget)
Log.d("BatteryWidget", "Battery list: $batteryList")
views.setTextViewText(R.id.left_battery_widget,
batteryList?.find { it.component == BatteryComponent.LEFT }?.let {
if (it.status != BatteryStatus.DISCONNECTED) {
"${if (it.status == BatteryStatus.CHARGING) "⚡" else ""} ${it.level}%"
} else {
""
}
} ?: "")
views.setTextViewText(R.id.right_battery_widget,
batteryList?.find { it.component == BatteryComponent.RIGHT }?.let {
if (it.status != BatteryStatus.DISCONNECTED) {
"${if (it.status == BatteryStatus.CHARGING) "⚡" else ""} ${it.level}%"
} else {
""
}
} ?: "")
views.setTextViewText(R.id.case_battery_widget,
batteryList?.find { it.component == BatteryComponent.CASE }?.let {
if (it.status != BatteryStatus.DISCONNECTED) {
"${if (it.status == BatteryStatus.CHARGING) "⚡" else ""} ${it.level}%"
} else {
""
}
} ?: "")
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views) appWidgetManager.updateAppWidget(appWidgetId, views)
} }

View File

@@ -37,7 +37,9 @@ import com.google.accompanist.permissions.rememberMultiplePermissionsState
import me.kavishdevar.aln.screens.AirPodsSettingsScreen import me.kavishdevar.aln.screens.AirPodsSettingsScreen
import me.kavishdevar.aln.screens.DebugScreen import me.kavishdevar.aln.screens.DebugScreen
import me.kavishdevar.aln.screens.LongPress import me.kavishdevar.aln.screens.LongPress
import me.kavishdevar.aln.services.AirPodsService
import me.kavishdevar.aln.ui.theme.ALNTheme import me.kavishdevar.aln.ui.theme.ALNTheme
import me.kavishdevar.aln.utils.AirPodsNotifications
lateinit var serviceConnection: ServiceConnection lateinit var serviceConnection: ServiceConnection
lateinit var connectionStatusReceiver: BroadcastReceiver lateinit var connectionStatusReceiver: BroadcastReceiver

View File

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

View File

@@ -18,7 +18,7 @@ import androidx.compose.ui.text.font.FontWeight
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 me.kavishdevar.aln.AirPodsService import me.kavishdevar.aln.services.AirPodsService
@Composable @Composable
fun AccessibilitySettings(service: AirPodsService, sharedPreferences: SharedPreferences) { fun AccessibilitySettings(service: AirPodsService, sharedPreferences: SharedPreferences) {

View File

@@ -32,7 +32,7 @@ import androidx.compose.ui.text.font.FontWeight
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 me.kavishdevar.aln.AirPodsService import me.kavishdevar.aln.services.AirPodsService
import kotlin.math.roundToInt import kotlin.math.roundToInt
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)

View File

@@ -18,7 +18,7 @@ import androidx.compose.ui.text.font.FontWeight
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 me.kavishdevar.aln.AirPodsService import me.kavishdevar.aln.services.AirPodsService
@Composable @Composable
fun AudioSettings(service: AirPodsService, sharedPreferences: SharedPreferences) { fun AudioSettings(service: AirPodsService, sharedPreferences: SharedPreferences) {

View File

@@ -24,11 +24,11 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.imageResource import androidx.compose.ui.res.imageResource
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 me.kavishdevar.aln.AirPodsNotifications import me.kavishdevar.aln.utils.AirPodsNotifications
import me.kavishdevar.aln.AirPodsService import me.kavishdevar.aln.services.AirPodsService
import me.kavishdevar.aln.Battery import me.kavishdevar.aln.utils.Battery
import me.kavishdevar.aln.BatteryComponent import me.kavishdevar.aln.utils.BatteryComponent
import me.kavishdevar.aln.BatteryStatus import me.kavishdevar.aln.utils.BatteryStatus
import me.kavishdevar.aln.R import me.kavishdevar.aln.R
@Composable @Composable

View File

@@ -27,7 +27,7 @@ import androidx.compose.ui.platform.LocalContext
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 me.kavishdevar.aln.AirPodsService import me.kavishdevar.aln.services.AirPodsService
@Composable @Composable
fun ConversationalAwarenessSwitch(service: AirPodsService, sharedPreferences: SharedPreferences) { fun ConversationalAwarenessSwitch(service: AirPodsService, sharedPreferences: SharedPreferences) {

View File

@@ -24,7 +24,7 @@ import androidx.compose.ui.platform.LocalContext
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 me.kavishdevar.aln.AirPodsService import me.kavishdevar.aln.services.AirPodsService
@Composable @Composable
fun IndependentToggle(name: String, service: AirPodsService, functionName: String, sharedPreferences: SharedPreferences, default: Boolean = false) { fun IndependentToggle(name: String, service: AirPodsService, functionName: String, sharedPreferences: SharedPreferences, default: Boolean = false) {

View File

@@ -27,7 +27,7 @@ import androidx.compose.ui.platform.LocalContext
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 me.kavishdevar.aln.AirPodsService import me.kavishdevar.aln.services.AirPodsService
@Composable @Composable
fun LoudSoundReductionSwitch(service: AirPodsService, sharedPreferences: SharedPreferences) { fun LoudSoundReductionSwitch(service: AirPodsService, sharedPreferences: SharedPreferences) {

View File

@@ -1,6 +1,5 @@
package me.kavishdevar.aln.composables package me.kavishdevar.aln.composables
import me.kavishdevar.aln.R
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -20,6 +19,7 @@ import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.res.imageResource import androidx.compose.ui.res.imageResource
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 me.kavishdevar.aln.R
@Composable @Composable
fun NoiseControlButton( fun NoiseControlButton(

View File

@@ -34,9 +34,9 @@ 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 me.kavishdevar.aln.AirPodsNotifications import me.kavishdevar.aln.utils.AirPodsNotifications
import me.kavishdevar.aln.AirPodsService import me.kavishdevar.aln.services.AirPodsService
import me.kavishdevar.aln.NoiseControlMode import me.kavishdevar.aln.utils.NoiseControlMode
import me.kavishdevar.aln.R import me.kavishdevar.aln.R
@SuppressLint("UnspecifiedRegisterReceiverFlag") @SuppressLint("UnspecifiedRegisterReceiverFlag")

View File

@@ -27,7 +27,7 @@ import androidx.compose.ui.platform.LocalContext
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 me.kavishdevar.aln.AirPodsService import me.kavishdevar.aln.services.AirPodsService
@Composable @Composable
fun PersonalizedVolumeSwitch(service: AirPodsService, sharedPreferences: SharedPreferences) { fun PersonalizedVolumeSwitch(service: AirPodsService, sharedPreferences: SharedPreferences) {

View File

@@ -27,7 +27,7 @@ import androidx.compose.ui.platform.LocalContext
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 me.kavishdevar.aln.AirPodsService import me.kavishdevar.aln.services.AirPodsService
@Composable @Composable
fun SinglePodANCSwitch(service: AirPodsService, sharedPreferences: SharedPreferences) { fun SinglePodANCSwitch(service: AirPodsService, sharedPreferences: SharedPreferences) {

View File

@@ -32,7 +32,7 @@ import androidx.compose.ui.text.font.FontWeight
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 me.kavishdevar.aln.AirPodsService import me.kavishdevar.aln.services.AirPodsService
import me.kavishdevar.aln.R import me.kavishdevar.aln.R
import kotlin.math.roundToInt import kotlin.math.roundToInt

View File

@@ -27,7 +27,7 @@ import androidx.compose.ui.platform.LocalContext
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 me.kavishdevar.aln.AirPodsService import me.kavishdevar.aln.services.AirPodsService
@Composable @Composable
fun VolumeControlSwitch(service: AirPodsService, sharedPreferences: SharedPreferences) { fun VolumeControlSwitch(service: AirPodsService, sharedPreferences: SharedPreferences) {

View File

@@ -3,7 +3,7 @@ package me.kavishdevar.aln.receivers
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 me.kavishdevar.aln.AirPodsService import me.kavishdevar.aln.services.AirPodsService
class BootReceiver: BroadcastReceiver() { class BootReceiver: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {

View File

@@ -14,8 +14,13 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
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.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
@@ -33,6 +38,8 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
@@ -46,8 +53,7 @@ import dev.chrisbanes.haze.haze
import dev.chrisbanes.haze.hazeChild import dev.chrisbanes.haze.hazeChild
import dev.chrisbanes.haze.materials.CupertinoMaterials import dev.chrisbanes.haze.materials.CupertinoMaterials
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import me.kavishdevar.aln.AirPodsNotifications import me.kavishdevar.aln.R
import me.kavishdevar.aln.AirPodsService
import me.kavishdevar.aln.composables.AccessibilitySettings import me.kavishdevar.aln.composables.AccessibilitySettings
import me.kavishdevar.aln.composables.AudioSettings import me.kavishdevar.aln.composables.AudioSettings
import me.kavishdevar.aln.composables.BatteryView import me.kavishdevar.aln.composables.BatteryView
@@ -56,7 +62,10 @@ import me.kavishdevar.aln.composables.NavigationButton
import me.kavishdevar.aln.composables.NoiseControlSettings import me.kavishdevar.aln.composables.NoiseControlSettings
import me.kavishdevar.aln.composables.PressAndHoldSettings import me.kavishdevar.aln.composables.PressAndHoldSettings
import me.kavishdevar.aln.composables.StyledTextField import me.kavishdevar.aln.composables.StyledTextField
import me.kavishdevar.aln.services.AirPodsService
import me.kavishdevar.aln.services.ServiceManager
import me.kavishdevar.aln.ui.theme.ALNTheme import me.kavishdevar.aln.ui.theme.ALNTheme
import me.kavishdevar.aln.utils.AirPodsNotifications
@OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class)
@SuppressLint("MissingPermission", "NewApi") @SuppressLint("MissingPermission", "NewApi")
@@ -88,7 +97,13 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService,
CenterAlignedTopAppBar( CenterAlignedTopAppBar(
title = { title = {
Text( Text(
text = deviceName.text text = deviceName.text,
style = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.Medium,
color = if (darkMode) Color.White else Color.Black,
fontFamily = FontFamily(Font(R.font.sf_pro))
)
) )
}, },
modifier = Modifier modifier = Modifier
@@ -116,23 +131,23 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService,
colors = TopAppBarDefaults.centerAlignedTopAppBarColors( colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
containerColor = Color.Transparent containerColor = Color.Transparent
), ),
// actions = { actions = {
// val context = LocalContext.current val context = LocalContext.current
// IconButton( IconButton(
// onClick = { onClick = {
// ServiceManager.restartService(context) ServiceManager.restartService(context)
// }, },
// colors = IconButtonDefaults.iconButtonColors( colors = IconButtonDefaults.iconButtonColors(
// containerColor = Color.Transparent, containerColor = Color.Transparent,
// contentColor = if (isSystemInDarkTheme()) Color.White else Color.Black contentColor = if (isSystemInDarkTheme()) Color.White else Color.Black
// ) )
// ) { ) {
// Icon( Icon(
// imageVector = Icons.Default.Refresh, imageVector = Icons.Default.Refresh,
// contentDescription = "Settings", contentDescription = "Settings",
// ) )
// } }
// } }
) )
} }
) { paddingValues -> ) { paddingValues ->
@@ -228,7 +243,8 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService,
style = TextStyle( style = TextStyle(
fontSize = 24.sp, fontSize = 24.sp,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
color = if (isSystemInDarkTheme()) Color.White else Color.Black color = if (isSystemInDarkTheme()) Color.White else Color.Black,
fontFamily = FontFamily(Font(R.font.sf_pro))
), ),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
@@ -239,7 +255,8 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService,
style = TextStyle( style = TextStyle(
fontSize = 16.sp, fontSize = 16.sp,
fontWeight = FontWeight.Light, fontWeight = FontWeight.Light,
color = if (isSystemInDarkTheme()) Color.White else Color.Black color = if (isSystemInDarkTheme()) Color.White else Color.Black,
fontFamily = FontFamily(Font(R.font.sf_pro))
), ),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
@@ -253,9 +270,13 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService,
@Preview @Preview
@Composable @Composable
fun AirPodsSettingsScreenPreview() { fun AirPodsSettingsScreenPreview() {
ALNTheme ( Column (
darkTheme = true modifier = Modifier.height(2000.dp)
) { ) {
AirPodsSettingsScreen(dev = null, service = AirPodsService(), navController = rememberNavController(), isConnected = true) ALNTheme (
darkTheme = true
) {
AirPodsSettingsScreen(dev = null, service = AirPodsService(), navController = rememberNavController(), isConnected = true)
}
} }
} }

View File

@@ -61,8 +61,8 @@ import dev.chrisbanes.haze.haze
import dev.chrisbanes.haze.hazeChild import dev.chrisbanes.haze.hazeChild
import dev.chrisbanes.haze.materials.CupertinoMaterials import dev.chrisbanes.haze.materials.CupertinoMaterials
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import me.kavishdevar.aln.AirPodsNotifications import me.kavishdevar.aln.utils.AirPodsNotifications
import me.kavishdevar.aln.AirPodsService import me.kavishdevar.aln.services.AirPodsService
import me.kavishdevar.aln.R import me.kavishdevar.aln.R
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)

View File

@@ -45,7 +45,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.navigation.NavController import androidx.navigation.NavController
import me.kavishdevar.aln.R import me.kavishdevar.aln.R
import me.kavishdevar.aln.ServiceManager import me.kavishdevar.aln.services.ServiceManager
@Composable() @Composable()
fun RightDivider() { fun RightDivider() {

View File

@@ -8,9 +8,8 @@ import android.content.IntentFilter
import android.service.quicksettings.Tile import android.service.quicksettings.Tile
import android.service.quicksettings.TileService import android.service.quicksettings.TileService
import android.util.Log import android.util.Log
import me.kavishdevar.aln.AirPodsNotifications import me.kavishdevar.aln.utils.AirPodsNotifications
import me.kavishdevar.aln.NoiseControlMode import me.kavishdevar.aln.utils.NoiseControlMode
import me.kavishdevar.aln.ServiceManager
class AirPodsQSService: TileService() { class AirPodsQSService: TileService() {
private val ancModes = listOf(NoiseControlMode.NOISE_CANCELLATION.name, NoiseControlMode.TRANSPARENCY.name, NoiseControlMode.ADAPTIVE.name) private val ancModes = listOf(NoiseControlMode.NOISE_CANCELLATION.name, NoiseControlMode.TRANSPARENCY.name, NoiseControlMode.ADAPTIVE.name)

View File

@@ -1,4 +1,4 @@
package me.kavishdevar.aln package me.kavishdevar.aln.services
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Notification import android.app.Notification
@@ -25,6 +25,14 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.kavishdevar.aln.utils.AirPodsNotifications
import me.kavishdevar.aln.utils.Battery
import me.kavishdevar.aln.utils.BatteryComponent
import me.kavishdevar.aln.utils.BatteryStatus
import me.kavishdevar.aln.utils.Enums
import me.kavishdevar.aln.utils.LongPressPackets
import me.kavishdevar.aln.R
import me.kavishdevar.aln.utils.Window
import me.kavishdevar.aln.utils.MediaController import me.kavishdevar.aln.utils.MediaController
import org.lsposed.hiddenapibypass.HiddenApiBypass import org.lsposed.hiddenapibypass.HiddenApiBypass
@@ -38,11 +46,11 @@ object ServiceManager {
fun setService(service: AirPodsService?) { fun setService(service: AirPodsService?) {
this.service = service this.service = service
} }
// @Synchronized @Synchronized
// fun restartService(context: Context) { fun restartService(context: Context) {
// service?.stopSelf() service?.stopSelf()
// context.startService(Intent(context, AirPodsService::class.java)) context.startService(Intent(context, AirPodsService::class.java))
// } }
} }
@Suppress("unused") @Suppress("unused")
@@ -84,7 +92,7 @@ class AirPodsService: Service() {
} }
val uuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a") val uuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a")
if (bluetoothDevice.uuids.contains(uuid)) { if (bluetoothDevice.uuids.contains(uuid)) {
val intent = Intent(AirPodsNotifications.AIRPODS_CONNECTION_DETECTED) val intent = Intent(AirPodsNotifications.Companion.AIRPODS_CONNECTION_DETECTED)
intent.putExtra("name", name) intent.putExtra("name", name)
intent.putExtra("device", bluetoothDevice) intent.putExtra("device", bluetoothDevice)
context?.sendBroadcast(intent) context?.sendBroadcast(intent)
@@ -94,7 +102,7 @@ class AirPodsService: Service() {
|| BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED == action || BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED == action
) { ) {
context?.sendBroadcast( context?.sendBroadcast(
Intent(AirPodsNotifications.AIRPODS_DISCONNECTED) Intent(AirPodsNotifications.Companion.AIRPODS_DISCONNECTED)
) )
} }
} }
@@ -195,7 +203,6 @@ class AirPodsService: Service() {
.build() .build()
} }
// Notify the NotificationManager with the same ID
notificationManager.notify(1, updatedNotification) notificationManager.notify(1, updatedNotification)
} }
@@ -219,11 +226,12 @@ class AirPodsService: Service() {
addAction("android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED") addAction("android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED")
addAction("android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED") addAction("android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED")
} }
registerReceiver(bluetoothReceiver, serviceIntentFilter, RECEIVER_EXPORTED) registerReceiver(bluetoothReceiver, serviceIntentFilter, RECEIVER_EXPORTED)
connectionReceiver = object: BroadcastReceiver() { connectionReceiver = object: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == AirPodsNotifications.AIRPODS_CONNECTION_DETECTED) { if (intent?.action == AirPodsNotifications.Companion.AIRPODS_CONNECTION_DETECTED) {
val name = this@AirPodsService.getSharedPreferences("settings", MODE_PRIVATE) val name = this@AirPodsService.getSharedPreferences("settings", MODE_PRIVATE)
.getString("name", device?.name) .getString("name", device?.name)
Log.d("AirPodsService", "$name connected") Log.d("AirPodsService", "$name connected")
@@ -232,7 +240,7 @@ class AirPodsService: Service() {
connectToSocket(device!!) connectToSocket(device!!)
updateNotificationContent(true, name.toString(), batteryNotification.getBattery()) updateNotificationContent(true, name.toString(), batteryNotification.getBattery())
} }
else if (intent?.action == AirPodsNotifications.AIRPODS_DISCONNECTED) { else if (intent?.action == AirPodsNotifications.Companion.AIRPODS_DISCONNECTED) {
device = null device = null
isConnected = false isConnected = false
popupShown = false popupShown = false
@@ -242,8 +250,8 @@ class AirPodsService: Service() {
} }
val deviceIntentFilter = IntentFilter().apply { val deviceIntentFilter = IntentFilter().apply {
addAction(AirPodsNotifications.AIRPODS_CONNECTION_DETECTED) addAction(AirPodsNotifications.Companion.AIRPODS_CONNECTION_DETECTED)
addAction(AirPodsNotifications.AIRPODS_DISCONNECTED) addAction(AirPodsNotifications.Companion.AIRPODS_DISCONNECTED)
} }
registerReceiver(connectionReceiver, deviceIntentFilter, RECEIVER_EXPORTED) registerReceiver(connectionReceiver, deviceIntentFilter, RECEIVER_EXPORTED)
@@ -257,7 +265,7 @@ class AirPodsService: Service() {
if (connectedDevices.isNotEmpty()) { if (connectedDevices.isNotEmpty()) {
connectToSocket(device) connectToSocket(device)
this@AirPodsService.sendBroadcast( this@AirPodsService.sendBroadcast(
Intent(AirPodsNotifications.AIRPODS_CONNECTED) Intent(AirPodsNotifications.Companion.AIRPODS_CONNECTED)
) )
} }
} }
@@ -348,7 +356,7 @@ class AirPodsService: Service() {
it.outputStream.flush() it.outputStream.flush()
delay(200) delay(200)
sendBroadcast( sendBroadcast(
Intent(AirPodsNotifications.AIRPODS_CONNECTED) Intent(AirPodsNotifications.Companion.AIRPODS_CONNECTED)
.putExtra("device", device) .putExtra("device", device)
) )
@@ -362,7 +370,7 @@ class AirPodsService: Service() {
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.Companion.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)
@@ -371,14 +379,14 @@ 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.Companion.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.Companion.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]
@@ -463,7 +471,7 @@ class AirPodsService: Service() {
} }
val earIntentFilter = val earIntentFilter =
IntentFilter(AirPodsNotifications.EAR_DETECTION_DATA) IntentFilter(AirPodsNotifications.Companion.EAR_DETECTION_DATA)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
this@AirPodsService.registerReceiver( this@AirPodsService.registerReceiver(
earReceiver, earIntentFilter, earReceiver, earIntentFilter,
@@ -477,13 +485,13 @@ class AirPodsService: Service() {
} }
} 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.Companion.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.Companion.BATTERY_DATA).apply {
putParcelableArrayListExtra( putParcelableArrayListExtra(
"data", "data",
ArrayList(batteryNotification.getBattery()) ArrayList(batteryNotification.getBattery())
@@ -513,7 +521,7 @@ class AirPodsService: Service() {
) )
) { ) {
conversationAwarenessNotification.setData(data) conversationAwarenessNotification.setData(data)
sendBroadcast(Intent(AirPodsNotifications.CA_DATA).apply { sendBroadcast(Intent(AirPodsNotifications.Companion.CA_DATA).apply {
putExtra("data", conversationAwarenessNotification.status) putExtra("data", conversationAwarenessNotification.status)
}) })
@@ -535,7 +543,7 @@ class AirPodsService: Service() {
Log.d("AirPods Service", "Socket closed") Log.d("AirPods Service", "Socket closed")
isConnected = false isConnected = false
socket.close() socket.close()
sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED)) sendBroadcast(Intent(AirPodsNotifications.Companion.AIRPODS_DISCONNECTED))
} }
} }
} catch (e: Exception) { } catch (e: Exception) {

View File

@@ -1,6 +1,6 @@
@file:Suppress("unused") @file:Suppress("unused")
package me.kavishdevar.aln package me.kavishdevar.aln.utils
import android.os.Parcelable import android.os.Parcelable
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize

View File

@@ -1,4 +1,4 @@
package me.kavishdevar.aln package me.kavishdevar.aln.utils
import android.animation.Animator import android.animation.Animator
import android.animation.AnimatorListenerAdapter import android.animation.AnimatorListenerAdapter
@@ -22,7 +22,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.lang.Exception import me.kavishdevar.aln.R
@SuppressLint("InflateParams", "ClickableViewAccessibility") @SuppressLint("InflateParams", "ClickableViewAccessibility")
class Window (context: Context) { class Window (context: Context) {

View File

@@ -4,7 +4,6 @@ appWidgetInnerRadius attribute value
--> -->
<shape xmlns:android="http://schemas.android.com/apk/res/android" <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<corners android:radius="?attr/appWidgetInnerRadius" /> <corners android:radius="?attr/appWidgetInnerRadius" />
<solid android:color="?android:attr/colorAccent" /> <solid android:color="?android:attr/colorAccent" />
</shape> </shape>

View File

@@ -0,0 +1,72 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="@style/Widget.ALN.AppWidget.Container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/Theme.ALN.AppWidgetContainer">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_margin="0dp"
android:gravity="center">
<ImageView
android:layout_width="64dp"
android:layout_height="64dp"
android:contentDescription="something"
android:gravity="center_vertical"
android:layout_marginEnd="0dp"
android:src="@drawable/airpods_pro_left_notification"
android:tint="@android:color/system_accent2_400"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/left_battery_widget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp"
android:textColor="@android:color/system_accent2_400"
android:layout_marginEnd="8dp"
android:gravity="center_vertical"
android:text="Left"
tools:ignore="HardcodedText" />
<ImageView
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@drawable/airpods_pro_right_notification"
android:tint="@android:color/system_accent2_400"
android:layout_margin="0dp"
android:contentDescription="something"
android:gravity="center_vertical"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/right_battery_widget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp"
android:textColor="@android:color/system_accent2_400"
android:layout_marginEnd="8dp"
android:gravity="center_vertical"
android:text="Right"
tools:ignore="HardcodedText" />
<ImageView
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@drawable/airpods_pro_case_notification"
android:tint="@android:color/system_accent2_400"
android:contentDescription="something"
android:layout_margin="0dp"
android:gravity="center_vertical"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/case_battery_widget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp"
android:textColor="@android:color/system_accent2_400"
android:layout_marginEnd="8dp"
android:gravity="center_vertical"
android:text="Case"
tools:ignore="HardcodedText" />
</LinearLayout>
</RelativeLayout>

View File

@@ -1,19 +1,74 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="@style/Widget.ALN.AppWidget.Container" style="@style/Widget.ALN.AppWidget.Container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:theme="@style/Theme.ALN.AppWidgetContainer"> android:theme="@style/Theme.ALN.AppWidgetContainer">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_margin="0dp"
android:gravity="center">
<ImageView
android:layout_width="64dp"
android:layout_height="64dp"
android:contentDescription="something"
android:gravity="center_vertical"
android:layout_marginEnd="0dp"
android:src="@drawable/airpods_pro_left_notification"
android:tint="@color/popup_text"
tools:ignore="HardcodedText" />
<TextView <TextView
android:id="@+id/appwidget_text" android:id="@+id/left_battery_widget"
style="@style/Widget.ALN.AppWidget.InnerView" android:layout_width="0dp"
android:layout_width="wrap_content" android:layout_weight="1"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerHorizontal="true" android:textSize="24sp"
android:layout_centerVertical="true" android:textColor="@color/popup_text"
android:layout_margin="8dp" android:layout_marginHorizontal="8dp"
android:contentDescription="@string/appwidget_text" android:gravity="center_vertical"
android:text="@string/appwidget_text" android:text="Left"
android:textSize="24sp" tools:ignore="HardcodedText" />
android:textStyle="bold|italic" /> <ImageView
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@drawable/airpods_pro_right_notification"
android:tint="@color/popup_text"
android:layout_margin="0dp"
android:contentDescription="something"
android:gravity="center_vertical"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/right_battery_widget"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:textSize="24sp"
android:textColor="@color/popup_text"
android:layout_marginHorizontal="8dp"
android:gravity="center_vertical"
android:text="Right"
tools:ignore="HardcodedText" />
<ImageView
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@drawable/airpods_pro_case_notification"
android:tint="@color/popup_text"
android:contentDescription="something"
android:layout_margin="0dp"
android:gravity="center_vertical"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/case_battery_widget"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:textSize="24sp"
android:textColor="@color/popup_text"
android:layout_marginHorizontal="8dp"
android:gravity="center_vertical"
android:text="Case"
tools:ignore="HardcodedText" />
</LinearLayout>
</RelativeLayout> </RelativeLayout>

View File

@@ -2,4 +2,6 @@
<resources> <resources>
<color name="popup_background">#1C1B1E</color> <color name="popup_background">#1C1B1E</color>
<color name="popup_text">@color/white</color> <color name="popup_text">@color/white</color>
<color name="widget_background">#1C1B1E</color>
<color name="widget_text">@color/white</color>
</resources> </resources>

View File

@@ -4,8 +4,6 @@
<color name="white">#FFFFFFFF</color> <color name="white">#FFFFFFFF</color>
<color name="popup_background">#FFFFFF</color> <color name="popup_background">#FFFFFF</color>
<color name="popup_text">@color/black</color> <color name="popup_text">@color/black</color>
<color name="light_blue_50">#FFE1F5FE</color> <color name="widget_background">#87FFFFFF</color>
<color name="light_blue_200">#FF81D4FA</color> <color name="widget_text">@color/black</color>
<color name="light_blue_600">#FF039BE5</color>
<color name="light_blue_900">#FF01579B</color>
</resources> </resources>

View File

@@ -1,7 +1,5 @@
<resources> <resources>
<string name="app_name">ALN</string> <string name="app_name">ALN</string>
<string name="title_activity_custom_device">GATT Testing</string> <string name="title_activity_custom_device">GATT Testing</string>
<string name="appwidget_text">EXAMPLE</string> <string name="app_widget_description">See your AirPods battery status right from your home screen!</string>
<string name="add_widget">Add widget</string>
<string name="app_widget_description">This is an app widget description</string>
</resources> </resources>

View File

@@ -5,12 +5,12 @@
<style name="Theme.ALN.AppWidgetContainerParent" parent="@android:style/Theme.DeviceDefault"> <style name="Theme.ALN.AppWidgetContainerParent" parent="@android:style/Theme.DeviceDefault">
<!-- Radius of the outer bound of widgets to make the rounded corners --> <!-- Radius of the outer bound of widgets to make the rounded corners -->
<item name="appWidgetRadius">16dp</item> <item name="appWidgetRadius">24dp</item>
<!-- <!--
Radius of the inner view's bound of widgets to make the rounded corners. Radius of the inner view's bound of widgets to make the rounded corners.
It needs to be 8dp or less than the value of appWidgetRadius It needs to be 8dp or less than the value of appWidgetRadius
--> -->
<item name="appWidgetInnerRadius">8dp</item> <item name="appWidgetInnerRadius">24dp</item>
</style> </style>
<style name="Theme.ALN.AppWidgetContainer" parent="Theme.ALN.AppWidgetContainerParent"> <style name="Theme.ALN.AppWidgetContainer" parent="Theme.ALN.AppWidgetContainerParent">

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/app_widget_description"
android:initialKeyguardLayout="@layout/battery_widget"
android:initialLayout="@layout/battery_widget"
android:minWidth="40dp"
android:minHeight="40dp"
android:previewImage="@drawable/example_appwidget_preview"
android:previewLayout="@layout/battery_widget"
android:resizeMode="horizontal|vertical"
android:targetCellWidth="1"
android:targetCellHeight="1"
android:updatePeriodMillis="86400000"
android:widgetCategory="home_screen|keyguard" />