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>
<service
android:name=".AirPodsService"
android:name=".services.AirPodsService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="connectedDevice"

View File

@@ -3,11 +3,12 @@ package me.kavishdevar.aln
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.util.Log
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() {
override fun onUpdate(
context: Context,
@@ -34,11 +35,36 @@ internal fun updateAppWidget(
appWidgetManager: AppWidgetManager,
appWidgetId: Int
) {
val widgetText = context.getString(R.string.appwidget_text)
// Construct the RemoteViews object
val views = RemoteViews(context.packageName, R.layout.battery_widget)
views.setTextViewText(R.id.appwidget_text, widgetText)
val service = ServiceManager.getService()
val batteryList = service?.batteryNotification?.getBattery()
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)
}

View File

@@ -37,7 +37,9 @@ import com.google.accompanist.permissions.rememberMultiplePermissionsState
import me.kavishdevar.aln.screens.AirPodsSettingsScreen
import me.kavishdevar.aln.screens.DebugScreen
import me.kavishdevar.aln.screens.LongPress
import me.kavishdevar.aln.services.AirPodsService
import me.kavishdevar.aln.ui.theme.ALNTheme
import me.kavishdevar.aln.utils.AirPodsNotifications
lateinit var serviceConnection: ServiceConnection
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.unit.dp
import androidx.compose.ui.unit.sp
import me.kavishdevar.aln.AirPodsService
import me.kavishdevar.aln.services.AirPodsService
@Composable
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.unit.dp
import androidx.compose.ui.unit.sp
import me.kavishdevar.aln.AirPodsService
import me.kavishdevar.aln.services.AirPodsService
import kotlin.math.roundToInt
@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.unit.dp
import androidx.compose.ui.unit.sp
import me.kavishdevar.aln.AirPodsService
import me.kavishdevar.aln.services.AirPodsService
@Composable
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import me.kavishdevar.aln.AirPodsNotifications
import me.kavishdevar.aln.AirPodsService
import me.kavishdevar.aln.Battery
import me.kavishdevar.aln.BatteryComponent
import me.kavishdevar.aln.BatteryStatus
import me.kavishdevar.aln.utils.AirPodsNotifications
import me.kavishdevar.aln.services.AirPodsService
import me.kavishdevar.aln.utils.Battery
import me.kavishdevar.aln.utils.BatteryComponent
import me.kavishdevar.aln.utils.BatteryStatus
import me.kavishdevar.aln.R
@Composable

View File

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

View File

@@ -1,6 +1,5 @@
package me.kavishdevar.aln.composables
import me.kavishdevar.aln.R
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import me.kavishdevar.aln.R
@Composable
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.unit.dp
import androidx.compose.ui.unit.sp
import me.kavishdevar.aln.AirPodsNotifications
import me.kavishdevar.aln.AirPodsService
import me.kavishdevar.aln.NoiseControlMode
import me.kavishdevar.aln.utils.AirPodsNotifications
import me.kavishdevar.aln.services.AirPodsService
import me.kavishdevar.aln.utils.NoiseControlMode
import me.kavishdevar.aln.R
@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.unit.dp
import androidx.compose.ui.unit.sp
import me.kavishdevar.aln.AirPodsService
import me.kavishdevar.aln.services.AirPodsService
@Composable
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.unit.dp
import androidx.compose.ui.unit.sp
import me.kavishdevar.aln.AirPodsService
import me.kavishdevar.aln.services.AirPodsService
@Composable
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.unit.dp
import androidx.compose.ui.unit.sp
import me.kavishdevar.aln.AirPodsService
import me.kavishdevar.aln.services.AirPodsService
import me.kavishdevar.aln.R
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.unit.dp
import androidx.compose.ui.unit.sp
import me.kavishdevar.aln.AirPodsService
import me.kavishdevar.aln.services.AirPodsService
@Composable
fun VolumeControlSwitch(service: AirPodsService, sharedPreferences: SharedPreferences) {

View File

@@ -3,7 +3,7 @@ package me.kavishdevar.aln.receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import me.kavishdevar.aln.AirPodsService
import me.kavishdevar.aln.services.AirPodsService
class BootReceiver: BroadcastReceiver() {
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.rememberScrollState
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.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.Text
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.platform.LocalContext
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.input.TextFieldValue
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.materials.CupertinoMaterials
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import me.kavishdevar.aln.AirPodsNotifications
import me.kavishdevar.aln.AirPodsService
import me.kavishdevar.aln.R
import me.kavishdevar.aln.composables.AccessibilitySettings
import me.kavishdevar.aln.composables.AudioSettings
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.PressAndHoldSettings
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.utils.AirPodsNotifications
@OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class)
@SuppressLint("MissingPermission", "NewApi")
@@ -88,7 +97,13 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService,
CenterAlignedTopAppBar(
title = {
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
@@ -116,23 +131,23 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService,
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
containerColor = Color.Transparent
),
// actions = {
// val context = LocalContext.current
// IconButton(
// onClick = {
// ServiceManager.restartService(context)
// },
// colors = IconButtonDefaults.iconButtonColors(
// containerColor = Color.Transparent,
// contentColor = if (isSystemInDarkTheme()) Color.White else Color.Black
// )
// ) {
// Icon(
// imageVector = Icons.Default.Refresh,
// contentDescription = "Settings",
// )
// }
// }
actions = {
val context = LocalContext.current
IconButton(
onClick = {
ServiceManager.restartService(context)
},
colors = IconButtonDefaults.iconButtonColors(
containerColor = Color.Transparent,
contentColor = if (isSystemInDarkTheme()) Color.White else Color.Black
)
) {
Icon(
imageVector = Icons.Default.Refresh,
contentDescription = "Settings",
)
}
}
)
}
) { paddingValues ->
@@ -228,7 +243,8 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService,
style = TextStyle(
fontSize = 24.sp,
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,
modifier = Modifier.fillMaxWidth()
@@ -239,7 +255,8 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService,
style = TextStyle(
fontSize = 16.sp,
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,
modifier = Modifier.fillMaxWidth()
@@ -253,9 +270,13 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService,
@Preview
@Composable
fun AirPodsSettingsScreenPreview() {
ALNTheme (
darkTheme = true
Column (
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.materials.CupertinoMaterials
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import me.kavishdevar.aln.AirPodsNotifications
import me.kavishdevar.aln.AirPodsService
import me.kavishdevar.aln.utils.AirPodsNotifications
import me.kavishdevar.aln.services.AirPodsService
import me.kavishdevar.aln.R
@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.navigation.NavController
import me.kavishdevar.aln.R
import me.kavishdevar.aln.ServiceManager
import me.kavishdevar.aln.services.ServiceManager
@Composable()
fun RightDivider() {

View File

@@ -8,9 +8,8 @@ import android.content.IntentFilter
import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
import android.util.Log
import me.kavishdevar.aln.AirPodsNotifications
import me.kavishdevar.aln.NoiseControlMode
import me.kavishdevar.aln.ServiceManager
import me.kavishdevar.aln.utils.AirPodsNotifications
import me.kavishdevar.aln.utils.NoiseControlMode
class AirPodsQSService: TileService() {
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.app.Notification
@@ -25,6 +25,14 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
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 org.lsposed.hiddenapibypass.HiddenApiBypass
@@ -38,11 +46,11 @@ object ServiceManager {
fun setService(service: AirPodsService?) {
this.service = service
}
// @Synchronized
// fun restartService(context: Context) {
// service?.stopSelf()
// context.startService(Intent(context, AirPodsService::class.java))
// }
@Synchronized
fun restartService(context: Context) {
service?.stopSelf()
context.startService(Intent(context, AirPodsService::class.java))
}
}
@Suppress("unused")
@@ -84,7 +92,7 @@ class AirPodsService: Service() {
}
val uuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a")
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("device", bluetoothDevice)
context?.sendBroadcast(intent)
@@ -94,7 +102,7 @@ class AirPodsService: Service() {
|| BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED == action
) {
context?.sendBroadcast(
Intent(AirPodsNotifications.AIRPODS_DISCONNECTED)
Intent(AirPodsNotifications.Companion.AIRPODS_DISCONNECTED)
)
}
}
@@ -195,7 +203,6 @@ class AirPodsService: Service() {
.build()
}
// Notify the NotificationManager with the same ID
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.PLAYING_STATE_CHANGED")
}
registerReceiver(bluetoothReceiver, serviceIntentFilter, RECEIVER_EXPORTED)
connectionReceiver = object: BroadcastReceiver() {
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)
.getString("name", device?.name)
Log.d("AirPodsService", "$name connected")
@@ -232,7 +240,7 @@ class AirPodsService: Service() {
connectToSocket(device!!)
updateNotificationContent(true, name.toString(), batteryNotification.getBattery())
}
else if (intent?.action == AirPodsNotifications.AIRPODS_DISCONNECTED) {
else if (intent?.action == AirPodsNotifications.Companion.AIRPODS_DISCONNECTED) {
device = null
isConnected = false
popupShown = false
@@ -242,8 +250,8 @@ class AirPodsService: Service() {
}
val deviceIntentFilter = IntentFilter().apply {
addAction(AirPodsNotifications.AIRPODS_CONNECTION_DETECTED)
addAction(AirPodsNotifications.AIRPODS_DISCONNECTED)
addAction(AirPodsNotifications.Companion.AIRPODS_CONNECTION_DETECTED)
addAction(AirPodsNotifications.Companion.AIRPODS_DISCONNECTED)
}
registerReceiver(connectionReceiver, deviceIntentFilter, RECEIVER_EXPORTED)
@@ -257,7 +265,7 @@ class AirPodsService: Service() {
if (connectedDevices.isNotEmpty()) {
connectToSocket(device)
this@AirPodsService.sendBroadcast(
Intent(AirPodsNotifications.AIRPODS_CONNECTED)
Intent(AirPodsNotifications.Companion.AIRPODS_CONNECTED)
)
}
}
@@ -348,7 +356,7 @@ class AirPodsService: Service() {
it.outputStream.flush()
delay(200)
sendBroadcast(
Intent(AirPodsNotifications.AIRPODS_CONNECTED)
Intent(AirPodsNotifications.Companion.AIRPODS_CONNECTED)
.putExtra("device", device)
)
@@ -362,7 +370,7 @@ class AirPodsService: Service() {
var data: ByteArray = byteArrayOf()
if (bytesRead > 0) {
data = buffer.copyOfRange(0, bytesRead)
sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DATA).apply {
sendBroadcast(Intent(AirPodsNotifications.Companion.AIRPODS_DATA).apply {
putExtra("data", buffer.copyOfRange(0, bytesRead))
})
val bytes = buffer.copyOfRange(0, bytesRead)
@@ -371,14 +379,14 @@ class AirPodsService: Service() {
} else if (bytesRead == -1) {
Log.d("AirPods Service", "Socket closed (bytesRead = -1)")
// socket.close()
sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED))
sendBroadcast(Intent(AirPodsNotifications.Companion.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 {
sendBroadcast(Intent(AirPodsNotifications.Companion.EAR_DETECTION_DATA).apply {
val list = earDetectionNotification.status
val bytes = ByteArray(2)
bytes[0] = list[0]
@@ -463,7 +471,7 @@ class AirPodsService: Service() {
}
val earIntentFilter =
IntentFilter(AirPodsNotifications.EAR_DETECTION_DATA)
IntentFilter(AirPodsNotifications.Companion.EAR_DETECTION_DATA)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
this@AirPodsService.registerReceiver(
earReceiver, earIntentFilter,
@@ -477,13 +485,13 @@ class AirPodsService: Service() {
}
} else if (ancNotification.isANCData(data)) {
ancNotification.setStatus(data)
sendBroadcast(Intent(AirPodsNotifications.ANC_DATA).apply {
sendBroadcast(Intent(AirPodsNotifications.Companion.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 {
sendBroadcast(Intent(AirPodsNotifications.Companion.BATTERY_DATA).apply {
putParcelableArrayListExtra(
"data",
ArrayList(batteryNotification.getBattery())
@@ -513,7 +521,7 @@ class AirPodsService: Service() {
)
) {
conversationAwarenessNotification.setData(data)
sendBroadcast(Intent(AirPodsNotifications.CA_DATA).apply {
sendBroadcast(Intent(AirPodsNotifications.Companion.CA_DATA).apply {
putExtra("data", conversationAwarenessNotification.status)
})
@@ -535,7 +543,7 @@ class AirPodsService: Service() {
Log.d("AirPods Service", "Socket closed")
isConnected = false
socket.close()
sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED))
sendBroadcast(Intent(AirPodsNotifications.Companion.AIRPODS_DISCONNECTED))
}
}
} catch (e: Exception) {

View File

@@ -1,6 +1,6 @@
@file:Suppress("unused")
package me.kavishdevar.aln
package me.kavishdevar.aln.utils
import android.os.Parcelable
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.AnimatorListenerAdapter
@@ -22,7 +22,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.lang.Exception
import me.kavishdevar.aln.R
@SuppressLint("InflateParams", "ClickableViewAccessibility")
class Window (context: Context) {

View File

@@ -4,7 +4,6 @@ appWidgetInnerRadius attribute value
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="?attr/appWidgetInnerRadius" />
<solid android:color="?android:attr/colorAccent" />
</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"
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="@color/popup_text"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/appwidget_text"
style="@style/Widget.ALN.AppWidget.InnerView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_margin="8dp"
android:contentDescription="@string/appwidget_text"
android:text="@string/appwidget_text"
android:textSize="24sp"
android:textStyle="bold|italic" />
<TextView
android:id="@+id/left_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="Left"
tools:ignore="HardcodedText" />
<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>

View File

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

View File

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

View File

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

View File

@@ -5,12 +5,12 @@
<style name="Theme.ALN.AppWidgetContainerParent" parent="@android:style/Theme.DeviceDefault">
<!-- 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.
It needs to be 8dp or less than the value of appWidgetRadius
-->
<item name="appWidgetInnerRadius">8dp</item>
<item name="appWidgetInnerRadius">24dp</item>
</style>
<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" />