@@ -146,9 +146,35 @@ object ServiceManager {
146146}
147147
148148// @Suppress("unused")
149- class AirPodsService : Service () {
149+ class AirPodsService : Service (), SharedPreferences.OnSharedPreferenceChangeListener {
150150 var macAddress = " "
151151
152+ data class ServiceConfig (
153+ var deviceName : String = " AirPods" ,
154+ var earDetectionEnabled : Boolean = true ,
155+ var conversationalAwarenessPauseMusic : Boolean = false ,
156+ var personalizedVolume : Boolean = false ,
157+ var longPressNC : Boolean = true ,
158+ var offListeningMode : Boolean = false ,
159+ var showPhoneBatteryInWidget : Boolean = true ,
160+ var singleANC : Boolean = true ,
161+ var longPressTransparency : Boolean = true ,
162+ var conversationalAwareness : Boolean = true ,
163+ var relativeConversationalAwarenessVolume : Boolean = true ,
164+ var longPressAdaptive : Boolean = true ,
165+ var loudSoundReduction : Boolean = true ,
166+ var longPressOff : Boolean = false ,
167+ var volumeControl : Boolean = true ,
168+ var headGestures : Boolean = true ,
169+ var adaptiveStrength : Int = 51 ,
170+ var toneVolume : Int = 75 ,
171+ var conversationalAwarenessVolume : Int = 43 ,
172+ var textColor : Long = -1L ,
173+ var qsClickBehavior : String = " cycle"
174+ )
175+
176+ private lateinit var config: ServiceConfig
177+
152178 inner class LocalBinder : Binder () {
153179 fun getService (): AirPodsService = this @AirPodsService
154180 }
@@ -170,6 +196,76 @@ class AirPodsService : Service() {
170196
171197 inMemoryLogs.addAll(sharedPreferencesLogs.getStringSet(packetLogKey, emptySet()) ? : emptySet())
172198 _packetLogsFlow .value = inMemoryLogs.toSet()
199+
200+ sharedPreferences = getSharedPreferences(" settings" , MODE_PRIVATE )
201+ initializeConfig()
202+
203+ sharedPreferences.registerOnSharedPreferenceChangeListener(this )
204+ }
205+
206+ private fun initializeConfig () {
207+ config = ServiceConfig (
208+ deviceName = sharedPreferences.getString(" name" , " AirPods" ) ? : " AirPods" ,
209+ earDetectionEnabled = sharedPreferences.getBoolean(" automatic_ear_detection" , true ),
210+ conversationalAwarenessPauseMusic = sharedPreferences.getBoolean(" conversational_awareness_pause_music" , false ),
211+ personalizedVolume = sharedPreferences.getBoolean(" personalized_volume" , false ),
212+ longPressNC = sharedPreferences.getBoolean(" long_press_nc" , true ),
213+ offListeningMode = sharedPreferences.getBoolean(" off_listening_mode" , false ),
214+ showPhoneBatteryInWidget = sharedPreferences.getBoolean(" show_phone_battery_in_widget" , true ),
215+ singleANC = sharedPreferences.getBoolean(" single_anc" , true ),
216+ longPressTransparency = sharedPreferences.getBoolean(" long_press_transparency" , true ),
217+ conversationalAwareness = sharedPreferences.getBoolean(" conversational_awareness" , true ),
218+ relativeConversationalAwarenessVolume = sharedPreferences.getBoolean(" relative_conversational_awareness_volume" , true ),
219+ longPressAdaptive = sharedPreferences.getBoolean(" long_press_adaptive" , true ),
220+ loudSoundReduction = sharedPreferences.getBoolean(" loud_sound_reduction" , true ),
221+ longPressOff = sharedPreferences.getBoolean(" long_press_off" , false ),
222+ volumeControl = sharedPreferences.getBoolean(" volume_control" , true ),
223+ headGestures = sharedPreferences.getBoolean(" head_gestures" , true ),
224+ adaptiveStrength = sharedPreferences.getInt(" adaptive_strength" , 51 ),
225+ toneVolume = sharedPreferences.getInt(" tone_volume" , 75 ),
226+ conversationalAwarenessVolume = sharedPreferences.getInt(" conversational_awareness_volume" , 43 ),
227+ textColor = sharedPreferences.getLong(" textColor" , - 1L ),
228+ qsClickBehavior = sharedPreferences.getString(" qs_click_behavior" , " cycle" ) ? : " cycle"
229+ )
230+ }
231+
232+ override fun onSharedPreferenceChanged (preferences : SharedPreferences ? , key : String? ) {
233+ if (preferences == null || key == null ) return
234+
235+ when (key) {
236+ " name" -> config.deviceName = preferences.getString(key, " AirPods" ) ? : " AirPods"
237+ " automatic_ear_detection" -> config.earDetectionEnabled = preferences.getBoolean(key, true )
238+ " conversational_awareness_pause_music" -> config.conversationalAwarenessPauseMusic = preferences.getBoolean(key, false )
239+ " personalized_volume" -> config.personalizedVolume = preferences.getBoolean(key, false )
240+ " long_press_nc" -> config.longPressNC = preferences.getBoolean(key, true )
241+ " off_listening_mode" -> {
242+ config.offListeningMode = preferences.getBoolean(key, false )
243+ updateNoiseControlWidget()
244+ }
245+ " show_phone_battery_in_widget" -> {
246+ config.showPhoneBatteryInWidget = preferences.getBoolean(key, true )
247+ widgetMobileBatteryEnabled = config.showPhoneBatteryInWidget
248+ updateBattery()
249+ }
250+ " single_anc" -> config.singleANC = preferences.getBoolean(key, true )
251+ " long_press_transparency" -> config.longPressTransparency = preferences.getBoolean(key, true )
252+ " conversational_awareness" -> config.conversationalAwareness = preferences.getBoolean(key, true )
253+ " relative_conversational_awareness_volume" -> config.relativeConversationalAwarenessVolume = preferences.getBoolean(key, true )
254+ " long_press_adaptive" -> config.longPressAdaptive = preferences.getBoolean(key, true )
255+ " loud_sound_reduction" -> config.loudSoundReduction = preferences.getBoolean(key, true )
256+ " long_press_off" -> config.longPressOff = preferences.getBoolean(key, false )
257+ " volume_control" -> config.volumeControl = preferences.getBoolean(key, true )
258+ " head_gestures" -> config.headGestures = preferences.getBoolean(key, true )
259+ " adaptive_strength" -> config.adaptiveStrength = preferences.getInt(key, 51 )
260+ " tone_volume" -> config.toneVolume = preferences.getInt(key, 75 )
261+ " conversational_awareness_volume" -> config.conversationalAwarenessVolume = preferences.getInt(key, 43 )
262+ " textColor" -> config.textColor = preferences.getLong(key, - 1L )
263+ " qs_click_behavior" -> config.qsClickBehavior = preferences.getString(key, " cycle" ) ? : " cycle"
264+ }
265+
266+ if (key == " mac_address" ) {
267+ macAddress = preferences.getString(key, " " ) ? : " "
268+ }
173269 }
174270
175271 private fun logPacket (packet : ByteArray , source : String ) {
@@ -541,7 +637,7 @@ class AirPodsService : Service() {
541637 it.setInt(
542638 R .id.widget_transparency_button,
543639 " setBackgroundResource" ,
544- if (ancStatus == 3 ) (if (sharedPreferences.getBoolean( " off_listening_mode " , true )) R .drawable.widget_button_checked_shape_middle else R .drawable.widget_button_checked_shape_start) else (if (sharedPreferences.getBoolean( " off_listening_mode " , true ) ) R .drawable.widget_button_shape_middle else R .drawable.widget_button_shape_start)
640+ if (ancStatus == 3 ) (if (config.offListeningMode) R .drawable.widget_button_checked_shape_middle else R .drawable.widget_button_checked_shape_start) else (if (config.offListeningMode ) R .drawable.widget_button_shape_middle else R .drawable.widget_button_shape_start)
545641 )
546642 it.setInt(
547643 R .id.widget_adaptive_button,
@@ -555,19 +651,19 @@ class AirPodsService : Service() {
555651 )
556652 it.setViewVisibility(
557653 R .id.widget_off_button,
558- if (sharedPreferences.getBoolean( " off_listening_mode " , true ) ) View .VISIBLE else View .GONE
654+ if (config.offListeningMode ) View .VISIBLE else View .GONE
559655 )
560656 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .S ) {
561657 it.setViewLayoutMargin(
562658 R .id.widget_transparency_button,
563659 RemoteViews .MARGIN_START ,
564- if (sharedPreferences.getBoolean( " off_listening_mode " , true ) ) 2f else 12f ,
660+ if (config.offListeningMode ) 2f else 12f ,
565661 TypedValue .COMPLEX_UNIT_DIP
566662 )
567663 } else {
568664 it.setViewPadding(
569665 R .id.widget_transparency_button,
570- if (sharedPreferences.getBoolean( " off_listening_mode " , true ) ) 2 .dpToPx() else 12 .dpToPx(),
666+ if (config.offListeningMode ) 2 .dpToPx() else 12 .dpToPx(),
571667 12 .dpToPx(),
572668 2 .dpToPx(),
573669 12 .dpToPx()
@@ -598,7 +694,7 @@ class AirPodsService : Service() {
598694 if (connected) {
599695 updatedNotification = NotificationCompat .Builder (this , " background_service_status" )
600696 .setSmallIcon(R .drawable.airpods)
601- .setContentTitle(airpodsName)
697+ .setContentTitle(airpodsName ? : config.deviceName )
602698 .setContentText(
603699 """ ${
604700 batteryList?.find { it.component == BatteryComponent .LEFT }?.let {
@@ -648,14 +744,14 @@ class AirPodsService : Service() {
648744 @RequiresApi(Build .VERSION_CODES .Q )
649745 fun handleIncomingCall () {
650746 if (isInCall) return
651-
652- initGestureDetector()
653-
654- gestureDetector?.startDetection { accepted ->
655- if (accepted) {
656- answerCall()
657- } else {
658- rejectCall()
747+ if (config.headGestures) {
748+ initGestureDetector()
749+ gestureDetector?.startDetection { accepted ->
750+ if ( accepted) {
751+ answerCall()
752+ } else {
753+ rejectCall()
754+ }
659755 }
660756 }
661757 }
@@ -943,7 +1039,7 @@ class AirPodsService : Service() {
9431039 editor.apply ()
9441040 }
9451041
946- earDetectionEnabled = sharedPreferences.getBoolean( " automatic_ear_detection " , true )
1042+ initializeConfig( )
9471043
9481044 ancModeReceiver = object : BroadcastReceiver () {
9491045 override fun onReceive (context : Context ? , intent : Intent ? ) {
@@ -955,7 +1051,7 @@ class AirPodsService : Service() {
9551051 }
9561052 } else {
9571053 val currentMode = ancNotification.status
958- val offListeningMode = sharedPreferences.getBoolean( " off_listening_mode " , true )
1054+ val offListeningMode = config.offListeningMode
9591055
9601056 val nextMode = if (offListeningMode) {
9611057 when (currentMode) {
@@ -1013,7 +1109,7 @@ class AirPodsService : Service() {
10131109 when (state) {
10141110 TelephonyManager .CALL_STATE_RINGING -> {
10151111 if (CrossDevice .isAvailable && ! isConnectedLocally && earDetectionNotification.status.contains(0x00 )) takeOver()
1016- if (sharedPreferences.getBoolean( " head_gestures " , false ) ) {
1112+ if (config.headGestures ) {
10171113 callNumber = phoneNumber
10181114 handleIncomingCall()
10191115 }
@@ -1032,7 +1128,7 @@ class AirPodsService : Service() {
10321128 }
10331129 telephonyManager.listen(phoneStateListener, PhoneStateListener .LISTEN_CALL_STATE )
10341130
1035- if (sharedPreferences.getBoolean( " show_phone_battery_in_widget " , true ) ) {
1131+ if (config.showPhoneBatteryInWidget ) {
10361132 widgetMobileBatteryEnabled = true
10371133 val batteryChangedIntentFilter = IntentFilter (Intent .ACTION_BATTERY_CHANGED )
10381134 batteryChangedIntentFilter.addAction(AirPodsNotifications .DISCONNECT_RECEIVERS )
@@ -1067,19 +1163,16 @@ class AirPodsService : Service() {
10671163 } else {
10681164 intent.getParcelableExtra(" device" ) as BluetoothDevice ?
10691165 }
1070- val name = this @AirPodsService.getSharedPreferences(" settings" , MODE_PRIVATE )
1071- .getString(" name" , device?.name)
1072- if (this @AirPodsService.getSharedPreferences(" settings" , MODE_PRIVATE )
1073- .getString(" name" , null ) == null
1074- ) {
1075- this @AirPodsService.getSharedPreferences(" settings" , MODE_PRIVATE ).edit {
1076- putString(" name" , name)
1077- }
1166+
1167+ if (config.deviceName == " AirPods" && device?.name != null ) {
1168+ config.deviceName = device?.name ? : " AirPods"
1169+ sharedPreferences.edit { putString(" name" , config.deviceName) }
10781170 }
1171+
10791172 Log .d(" AirPodsCrossDevice" , CrossDevice .isAvailable.toString())
10801173 if (! CrossDevice .isAvailable) {
1081- Log .d(" AirPodsService" , " $name connected" )
1082- showPopup(this @AirPodsService, name.toString() )
1174+ Log .d(" AirPodsService" , " ${config.deviceName} connected" )
1175+ showPopup(this @AirPodsService, config.deviceName )
10831176 connectToSocket(device!! )
10841177 Log .d(" AirPodsService" , " Setting metadata" )
10851178 setMetadatas(device!! )
@@ -1090,7 +1183,7 @@ class AirPodsService : Service() {
10901183 }
10911184 updateNotificationContent(
10921185 true ,
1093- name.toString() ,
1186+ config.deviceName ,
10941187 batteryNotification.getBattery()
10951188 )
10961189 }
@@ -1351,7 +1444,7 @@ class AirPodsService : Service() {
13511444 earReceiver = object : BroadcastReceiver () {
13521445 override fun onReceive (context : Context , intent : Intent ) {
13531446 val data = intent.getByteArrayExtra(" data" )
1354- if (data != null && earDetectionEnabled) {
1447+ if (data != null && config. earDetectionEnabled) {
13551448 inEar =
13561449 if (data.find { it == 0x02 .toByte() } != null || data.find { it == 0x03 .toByte() } != null ) {
13571450 data[0 ] == 0x00 .toByte() || data[1 ] == 0x00 .toByte()
@@ -1657,6 +1750,11 @@ class AirPodsService : Service() {
16571750 0x00
16581751 )
16591752 )
1753+
1754+ if (config.offListeningMode != enabled) {
1755+ config.offListeningMode = enabled
1756+ sharedPreferences.edit { putBoolean(" off_listening_mode" , enabled) }
1757+ }
16601758 updateNoiseControlWidget()
16611759 }
16621760
@@ -1676,6 +1774,11 @@ class AirPodsService : Service() {
16761774 0x00
16771775 )
16781776 sendPacket(bytes)
1777+
1778+ if (config.adaptiveStrength != strength) {
1779+ config.adaptiveStrength = strength
1780+ sharedPreferences.edit { putInt(" adaptive_strength" , strength) }
1781+ }
16791782 }
16801783
16811784 fun setPressSpeed (speed : Int ) {
@@ -1741,12 +1844,22 @@ class AirPodsService : Service() {
17411844 0x00
17421845 )
17431846 sendPacket(bytes)
1847+
1848+ if (config.volumeControl != enabled) {
1849+ config.volumeControl = enabled
1850+ sharedPreferences.edit { putBoolean(" volume_control" , enabled) }
1851+ }
17441852 }
17451853
17461854 fun setToneVolume (volume : Int ) {
17471855 val bytes =
17481856 byteArrayOf(0x04 , 0x00 , 0x04 , 0x00 , 0x09 , 0x00 , 0x1F , volume.toByte(), 0x50 , 0x00 , 0x00 )
17491857 sendPacket(bytes)
1858+
1859+ if (config.toneVolume != volume) {
1860+ config.toneVolume = volume
1861+ sharedPreferences.edit { putInt(" tone_volume" , volume) }
1862+ }
17501863 }
17511864
17521865 val earDetectionNotification = AirPodsNotifications .EarDetection ()
@@ -1755,10 +1868,11 @@ class AirPodsService : Service() {
17551868 val conversationAwarenessNotification =
17561869 AirPodsNotifications .ConversationalAwarenessNotification ()
17571870
1758- var earDetectionEnabled = true
1759-
17601871 fun setEarDetection (enabled : Boolean ) {
1761- earDetectionEnabled = enabled
1872+ if (config.earDetectionEnabled != enabled) {
1873+ config.earDetectionEnabled = enabled
1874+ sharedPreferences.edit { putBoolean(" automatic_ear_detection" , enabled) }
1875+ }
17621876 }
17631877
17641878 fun getBattery (): List <Battery > {
@@ -1865,6 +1979,12 @@ class AirPodsService : Service() {
18651979 ) + nameBytes
18661980 sendPacket(bytes)
18671981 val hex = bytes.joinToString(" " ) { " %02X" .format(it) }
1982+
1983+ if (config.deviceName != name) {
1984+ config.deviceName = name
1985+ sharedPreferences.edit { putString(" name" , name) }
1986+ }
1987+
18681988 updateNotificationContent(true , name, batteryNotification.getBattery())
18691989 Log .d(" AirPodsService" , " setName: $name , sent packet: $hex " )
18701990 }
@@ -1877,12 +1997,22 @@ class AirPodsService : Service() {
18771997 " 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"
18781998 bytes = hex.split(" " ).map { it.toInt(16 ).toByte() }.toByteArray()
18791999 sendPacket(bytes)
2000+
2001+ if (config.personalizedVolume != enabled) {
2002+ config.personalizedVolume = enabled
2003+ sharedPreferences.edit { putBoolean(" personalized_volume" , enabled) }
2004+ }
18802005 }
18812006
18822007 fun setLoudSoundReduction (enabled : Boolean ) {
18832008 val hex = " 52 1B 00 0${if (enabled) " 1" else " 0" } "
18842009 val bytes = hex.split(" " ).map { it.toInt(16 ).toByte() }.toByteArray()
18852010 sendPacket(bytes)
2011+
2012+ if (config.loudSoundReduction != enabled) {
2013+ config.loudSoundReduction = enabled
2014+ sharedPreferences.edit { putBoolean(" loud_sound_reduction" , enabled) }
2015+ }
18862016 }
18872017
18882018 fun findChangedIndex (oldArray : BooleanArray , newArray : BooleanArray ): Int {
@@ -2036,6 +2166,9 @@ class AirPodsService : Service() {
20362166 override fun onDestroy () {
20372167 clearPacketLogs()
20382168 Log .d(" AirPodsService" , " Service stopped is being destroyed for some reason!" )
2169+
2170+ sharedPreferences.unregisterOnSharedPreferenceChangeListener(this )
2171+
20392172 try {
20402173 unregisterReceiver(bluetoothReceiver)
20412174 } catch (e: Exception ) {
0 commit comments