From 7d58fb502ac172de46ab0e2f75c1b68fef057066 Mon Sep 17 00:00:00 2001 From: Kavish Devar Date: Mon, 20 Apr 2026 04:11:16 +0530 Subject: [PATCH] android: 'testing' for Play relase yeah... no big changes, unfortunately --- android/app/build.gradle.kts | 96 +- android/app/libs/backdrop-debug.aar | Bin 128338 -> 131950 bytes android/app/libs/backdrop-release.aar | Bin 121657 -> 125139 bytes android/app/proguard-rules.pro | 5 +- android/app/src/main/AndroidManifest.xml | 42 +- android/app/src/main/cpp/CMakeLists.txt | 77 +- ...e_constructor.cpp => bluetooth_socket.cpp} | 0 .../me/kavishdevar/librepods/MainActivity.kt | 303 ++-- .../librepods/billing/BillingManager.kt | 5 + .../librepods/billing/BillingProvider.kt | 28 + .../billing/BillingProviderFactory.kt | 33 + .../librepods/billing/FOSSBillingProvider.kt | 30 + .../librepods/billing/PlayBillingProvider.kt | 187 +++ .../librepods/composables/AboutCard.kt | 40 +- .../librepods/composables/AudioSettings.kt | 76 +- .../librepods/composables/BatteryView.kt | 175 +- .../composables/CallControlSettings.kt | 89 +- .../composables/ConnectionSettings.kt | 31 +- .../composables/HearingHealthSettings.kt | 118 +- .../composables/MicrophoneSettings.kt | 52 +- .../composables/NoiseControlSettings.kt | 116 +- .../composables/PressAndHoldSettings.kt | 28 +- .../librepods/composables/StyledButton.kt | 9 +- .../librepods/composables/StyledIconButton.kt | 17 +- .../librepods/composables/StyledScaffold.kt | 3 - .../librepods/composables/StyledSelectList.kt | 32 +- .../librepods/composables/StyledSlider.kt | 194 ++- .../librepods/composables/StyledSwitch.kt | 21 +- .../librepods/composables/StyledToggle.kt | 507 +----- .../librepods/constants/Packets.kt | 2 + .../data/ControlCommandRepository.kt | 63 + .../screens/AccessibilitySettingsScreen.kt | 257 +-- .../screens/AdaptiveStrengthScreen.kt | 86 +- .../screens/AirPodsSettingsScreen.kt | 471 +++--- .../librepods/screens/AppSettingsScreen.kt | 884 ++-------- .../librepods/screens/CameraControlScreen.kt | 109 +- .../librepods/screens/DebugScreen.kt | 1 - .../librepods/screens/HeadTrackingScreen.kt | 63 +- .../screens/HearingAidAdjustmentsScreen.kt | 56 +- .../librepods/screens/HearingAidScreen.kt | 66 +- .../screens/HearingProtectionScreen.kt | 65 +- .../screens/PressAndHoldSettingsScreen.kt | 120 +- .../librepods/screens/RenameScreen.kt | 16 +- .../screens/TransparencySettingsScreen.kt | 60 +- .../screens/UpdateHearingTestScreen.kt | 88 +- .../librepods/screens/VersionInfoScreen.kt | 37 +- .../librepods/services/AirPodsQSService.kt | 2 + .../librepods/services/AirPodsService.kt | 1450 ++++++++++------- .../kavishdevar/librepods/ui/theme/Theme.kt | 2 +- .../librepods/utils/AACPManager.kt | 35 +- .../librepods/utils/CrossDevice.kt | 289 ---- .../librepods/utils/IslandWindow.kt | 7 +- .../librepods/utils/PopupWindow.kt | 6 +- .../librepods/utils/RootlessSupport.kt | 49 + .../librepods/utils/TransparencyUtils.kt | 4 +- .../librepods/viewmodel/AirPodsViewModel.kt | 417 +++++ .../viewmodel/AppSettingsViewModel.kt | 158 ++ android/app/src/main/res/value-it/strings.xml | 428 +++-- .../app/src/main/res/values-es/strings.xml | 4 - .../app/src/main/res/values-fr/strings.xml | 4 - .../app/src/main/res/values-pt/strings.xml | 4 - .../app/src/main/res/values-tr/strings.xml | 4 - .../app/src/main/res/values-uk/strings.xml | 4 - .../app/src/main/res/values-vi/strings.xml | 4 - .../src/main/res/values-zh-rCN/strings.xml | 6 +- .../src/main/res/values-zh-rTW/strings.xml | 6 +- android/app/src/main/res/values/strings.xml | 5 +- .../src/{main => xposed}/cpp/l2c_fcr_hook.cpp | 0 .../src/{main => xposed}/cpp/l2c_fcr_hook.h | 0 android/app/src/{main => xposed}/cpp/xz/xz.h | 0 .../src/{main => xposed}/cpp/xz/xz_config.h | 0 .../src/{main => xposed}/cpp/xz/xz_crc32.c | 0 .../src/{main => xposed}/cpp/xz/xz_crc64.c | 0 .../src/{main => xposed}/cpp/xz/xz_dec_bcj.c | 0 .../{main => xposed}/cpp/xz/xz_dec_lzma2.c | 0 .../{main => xposed}/cpp/xz/xz_dec_stream.c | 0 .../src/{main => xposed}/cpp/xz/xz_lzma2.h | 0 .../src/{main => xposed}/cpp/xz/xz_private.h | 0 .../src/{main => xposed}/cpp/xz/xz_sha256.c | 0 .../src/{main => xposed}/cpp/xz/xz_stream.h | 0 .../librepods/utils/KotlinModule.kt | 0 .../resources/META-INF/xposed/java_init.list | 0 .../resources/META-INF/xposed/module.prop | 0 .../META-INF/xposed/native_init.list | 0 .../resources/META-INF/xposed/scope.list | 0 android/build.gradle.kts | 4 +- android/gradle.properties | 17 +- android/gradle/libs.versions.toml | 44 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- 89 files changed, 3669 insertions(+), 4044 deletions(-) rename android/app/src/main/cpp/{socket_private_constructor.cpp => bluetooth_socket.cpp} (100%) create mode 100644 android/app/src/main/java/me/kavishdevar/librepods/billing/BillingManager.kt create mode 100644 android/app/src/main/java/me/kavishdevar/librepods/billing/BillingProvider.kt create mode 100644 android/app/src/main/java/me/kavishdevar/librepods/billing/BillingProviderFactory.kt create mode 100644 android/app/src/main/java/me/kavishdevar/librepods/billing/FOSSBillingProvider.kt create mode 100644 android/app/src/main/java/me/kavishdevar/librepods/billing/PlayBillingProvider.kt create mode 100644 android/app/src/main/java/me/kavishdevar/librepods/data/ControlCommandRepository.kt delete mode 100644 android/app/src/main/java/me/kavishdevar/librepods/utils/CrossDevice.kt create mode 100644 android/app/src/main/java/me/kavishdevar/librepods/utils/RootlessSupport.kt create mode 100644 android/app/src/main/java/me/kavishdevar/librepods/viewmodel/AirPodsViewModel.kt create mode 100644 android/app/src/main/java/me/kavishdevar/librepods/viewmodel/AppSettingsViewModel.kt rename android/app/src/{main => xposed}/cpp/l2c_fcr_hook.cpp (100%) rename android/app/src/{main => xposed}/cpp/l2c_fcr_hook.h (100%) rename android/app/src/{main => xposed}/cpp/xz/xz.h (100%) rename android/app/src/{main => xposed}/cpp/xz/xz_config.h (100%) rename android/app/src/{main => xposed}/cpp/xz/xz_crc32.c (100%) rename android/app/src/{main => xposed}/cpp/xz/xz_crc64.c (100%) rename android/app/src/{main => xposed}/cpp/xz/xz_dec_bcj.c (100%) rename android/app/src/{main => xposed}/cpp/xz/xz_dec_lzma2.c (100%) rename android/app/src/{main => xposed}/cpp/xz/xz_dec_stream.c (100%) rename android/app/src/{main => xposed}/cpp/xz/xz_lzma2.h (100%) rename android/app/src/{main => xposed}/cpp/xz/xz_private.h (100%) rename android/app/src/{main => xposed}/cpp/xz/xz_sha256.c (100%) rename android/app/src/{main => xposed}/cpp/xz/xz_stream.h (100%) rename android/app/src/{main => xposed}/java/me/kavishdevar/librepods/utils/KotlinModule.kt (100%) rename android/app/src/{main => xposed}/resources/META-INF/xposed/java_init.list (100%) rename android/app/src/{main => xposed}/resources/META-INF/xposed/module.prop (100%) rename android/app/src/{main => xposed}/resources/META-INF/xposed/native_init.list (100%) rename android/app/src/{main => xposed}/resources/META-INF/xposed/scope.list (100%) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index d2ed2db..6d2aa93 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -1,42 +1,74 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import java.util.Properties + plugins { alias(libs.plugins.android.application) - alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) alias(libs.plugins.aboutLibraries) +// alias(libs.plugins.hilt) id("kotlin-parcelize") } +val props = Properties().apply { + load(rootProject.file("local.properties").inputStream()) +} + android { + signingConfigs { + create("release") { + storeFile = file(props["RELEASE_STORE_FILE"] as String) + storePassword = props["RELEASE_STORE_PASSWORD"] as String + keyAlias = props["RELEASE_KEY_ALIAS"] as String + keyPassword = props["RELEASE_KEY_PASSWORD"] as String + } + } namespace = "me.kavishdevar.librepods" - compileSdk = 36 + compileSdk = 37 defaultConfig { applicationId = "me.kavishdevar.librepods" - minSdk = 33 - targetSdk = 36 - versionCode = 10 - versionName = "0.2.0-alpha.2" + minSdk = 36 + targetSdk = 37 + versionCode = 21 + versionName = "0.2.0-beta.1" } - buildTypes { release { - isMinifyEnabled = false + isMinifyEnabled = true + isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) + externalNativeBuild { + cmake { + arguments += "-DCMAKE_BUILD_TYPE=Release" + } + } + signingConfig = signingConfigs.getByName("release") + } + debug { + signingConfig = signingConfigs.getByName("release") + } + create("playRelease") { + initWith(getByName("release")) + versionNameSuffix = "-play" + buildConfigField("Boolean", "PLAY_BUILD", "true") } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } - kotlinOptions { - jvmTarget = "1.8" + kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_21) + } } buildFeatures { compose = true viewBinding = true + buildConfig = true } androidResources { generateLocaleConfig = true @@ -49,17 +81,41 @@ android { } sourceSets { getByName("main") { - res.srcDirs("src/main/res", "src/main/res-apple") + res.directories+="src/main/res-apple" + } + } + + ndkVersion = "30.0.14904198" + + flavorDimensions += "env" + + productFlavors { + create("normal") { + dimension = "env" + externalNativeBuild { + cmake { + arguments += "-DIS_XPOSED=OFF" + } + } + } + create("xposed") { + dimension = "env" + externalNativeBuild { + cmake { + arguments += "-DIS_XPOSED=ON" + } + } + applicationIdSuffix = ".xposed" } } } dependencies { + implementation(platform(libs.androidx.compose.bom)) implementation(libs.accompanist.permissions) implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.activity.compose) - implementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.ui) implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.tooling.preview) @@ -71,15 +127,17 @@ dependencies { implementation(libs.haze.materials) implementation(libs.androidx.dynamicanimation) implementation(libs.androidx.compose.ui) + implementation(libs.androidx.compose.material.icons.core) + implementation(libs.billing) debugImplementation(libs.androidx.compose.ui.tooling) implementation(libs.androidx.compose.foundation.layout) implementation(libs.aboutlibraries) implementation(libs.aboutlibraries.compose.m3) - // compileOnly(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar")))) - // implementation(fileTree(mapOf("dir" to "lib", "include" to listOf("*.aar")))) - compileOnly(files("libs/libxposed-api-100.aar")) - debugImplementation(files("libs/backdrop-debug.aar")) - releaseImplementation(files("libs/backdrop-release.aar")) + implementation(libs.backdrop) + implementation(libs.hilt) +// implementation(libs.hilt.compiler) + add("xposedCompileOnly", files("libs/libxposed-api-100.aar")) + add("playReleaseImplementation", libs.billing) } aboutLibraries { diff --git a/android/app/libs/backdrop-debug.aar b/android/app/libs/backdrop-debug.aar index 9ed9a7164cdfd2a27f1b2f4cb017240611636508..2e53719d19edf8a2a4a2273304651b33f1ddd4cb 100644 GIT binary patch literal 131950 zcmV)CK*GOJO9KQH000OG0000%0000000IC20000000jU508%b=cyt2*P)h>@6aWAS z2mk;8K>%N`cE6JV0077U000vJ002R5WO8q5WKCgiX=Y_}bS`*pY&Fh73d0}}h2ecp zA+wJ*U6r88Aqw4FHHlb9aZG7%Un$8ff4+ey=TCgGw=3imDj4_$m_w7S$5p{zm;MIx z)XAvPFODi}9BI^OOY&C1R%;%GAhWrsi65Pt6ot+~6i4D{(6M$|o1j;xpa~wn8Y_Rc z)I@6aWAS2mk;8K>!$nSgr&B z0sxR40sspD003ibVRLh3b1rIOa*VqJurSTCCAw|f``fl{+qP|6-?nYrwr$(CZNGo+ zy>DX9#GNx!(HT+Iu~v6fWOZh(>?nCDU=V=+Xao=hKmh+sgZ#&2M3e<+BxFVDi2vZd7R7sH0|EfV{PP6b|DGrKUmap$ z6H@~hYi9`?duu0JBWnXECm(qkxlMiqA8c?%B;fr<8LHY|p2rg87O2_$Af$^~ET`Dd|IZMmZs) z3u%k}qUeOJHbD#C^pvlz`En&uEnn|YxI%UhYqkv@BlpLsN;pmew7RuHXljP4Z9fOX z?6DwZM4`$oFz4?Scz<|(eU9haq*^$Pf#2JixlX?8(Jh9iP|`%0%J78;C#4vZZ<#3Q zi=g_I20XL@O$*G9(vor|kE*ZDJ*&cO%H#Bgh%qruY_bzqhcL7aU+B7@1ikE)^#V)y zqu_^$ou5zmz?YQ){d=tK+?-IG2%QB$f|Pl&zp0OK>3?>yG4@cGX{?ceb`lE2vhKJ7 zFeVvP4XX;|Fs&F?eu4hg(SW>T;8(x^0MrowESP`q=>PQXw92j>wg|${i#G>XOro|l z@zB0 z5iEG}ma{x{7TvDo!MI+oL#gDi<&zM%%UL=;d4T73izQa|XtW>C4f zU&@hw$X95~s{32rT%%I+2`ifYQhmWYGf#I){i8Oo=@kdIts~2T_X2^%nvjVXF^t~Y z@D{-*_j~B7Pjfb!JKof}Er)0y8`Zq&kWdF#*9U!60{wx)YfW=Or(g3{J)IXwRgrWBOzyi%v9RPESospFDS|VqJ;{eif4GVQQLw1!BiO zmWVPb=4N?UCeEhKQ|R)&&(Q1PU<%7;XqmQo8-O+(r5#6ZTFB!v7KL9TK^eAj+JEqw zM;HuoluCvi`z!sVwqi6q$q2uG&si`Yd9Aw5Xv0teE9I~(0?6vOEl54>@spH72I`KG zg$nn=r0;n3$#><&pRUMZ_iR{xGG-@9)a(kC-gW~|$`}UkcdX{I5ygPqPad+%>q;pH z`p9_-lj7fpwFV*0cRTSK&aDcj7KmL&>=cW2eG~8FhTLbMhefwF(-hQ@slfsvR=;aY z5Xpfnk&Gwx>DK=Wna^eTvG6?rm;T}8NcuUD!nlg*vA1$4&-)Ei3%%n3 zwR8_Hg0u0)xJ_UQDcW^RD_Eh4lilaRGNbs08crE^3mw7Q5e|MjJ<2U1fz}dNVNe>j zMy=gjpk82~TC$A^2r#AS7 z{ntWf=_Rly8765?b--im3|HxxXMt1fl|4n*mR?$(~6h+~&A|u5}1j&%+)ijSt z)DI&Pf{?jnrs?;eV1eu3iz}hgnS_p~k;2n3{nJ~1{S<#LTxXp|Rz$L`6gI)SW)98^iDU^AP8tdd(p- z(M8K#8_|SC2p5N~qPge0I)R@{B>_sO1*fMBIcR7qi~wsR$0V;ZM=uLIzhNjrjg{gl{KC_r=zCV%ag0$2;z@c#Et$ry#n$VFk7k2T;@5LKwm-!{#1SOlQq`gm=fjt|)4L zB?=LIOWc-pa7im2z62lZQ&4U9NT`N&uQS&qCA^8=x*;Vo0G^M*_Na3{gA$5-}S z#(v9`0+&53Pjv|SP^DC}Hh9`ps+y+DGW@Qk9Uv-l5z_fUwYpS`da^q7v(l=_U81AV zQXCAKWv(hV4txW&%yidNI}JGA6)U%S0o&;?-lLZk7D|GEMl80Y2aG-;_yVE$UTpe_ zSn~d)8K8usbXC485ZCKct$)S;*Jva@dNH>P3IMPS|Ig;-|2Z1{X8KXT+Y&TrFav(a4lifvv3o zJJ_TGIGk*;m_v&W)kH;&L0wJ~{pA7hQ&60e@OouJTq0+o@GnT8AK*NWF_q1 zAD+*#+LmM*7r13kpS3lgr>tPris1g_(G=d8L1ErhosF`RV~l~Z^)dsv7N8WHF>RS8 zX5(ba!s__EG4_x+Ie(mGZ`;tb7A*r4->pak5Lo9fIKa@g1ce{N#4K`6lFhUfJu!z< zEN=A?Vah48eQdfX%nk_# z_fuXQoP=2WFJV)RyTw;8>CH;pr0jw_rdrTQW%Ex|s^Rf`L%J{havmQts^1L&#H2Xo zKIceK4)&4Ug6FnO(CVvsccH9a6d#sblOhDa|mAQcB2pUyC9cn~H#*^LrnjcyOh zGV>_3=r+3Xr=!=>MZ#a~N7|Uk42>mgD;b9udJr3>Dh$$!>2?je z$gj#|N%`$W%#MNaE(>&Lq4Wi7meLJM6>U9-VY%v6vY)R5_@qmZ`n;bDt>xhe+7>#u z`2E}IXKb1tUA^bl_8Z#4pKm#ir2}X-l_S8@_I5no^KEVX=VsOnnqF z1qNO&ua-cO>fs}5Yh36sUO7%9Y2Qi$$ZnZFOONu+6n3p2@)n)5N=+nIYHWNxunzpa z#Yl(80l+5&j5J+5BMjOVwU}au8v&ZMYle9T1m1!;RwT+r+)X-Ti_f>U!h{A@XWFId zhTdk}1q8;otC-7*>`&r=WHJHiA&+8PZ^){N_2yfj*^+H|$Uhd!8f_~NYAcoIW)lT%R9VfytN_}%AKQC6~gbbzj%k(oxevNaWD&b z!T13|LsFC0f$;d793hkEiI>=8(BmIsi~@*-ANs&W20Ar4isP_~r5_@QVUI$Mg5xlX zfsay-Vu_K7p^LGN8pM?1;5$Cq)qrRnz>E*Kru=1fNMWXFb%y%?9^Nz8$RAE`OCj2!PuY}6!7ktT*D({oq zc01Jxmt7eoFu-$Ex4KSSaykPOM`ioH)hY+1FOk#x&wG+=>@ioaTV%FjMT zMrkTm6I_XF8WT+FDQ@;&Bsag1DiIqz^fV=}9!4UpB0(CDB8{dQ@JdMMS$EI!s>+8T zp$gWV&?|fX3y)5r&JjN~WE6h)wq(qnGIn+zga$8$l+K(Q0}D-8){lk>r05+b&!X&EILa!tG>^Vnz+cd;09z&XC;4ERvdbu;5%X^8(K)mxNZ47`S} zQa7Ub&9PSOMJ)+uV!jyAR+cwfGm^D{vvl?+=ex*3DB2_8khL`5v1Xc4JFG@Iiutjm z$z8QpzrfshFKw>WhkYWB8b<3qube%K=z*L`c{xc^V8NQQ!UUtfMV*T_s?VcPcyWV) zpi#OllZR;6xdpETWF!yX2x=pHAj?Dr9Dr^_#d`QXIgMXiq;y@XtPO+eNL_Lm&UsvS zLr!Y=n1}$MA|umt?tl=*Xu+x!gQmN?I#dN`(1FkBtEot`vJC)zV&_rCn#qe!z8*Jq ztR9)twLt5|wkVVI$w)Oy;oT4#L$_z*jvux`%~7&@&`7$;Gf5!#;w+5`%cv-MG)E>9 zMOUi$U@B3D#;4_xD?tWBLLkV?v0H}Q2P)=^uEHjt5!l+yxBYucBzqIqxz-j1Wgt{b z`HCg^1q&72LA>I^C757DK$vcazczxTBmsG8jq}rh-YR}ZH530NM0qQMP30iky)j_f zBi|UtNuA%5=UZ>K4mai!tNIH~!^wI`>l`)xXy-8hr5e2{7N19M@3m;xQ2pqM_p1&@#AJ@55a6QJ_ z(cZ6jOQ0y3i#>QT!EdDH@g~W*5dyWwabjt$w5CY!tpYAH|Q9+R6S_?}O72+)En(2Fn$kHbDsZ+KO@rIt!P$BapW z_%O&DBM8cSGeiRgL!_fs0|RgNuU^OQSAHEOxCf`9!^&O!P*{%ar2-zWAEh26M7o>% zwt5<0{kNy65$nA8aYj}`Bkg-x96UX5LxUr3>^PgKwVHEc!Ism>P~5J{X~yR}nDTZ9K)sx**%v&>Ab(y?~C1D8VkL$vkuS~#Or+2K&nXEJWW~#4t+UK0^!NV67 zkgaYPe;3%AmuK7@e&|ir7J>B}-3={Nb0a%oF+pp#ml~*`^%gg3SZhml<-;0+l=~xu zdF`I*?wmf41j9T6`q=8zy>+chb|n!BtSw%cpu19E9m)DunTq_eDl4h2F0giun#C;ACtgNSqnXtv0lzO@gEld z68{Q*ruS7mZm&Wu+sJ|3?#)IKtSAz1srK^V=OSbafqG+Y!gK1U=n_Rc>Ym?tdzYLE z)nrr^85R$-EUAnsjrV??QKkpdcV_-HhZ-+_snH`Z+q!m)$%;W}e)Ky4IJqaQ za-7Yt{xx&KN#n&K6{2vE+kFQai0o9#Uwy_*ycI7)$LSB8FMlL~?dYik+aK<``&p9P zWA0d54`epY9nI*JRnd&&*(O$~EAPfJXVi3k#4VBZWJHrCI%D3+DP&Pb}@lX&qt2;{s|MCw5l?G*syN0R@PZ8d*VVO0j#_bKD%@cGWot+U0c=SbaVJ!014k~%d2wG6w5O$nCy-83R&IIX zAxdz9tC_N~10L|Ez-s2`4b{d=18qF(!cUZlr6n|wb@s9Uhj!`*{l3c1Y zr=}Iu;T6s6KZS%=raa1k99>I%_tf=8$%awUXPuQ+pA z{L1qx!$9fcvOwdDw&i0W$IS_{f85$*tDQqPS~9H4zF}E}7QjmD+HD453W5(RmRgzy zEfWVDglJ*}PYD+UvAk%B?vxw5GYV(J_u5545*VrDa;OF)-?hUKqcS#?JTcI{vpD8e zkqcpLV)YgKX<4gQywwdeqoR+k@rMk!z@U3(DpZlAc+6dGybNZnJpK~y~%)?)GC*=M8?%Kh6e(7mF zyX-v}_8r^1za&F(C7rQa6pBnlC+?lR3Ai*V+Jp=uF2P5J_TE>(m6Q$aN?3Zb%Q7-7#tQ z2xcU^aBArb&LJnE$ml~d%hV>96sMsbm~VT8EZvZzcV@_ReKvuM@Ru0j&Mp%9&)X{ZSx?ZkTcMy^UIK|1!BC zglNsgr9StuGG>yN@x2NIT$0er9Kr_*uHfT(xbiRgaD5BhEt1Ti;m1uCQ z@|Hxk1+yMhuM9L@f8RSY@Lkop?YPrOZ|?AEX8oNtDU9W~`i99N??2k%Q{Xi{-|?t* z9q?|@o|5BwL%N-A|6?d6*!ZSx;2YPxt(|nIt}qZddkD>eJw7YHaB5;t8K$iBfM)|~ z%e0|3f%;VmCwfS-3HgY9Z0OqvvKVTopr^=hJJ>; zMpY-L-`kfA1PlZY3J2K)ZH>fEeaGcrI3k$Q7dDTH+$OPSTId(qcMJOS$G)=`#_tp{l7xZ5h&hy#b@9IAar=R%$SPBz%G;sT$D#zNy#>Ce7Kh@2$#;2dsG3IX$ zvyH4!95cTKGzye|N)fXSwX3EuqCmv)3L%^WcIZf3DY44m-Wy*+x15^=LT~ z>gZxoBBIY^W6}n#@MxGKC8PMr#ffF~^u%UJ7iAe!?l@xYl}h4p1pGDh{hIou648;v zcwPh_%_l;my`ap)>@!AGGnbUTT z!p4GQ4+WZd--US52JDh7P4uRCgSb9C<5`-ajB4ni8Xuf z5)oPkJ?CgH{JUX|3LGY2m_ITQ<)H~oY)TuHA0D#mz9E#{ur<9Hnb{-$Bm<9+W#bD8 z6u^N%kzPvY`D1u2ja=13m_L$?=@aCllxs;7)>v}HX(a(l28bf-+4A9}TKjZH89}0% z*@xp%8M2h&;&P0MCmc^&@Fh}dxY(*jL?;4TNllCuND6{ZQnz>}wMY`={*mH31@|W3-Jv2lMjh*2L z10=arY~|1K<=`n9mP5+~POKppqmo_Dl^G8Qp@4m3K-^5(Yb5NYhg~nzl9OavY{aLJ zKy~LDrX-A^{oi~K(AWHLF1yMBBDuP^EQ%}~+rqr8;jWbbkX(^R9<4#CZ^sHID;o5a4Fr$5g199jptgiI+W^< zk<0u#a=a8!mC_C<)vFO6_5Ovi^(af9gFR(Ku^S;?)RiUkq;Cj}DpnyWj3Bd8XM8JjGp$DTOJCC3mBO*aD3O$-!r0S)!KVK? zlju+#G*_r(zvF4(PoZX}8ZMO4|C^6X3vh?68?U)b>oXUaQMe zLuy2MdGl%?nD^!biCfye9f;qsNw6r=ojhJw-OAWWvv0~b5Zm2`$TqG{SSTV|8C4dA zigW@NOdCjIy~x?@99)ubK%SqFCj-rFQSSh{DM?aYgUT+_C3geulFuz4-oMSe6|Cps0DoiRcN7N&%@_{;>UIB@AC@C8 zCCm~Y@8Q6pzMQh!@(?mZf{^J2Z6hD}evgf?ox>~9g${9Pu@b&}?aXBLB`t`Q?3RN6 zbq{hUmOWG*upo!#V~&noF03}Zp$Ky^HC3WCx7Tb#C8Yt`uk@aTMu4M{Nce!_(T95f zXvyp=_lb}t1qFLKYCte=?x|+J8@0>NDO@br{cj8}p|1P*$p$*Q-B*o0tg|?(%V_QH zvCDG1WAEYWWo>X^%pO}Dlp2&7XO4Di;a=_uDugfZlR}CPH$5fz7NA;ZrB27$(YIpW zgx>s~rJGaLkveMH;Vbjh5<|svX{!#egIuyy4qDmkCzk1D-T8=Qc9K@&w;4+tN3H;M zZ@3B*bySYN*|yB+gnK5IemA5}oe_Kj30pOW6_Msekr*2@H!Dn5s~;co@O zSzWzNHKViWBooWlewx|mSt5BgLp=7~-L=-TB+*B6=r8Ecfa2v$F>AsDX{l9@JHlVX z8@lp0Zo|0G)!cH_hoh9Y)Q7DE(s3fzOCVVje5FX5pI|b)iVB2##N@e?G;0y)YiF|Q zJ!o~n^xi|7dlK8`l^1wri6P5E5yR`As0phq<%NP~8$LSL^>lVd7L|}>glx}Pr3q&Q zQSXsu&8y>@1mV~bP&dWfa=-mxr)pYF}KQdn}SHxs!~Hq!_I`5$aY8=w8iOg zdHcSn9~iIq=!vxU>D5H(igOl7kwsyxt82OFQlpEGHulmT;&vaTLUXh38snO3QT{E9 z`3Ap(st6;N(Z6kN86bz>C~n{Bm$ckEL9XrNNUyLH4KY}qoGSvOUEB1Y_%s|!t8s4<D%|AqIF$7`{O!wnEP-tDJ!e5U1)m>4IE9{!?q!7g}oBI!eB`|d~45)IH6FJI*<9s?JuFDv1`HpN}UPUwWsY>lR)Yw5s^D<1hO!H7-t7Z zLhhXK&n=`2m{${-+5E%jLSPiuQ5#w(QNPXWj4=#oO(kASBqG-?4#~ML>YOnQa7!Xq zpV}{u0qKl@-0_G2A-U%<#yZp#&A3}$Ij!>uwKx39c!PdeozhF|W&PoNyF9ABByTyH zMXiV1npW=$b#SHg`R09d;4{4^s)K`PZ08LM{%V0fK-}rwwK=R^Ccq=Uvzj=%REQzG z_#E_~OiKpAj@GzBFj7L(Lo}j?7KxhHfC*#B8m`0GY`{P?5<{b4a5arbGg3o499maP ztEjjm=S#*HqQMg*%IQ<&9!GgWndt{nQbD`4N6Ack{Je{r@vZJxrB!)0!hTT-CMO)tDs9P;?>=%wmL)H0 z^q?dgW+&}C0$I#L>XIdIZ1mtHOWw-R9*hj-=TyHpyQn*wR;iaaM|WV2GaBq3p5p+d zwnrRt$7OuSNT;J)59#}d><#dwH82sBZ%Dazw4L~(3gJtg{0o3`TWo-?-vuKXRcuVw z=#5<)Cual~t!x~CWu0FD7^TTS16IBf7KN4H0K04&pn07i7p!a$?1Gs;n^is)@S=hL zh6p7O2>+1-caht%?_+V%I|&Lz*Qnkj+x8a>?NH`pTI;NTvkmF?Pq3Qe2}#>fqj{$^~-1E_Z@B6VZr5QjN9}Evh25XZZ9o-h*FkK2xDPHqAX`(brvuzj6+! zKmOQvavKMF98}}hy1vmwN4QUpP69@N)?u@xu(P(|0wVXauS3kqOv zZK8d8uP<-U=$d!iW2bE$8?o!Wc{i}uxSY&pvYbp$vTmo++wFcok17FH4s(HEfz$Wb z=6qSQBcCgQh3>6QiCY*dck25+*Av3YUWuEpnmIrP|1_!LDj89*@L+6i^r~U&TLkxI zv1>h*EE#Xuk`C$nv}TZY8hh(SF^V#Fpkd35So_*+!v$Qg*lY`>Yg4AjT;pQLTw}_> zA0?zW;cXfpXG|`&Ce^wj*t+S(x>SBPU%|LgkX1H!(UN>_EUd_T;)sa6aK|`*LW!LR zYPb-`F(+Fhmr;p`AT{$9$4@QhND)6b>%M`ap{MaPW-m5a zu%KQ_o;D;2#wsBol;Me+0}G|3>9SP9SrMbOwe@&0^0uNTJxP!T+Q4oCrvN@ zZM2<=sOKrB!d(w@1Bx(hDZU!^^i@fB*sI(b=JzRruQJ@_v1MXPjWkyUlwD~h)O4xt z4R~%^XO&j3%UdJT-W7sHf}(M7BzAj~t5fb>?sXKDw6j7ajr4VJn`i+vx6Or`lp(h* zNdz&uuLT@)wrT${SHc`4tm|z0;;!Ow>!uz9myZSGqZKXu%auB}w%=?*oKn9BpjuEK zF<2+5-##deHsfk0iM_IL<=%);c_Bs6epsPvjBFxS&=haq?jpa!x(~m#AS0(0q{^ph z4{0-UN3+QRVwv5c$gK_peL>8;B(tq=P=9D?v`L|G3`F+xZUg(>`XtYmD1w=Qmhh%D zVoQ;XiE;2EzsZ&`e$v!NBdTAVh_)z`#@`2oMH4SQI!QSv2^wG;*)xBI=faO%#<xOgJ$lGz%0Xt>YRMIUa_a?KbB-s8)@aWG_Nch z#x?oYyI8l0OLrLbtM_?X6M(u9WiesfuFyT76;dryX$YX@kPJ`z1SSs|o7;YAG4F_U z9gluc4S&;>oca2y^-BPJ%lLM2!x_u;-XGJMv6($b5|1NY^fo!ny4l`g z%h+c8@Z;`Wyr}6K5;ZX*h##D2^N+nqq~HP$5%;8qUrt(&v`aaz@u=&{>-WD$zeNLO z$6z~XeuSCYF-+1vG9qaQ;rea`Wk=zauJ$|Ub-iVNw~4fvJDTtR1$uXF{cy;%M$cJW zIzbnGd!WH&&hR-krRCV~1$u-^T?nt9>pVpan~XAOKkR+&G)}Sl-Pcr{HK{~xXwiIU zA^8ikGNz-N;N@4j&@>AWGL@kWuxTH-<;Nf&BW)_5su=otXsN@*cKr;?q%)p1c+@p5 zTjc@ejd1SBfoh(UZY0yBY7Z_{FF<4F!ut2SZ9CSdO-bK({txO?&rJJk z$?bY2oe@2_NrXAd7L!btnlNjAwF87xU#QnT2Lat*vU^N?-ssYz=w$95N6nP6cAvlKR3rY{B0htzhLq9z z6Zr-ZdJy`;nsLy2S=arkf^FI0Xvof{W*$m}JQk5DN|;`!jO*V}Jrg%~>w*LkjPlp+SXe_WGChfBQ&d_c9H3T6ysMMvJdI?g+8{sDe5N5!`k6A_fo} z1m^;Fjsq9M;j}~uyy^bR?dKi6BYEJ#?eD=jTe3KNl1!=cA+(i9gU=Y&($(E-r=faq zwSEHCrkXBp_`a0rWf-kj_a?HfQR)HA5B!4lQR*j%BU_Z z;8^cg&p`oGvzD+yrefU&ZfnapmT1+`f=&fa(XDm&Pb^-uI1g=IKPy?}b=lSjmjE8vM|EZnYnAf^xN`fEh~olb-0nGq8hD|L+31^CE;o}FT}_ikHp@}P z5<5^S+HIdvT+bGq2BT8JZYMxA#T=YjTJ~DzmFd0wSNlAKoqMuOChTX-AuD|-Lq*ng zTjg9YB11zpa{YGhx%KsX%u6L2EJKN&dU}rZ)hD8jNjgs7=A5ZGD;*0)52ulFKeKr#l^VOc}UytR%%nVZ(-C&>Z_k+MrTm_F0qoA!pUcx441FgG{ z)4_#rz)J8mBa_UOfvMix^%5@J>NBgjbQO<;gz%XZ`?1VDW{CW#?Ic)(Dx(Z$BJQ-g zRD;XjtikAam6%-^ME5 z-~$Jrf}(o_GTm8iGc=jM!WYZUa!F;l)OD};`r%heW)MlePXx*f$F<$>V|5tN>| zkCLj3vH+$XrYuQy>RYA1-m9}S#EReVu;@okReIGpV&ts`{fwkxU&Bx!rB+Wj>_?o< zOml-`yAA^($}w6KS)^gT7cUmDk0dVR4%t?MJR>-=&m^|T59AE)b577tJjX-yE22cQ zcSNek&^;$^jK~hEG5jeVl)j`fTO4uB%uXrvG0(sfbq?F*r0%!Hnyh|uO7`8EC5T*9 zw=HBNGe48i4(&c8(%>?c32Kn#SxbO0ku{Pv6ypG_LF^iZJw}M@ zlLbLiFLXlOAYOz;7SW6;&u9Bbs!uTGS}D%N>aHrT7{O%fOSx)&aG$mQMDOWsUHMes zWv&lPa9p=QW{w}b2d1|CND9v`0Bi>#F6lxaa07&FF65XtoEwbv{r#lHzAVC{HW^-e zgn02dhKR{uTICKD*w;@*We$j}-S;n5V5&Z{v8K#rIAirOlmp@-=~kF&NoBic6Vvq& z+=dpNjSU%vC&y(c`(SrdN2Y~YxAi12k`+b@eX`#KVifMMjv}-HMx*w&08{wHoFC=A zkJ$a5@qs83Q%P%BCu9?CgSz?P{-AcZ8gowHA?=nsNG=N(^{N;Joi1q}}B6JBbXw#Y7y-2)3Vz z4st0!DeYPk-`m2*@h4x-RO#OlPjuhVUM8OeZP~dgxf?+dbXk{uF&*bJwe(>t9rozl ztAIHNu-aR}>X@LI+zWT1(YP2k7whHwl$cVNFX_=)m#*qZWESmwqAH{27!S@7m#NkJ zm>5^w-Z_Wjm}btEbM>;rR8HSwqs*9RPS!JwImFfc^EZ|sf#wv&Z@ar!n4YLw^>#aC zSmX8Ad&ZbA@PE}=upERt>p%biJD>mnZ2xziC2MDFLTvKC92H^sPra3`tRsgkfWp)G zZA_sE^>BK^ERs|d5(XiWC#r!HCKOa+t^rrH&S~C{&rMI8W9d(vOm9T z&s^evQX_wOs z&!rkp_P$TRmc=tz@n}p6G!l2SchYICTkzQI$r1n{^cGGZ+&B^^iOSjbgUi6CMvPxd zZR{9sP(qf9K^XQTAb>CQhea)lAjlV0Um|&v%|9qYV#^qVQklAxQraey%TvP5nUcSp z1-J-(SO@3^2u#%hRx!YlT03&1D2Wlqs(R6&*`cYmK4d}8tPaL^s_q;afno;_@!Ga>iEkf`M-0+LM) z>MhJuDW3q(;2Wl`&z~}&T$s1>_+bd~I#vw?cpgDT_Uk?nRdd*dB};eVlmHU$l5Yx+ z?UJCIE26kjWjwdXw$O9#gtT!ZwAa^uV#TygG2sqVO+#Hn0Yh;@g8MzT|Lu(JTRICg z5deUXF#rI?|J&E{U-bwbNOx^D7v59LBFt#f7jx_Jlmn4^Vlh0471POvWA+p`k3=I5 z-2>toaV!m^SkojAqESf*2c(8j>ncbJ6L>9+fK~ZE7UE)lDe`{|n_M2qz)DJM1L(%@ z*5`J*fQ`u!smJ%5H^Ka5#^*KXJIm|l<)L_&=Tn350u`Qq8FqM7GgdalaD1hIZf<^G z*t^R`h|u|w^Ko?M&3;X0%GrZFBs6~iu=T?kvG1{^9*iSTFTKi&e)jMjmhQIx`zV`A4bG&I9i+$DfW2>M>2@XE0wYN=xUqK zJP3JoAbg6}M4F_Ms9xL=_Hkk48m^(?XzB3DUZ0s6ZznknKN2PsnP)S0BynE6>Upfb zekDG9Esl86eA_r>1Del`-|l=W?f{ldajA<^8#ma30>E!vmlo8 zC^P|B=>EQLdYWa4QLT4y|}01ip)(`xR6Ks`CT(#!kivUlqvT32M>y#MtsqUI0c5xFyGutk2)OyZ>>7hlt~3H zIwUkEBg{1zDZic`i&1C3?OXgzy^IAb0@wB$pBrfPT%r;)jvwxodi}DHGUKpVGGkKN zwkVVhewn^~aN-gMe%dGlF}66C7*DfLd5PR0NvS$j+nHnW%bRxVun=H5IU&!pL`0T` z3AifO(!Ac<7gVX*M3w^vtnPFkX`OxSD7s<~Ga4TES)sAU*5DyDHg*(w{H_jFDJ>(r ztga~zH^^s6(gsO{D{&S@)SVd#`$eHtw4v%xKJFi9Ko<#esy~jfCi;X7NLWyx=EY!* z5#`c+>q%jw3A~!sm$k;0ZV)2pn(>GB-76VTc+c0i9TDa846bHw%3unz-MX4mm;6Fl zI3==V3MN8h!mzq*bE4yyq$R^}r0vAhq%u^`i)ba59_@k^wM5|f0c zTQ+5_j2?#Xi^E7`KM(GkztKx#NI5d4gVGC%6*c0Eq)kXX*9aTKV1C!%p%BC}Of!-+ zP+-AZoJ-X=vD#8jDOW(VM=rLd04+e$zZyCtFR|ibfk}lfzFE+Y$$?P^CM1+YmUx8U zA%GZ1<6SlYEsj0GxKp}HcU>7fuMzw<%Iri*vn@JcrjzdTx6(hOg-Hh!e4M)g8lKZN z;;=u>UvHSm_Q?{{NBW^=U-w-VaGj0qST%oiPs^bIRAvUw9iXjNz1G=7B}6EiJL25sEU} z*U3{AZF@AOgyKMU;~YpFgGdK1>ibImk$i0#P~uBRGb6;!0gJ3fDnD{bVGgEvzfd$o z#$i@-YhY6S8_$^qRI)se=Ohq+GzG%(&H1ZFLAN+piQ1#2-(WqJMFK>3HaisgMc1p= zCCbzpfiha+T)Lltd-r<^1zBSBTpSD7>z6|V({e_oSkMY8PY&f=M*lJlMET*@AWCqp z`~R`^j=`A#&DwD6WMgw<+t$Xmv$1{0*x0sh+jj2Qwry^bcb{{YBP{rl)&; zOm)w6b@d>DuYXNSWyIWQsj*(FTMdLPPN!sRG2_uAcQBPp$uy*`8`8@6i<}rA<63L~ zNLvY@zPd_MDXFyM2@i%a+`x;FC-%xjL@4Lmk)`o*$(T->qvnaEzD0Kri)+8_Jkt^z zdYAl^^nrtX`CU|-8rZI>j&y_#6>_^*Pf zudE)EaE7Hp^h~vU_ecE143*(x74kF*ZsnZW35M*uiea3|&I_l81Zn53#}!W(HMIyl zqEAGh>Zl(1oj9CzHbcqF-NBJ-k!2)kxZ_JJX_oAW-i1M19*muQ`_#Fe1wkg}v~+Gv zBpkGG2bmN1(7CM;y0ez9^{xEOOxS63W#xfI#3i21)iUO%y-5SK5BB1Cu z1oJ5oYFel-$ekQ~ZRQtubT6Rug^c1r^ICI0EjLF`sS2OE!{-t8vn|S3Oi;KY1G;!? zP)=|u-^jp(i@V~oct%37)WH4B{86Sv8G9kc;qaD9J?~3xEN9h3##oF%D}nuS5xJv4UwFVQxAl~J#%B<7ryaAy~`$x6&;>D5NS8enHB$TR&2D6 zfV4z31PLXkl7PB%0Ze&2B;FF{!$+$9GFJ?LzdCJpA}PhPxiOC5`1PW`1AZg9rS=XO zrq!U06k*NB2ma=t*g^oZhP-(R9Ta}|yV<4Ddp&6t#4|*-oF^TacF#-?jQ_L75&|8JmhKhAFHmy=#kg{3RH`PH7P(HmlczCT)LgdZXeZ{K znzf43*fTXM+-x-<$12mt{YMVHDud3+5Z*(Mt6ryCFc?iU;ZVH<_-GaFx!7RNPQ(wM|^{@)GAglNjkNp z-5Ebnt>HA>BU>*rwYZ1PVUv;tGt^ft8WuF%#3C4!q}U*bSwEJW?bzvoalha$OJo%$ zuRV%8Fh~=#3alaQK{XMJ+BRVcs^;e#M#96tr_tB>m3ypDwbjo>C)MV`uc9_2naN}W z6B}wg7{)BH4TIg~&-r!=|Cr^nPcPsZB0w$^ruPunNi8FNp=-~b7~5J1k3h&vqIQ?& z(PrfC#>Hk+MI_#rnlwEi?MsClA1_g1-fhx1+Ue5QS27aSCXo1hGy(L}A~tuLX=>ct z>LbiWsg#^Fd|+QkTPT)4;yge1<<56}9h3{x+se#JxhCi1tz*Rsn2!iQ{qYt8g2DO> zTL;efq#NzTqhf_4poZ@r4)#HfUAmn;3|rgb!=^F3{q$CX2;M_iQG7oLl#4`ksOnhQRjx88dzShB+uCX zh+7k$_`y0gfo!9nZ6s|!)U<_Zg7h zJV;d1LrcNFn(f`QXA9VR2TRl~WRN)DxgfDqU@s%%2i~$HXE?pt@s@*s(uj}a%DQ~Y z&*fQc6u%(LFe*uzd`}D}{?mIwj0|PlCZ|@(@t&*6ris~;L?(%r#dGt1+(DlUcDYBSe$!-kl6(EH6l-CHEG zt!9*Q#6eNAF0#Q+9MA{KDR&Tp)Z^aJe4HpD`EReWCf<6$n;`_YnK4Tg)!4a4BRbSZ zg|xIr5gTjnbgHpFBlUAW>5Oxds#t|^qRixVJla>aPI2q zfxl7LujS~+)X|GDwpOCG%4wucu?nQl6hDlc0QkivY1K|3{F#T&*yM(47lkN8UK$6@ zPVR4P-BhTd#47e zOwcW@gjak)MybCKA|yILh*r~L2uAj(Ap8dt?Y`h;#g76N5hQq-Yk{$gpkm!ycv;@* zy~+~?`@M8*n@7P1$F)D54BM`VaF~&62I^B%vcu(85c>RkLfCPA{p*ujg{-6z!*KAM zgE*=#1RHz{RSk2Krgv^Ri(I}irOLS5Q^(^odm$}fuJBhEI#oCrT3Os!8{c`qK9s*W z{Gk7xeV>iGom>O9%u>R7h$xCj)p!G3_^CnH zJ+W2N$EWHsV~<~8zesfytTPg0#yrkVpvYbojsyJsi>mb??}_dOfqb75*l;eZJ=_WS z**pi13Ho0AU&DtI@YDH_DdH(-g{lpUDeqKV#!d-HPM(?ti!uByZW#9$vRuo3rw6(B zGZSqAen=u&N!Fb$#(tdN%8VK{KYk+xETz%H#pmM<(0(U>S4VvZ`;oJj@n)+7;}2KI z0F0d9lyxN1Ls4bgX4H0@LCtnV-&o&nINurHqoX@Zjqf`~_jjEj$zxhR?~c>u7vuzX z{alDQb^c5Ml_5t_^Ij?%G?BL5B+(LN+)4;2m1Cun429k%Guq%G?v9 zsHC#30@Q~SlIA835{02=v5}EN)Wc=+Jd>ET3kK&NeYdsO1$gv1-dqp~Qj}h9`peKi{jeuhqGR))x zYrU9FE)9iNi`f!KTP_s)@TVV$%|RjF`*3?t5yN1W+MsdGlmb2cd~h6K8b zGzeXg;%F3f1?_PLyf8l=VCvCxDA@fKX=d1oT-9r@@@k{!$+m0@wrI7*q1#k8vPthH zaXFkvi}Hf_MK_{*f~nmc@p|h<}ZwAJv}Vz zdU4s++#6&IVLr0Y; z*gQqh8{vuR)pEr(;zb?AI2IyTKbLrF=RJh^mV82%c@3LarHKR!^Oi3@_fDyx@x$-4 zzc(Ps+iJ0M2YujX?evpm{gwR6%@X?E!7NC01$j)iFef3L&8uBo?KB#f$&uLyNw3*3 z3?-iyS|@@pl8w!)9@Hf?7W?zfpS8?e6*CSt`~XF3fKCXZwejNPL_yab-fLKQ@73)q zsABrBa`b11=yBqtK}B}yG%cSvZriBXzN1$#kso1{GsS*Yj_VJFI4?e<>o+ulg6KQS zTa6$=tgna_pRZ4Rs=L(1Fb>Wh{#ORX$vZD=Ge}*~xnq^wBU@9fZIz#VtYAxD>FmiB z)3v#irXT5}21*A5Hz3+mEILy;^XJljrc=^tyu6A;nkFe^40!MkZ+y;Rgfp9efCrXU z%S`JPHn>(!BDK#3DHk}NoqP{}A6l2+p!<$;@+>+_3Ws{Jj(lFXV{gE9$dFP`w7>hS z1-=1g)Do2!t*W+9ANFy*v)9NyUP=ddJ5<(+GxvT~{nD zZ+abO5DYW-`27k-jrJ6s&k`A37k;<-Hh8&NN(&GMNEuNNF0q+TG@0*SBr~DW+dXdE}+s zCa!17O;w6N$1Taz^*Yp2lTY_wY6IRqpAFg;$mJlWTX6vzG=TT=J^enzfCF`%6%$-&_hg{oxYA?R2b;e6T8&cU>@meuOj!7 z!SFXM`tL^I}06`^Br-u8+hQ=a*4d~R`+F4!WtadWNq3XH}n3zbu z+CHK^PsyJ>yRy;YN>d#ITW#%MUWR|BrESBZqS78cgP+otFQ+u57zY{4y7 zJ-WIs0ty|yCLEGKy#i~uJ^PKIPi-}_w2H6I;u^h+t01k_%k`FPY*d=Fsf5|ww@p{* z8!yz|c{@z)#|{3Se;+NK3Pvur57*2qvRHLqruMl=H1f`;=Fy(j`Y@FKpk_`$C_yD7 zc>G`+N*HS^4BdsAVR}0OMTfJI4O7-?A!VEY)m^@#i#-)eeDBo6yG@&G+?0?aQY{`& z1bB!Ur-1<-raaXT7$7@5K`@8-p_pfjisS&`H`+hD`Tlvae^-Q1d+_d5DQwGyd_K@i zen`UjHWs_nmMTxDAB!Ua3xy`w6Wrx3P>kd@DHXPHLRin~u(4bN>mE45HboJdjs2DD5RoEfv zhRme&d3&pOg2Ufo@QFqS$d#6nta?Y_2GW<7QEOiDh|2^n4VRXYYU(*fq(E10m6q{p zUU7@__n*(JG*P@-YI}kBtmGC<_3t*i#5ththY;r$?=`Y{MilfnG$wJ0j~y-`e5!jxm=gz~#y%qULvzM-@ z&WaN2N|J-}Q(V0=AmtQ=DCFgtl6Hrkk`HGpaL#s_u8Kff_zXcbVe&~c_{jN>7FiW* zt2CtCO3S*db!5}i67g4bY1|*CEJ}LPo;rFaf=5TMv3<1>N86XQ9+uQIXZ}K*3IBy7 z*I?mI+bxIZ6@s7HVEH#3Jeme-Zlm$=4T9ei%q7lTg`*m`2CZ5~sB+{s#wH`zsmQ~U zV~X*l=q=VJqc~n|me)2zBb`I>XLB!GqV`WjB^4bb>t2X;1*MNsYAg0zx4BM&syx!n z^;{lB>u4z4S+CP>iaZ1w-iGqUxU8}y(m&OHh>t!o4Z1mUTQm4&3+oNz}9GL{HMg zZv3Qk`^%0CmEY-nkcdss_&#w80_gPeW-8}u*B?Ls@QvD_-XgD$q166Dts`wq)M(F8 z$G`$4vuR-E7BU(eN;%RgtjWOr{R^e&z^Rt0Y6A)(cZg0x>GuM``^Mq-3a1TFOt*`Vl27fMBN0Bqx*tDU44ix z*`4B>bV5JQB0aEcolLNuQTHAE*ZjX-fAPC@#VR8*DN@n*mu5fWfHp_ax)Huq^{GIi2#mwe*+7J8*2r08 z!AS*6DglO-_*^W~)C8>+W4T865?N*W`QwrYUB?XCTw=FdgcPOSFfa2+B>NJHwd*mCwB=Hd|w8)-`B+AZ)x-s zwl29+k5crCN3$sh>h^P>LqvBBdL+D|y4`gs%FO^Tux2E41gb3j#gNpJC}x0AvFem* z#X4~ds3F}tMbVjSCC7Lk#C}Tl!geDMiE8OTv=Sd#tKqCayaiT=TSf-S8gMcW`LrY1*1?~EXqh&_odsnXf`M|_OrMC3um`7n!|J`*d%Kl} z)*p5UJw6gZ`9^`g!KVetpj4#e2ewMXA1ru>vY36I*06fJNkc}fdZVzSy2@O2ym4^u zcqvKKJ$-ydmr>NtD<(h66Ap7P!c6UxHf6<%uo_#M=w$r%?W6F@4MGH+jMsNaA=)Ag zy6Or(L*Ieml}ap+xjq2#L(?xX?=wn-Yf|lio_nhP%Q5_oufj2D-x}Zbhhx5XWlge4 zsAAVgZtCQf`puQwa8bTg{B9KI8J630?L^l}Z|G4>2;0sVU!?!+)7sWqPudiDWGe5O zD~LJ#764Cws8f#FdvUu0QqTTyaW~SwQJ$(mC0|529Z<4U%vTS_XQ}WwOOx3Npm6KH zS8YD??wnO{I)UY2pAj6q`JkFOYh>p~OP-w@aYg#kn=6<{MsdR;Se>-H_d|Mf8njV^ zFxK_gTl`tU-!kwC!SPU3=!$G5U!x8x!LvUsjHz4^S5fzeE=oC0Lg}f%g_h!yk}dOB z8ybXD%T{$0Gq#HOn?_dAJ|!xpcjZLxDE9X0J9Efod3ogn%dY|Mub;krgMNUFUf-Br z>QkJr(8W61PoP|HKh2c=84ChXV^(~l7bM|(PPgA;;7N`9c!g>M#v#o%6U~G)e*AG6e{2^ox3zQ z{qb5$=%i325$%t=-&G&sF2dn!C5&OtLyR)-FtrOTXCF{Tg+(`cig_LYE7{x>vYov^aWy2 zm>Dt`!J+yPZ^YYXcf_iBT0@oXY?0zi$j02--6joGqF*t`Cuf-vf;cZ38JW|tM0S@Q zaRM6c>TQu7(v_QE>pC|V_n3fWHP0EiF1?%n5a>t=`y`wmtH`8zJ+-a@mBpj#`$^13 zyWZN$sxqL4R?2o;8c6;8(oqfFwFq!t`eo~DeO!8Gt6`A9JXm)7t~rle)VCV(2Sumu zz36eZM~A@|Te1M@xk4m)skn^FgVtO+$xR!6v4GGK4YCF?!a-m4TWrJx774~^sFUOY zwgAI`w{we7lAB6AB7Xi85{NelZJ=EbLijnK=9RaJKKvd^bKR!6;??J307*?IQ4#H3 zH&kz}$fwyMOXnL2PoI4elc21`tq*!{VVT1HkxLd)|N36xBX-ZuB;uoMpEoq;yh;}Y z7Bx{JJhqvB-^-3uTuGxjZZ24v8q%^w z(|>VQF0x$PrLYE~hgG3UEE~QSVbRBj&ElE95F8!35mr*?v`U>z9%Li=yqvCDcpMx&o3civ975|@q#lzz-JSuxn+jtAvh(KTvZtZwBS&}# zP;n#ms9F0yF%PxWn$Tm#6Z+%6u3DBh*yZK`pILagFAucfM? zB+*5bITvGtH6U>kVoNf*vR%f*y!x8XwJ-5V@BxD*g}h$rQtSekY7~x_Xv7|e?>*Bj zT#tuNf1M1>H<90GjOi1OhTCukcJZ=rdT6r?w#nw#>n51!Wua}Cnq!{xp|6hQXMw>e zsz>cIx-#@Y>EU?bDl?}jp%woAZ%Cmz&Z$Y)J0;Mq%&Q`FBFAnkz;NM}SMXj36)w z+9x1VIN8(F@pM@%}a)==C}=z=&Fe(WI2t7;K@H|!F?166fm5HMtqMGCSpDXF9cp@&V@Af7EXL%ra20e3+ID-nGA47>T+z<>5& z_|>WubjfYTO449GJ^xNRFU(%eS}fK%<7jkg)q7{O(XJ@nEVyuIBnp~$*%I%#@&o<9 zGiu(4>5!aYaa z_zrQ&0wG~y>hJ<2nF2{A3h{Gd^796Ie_~002vA&j^UP&S&tj?d61N2o%S<-8MBh&MKYfFw!%cooev4mc-Tl+kmz<5Ajl7+) z1|Lh6wP;Fc2FCXx`-A@Zjs2P|vv>%IPvrZg{w=GTTqFYo{f&EKZd3xOuBfaYP=Ut% zLTuQ0Fqanu2%!n`4Xls>%wuBj(-3(UWDEoTbCQO{DEQnMu;G2z=<(GRf65)K$uOnt zj2kp+ubIcB7&y7h!YtE^TO7Q(vShOmT_mPKrI}TfSl8i3e%@gx_8xSwY|B2;LnSL( zqTsMamLGE>yUg2^;6V+Hi!w8#B8k6~?g@NvXZ{WB)*~_f)hX<3QIk1gL6|5CLXB2f zmvxL4A5D{%<+GO-MmAH>wUh_hq?p1O!2Efq*}#h)6@sW;Ad@;EE|P-JY|+k8A&o`A zZV}9&J+5kQIk33B6DVJ4mH{oQJ@DB-XVLJXTF2NvyxrGiracLb)}MA7OBwP) z{u4YtEs=wTUE9rZT{bbSypQycydvUL*TDEbo*36wW>((vT`o{|759}tn z_w!%tNakeD%y^4M+4X3J&g<%(Op#2v1;5)o$mn=W#W_=ehT2-)o}NThkl0(*O&fBZ zrxYb%Bmkp;8QHq*=drt0|CW;AE>_%_C&0)D6UqzAl0q$?ipPAe71T9jfsf0da#u4s zIceTKwUL1v+PB3&)nWIqI>Ap3LipJ_{Bs*Dp76 zO@!wmmOk^R$oaJ=LHBcbYcNG34CD2aPKLTJ)_$dC<)fZbCvC*+4UGva3n> zsju~(Xv${q*oKY7I~O?n3cs?p295U)}!M)`Y}C%b1L1m~pPEB;vwHTy6E#TR?xhbub+N_%{$eMMUyz=9 z%$c+t%$u3Fp8qp*c9u3}qV1I}KVE~F$~+Z3p8jOxw3QCOB8wR**RT1lSQr&yt6Zk) zsc~JcO{%mcZe4RyQnk+1%aufbnYsOLqZHU9VlR2%2TyErV|sCCF#23IG4VJ&5zz%; z?gslkTY-5`3!&qBjc2nRyh&1^5;6JLsN<7|_#L%>L4<RUO-uhdo+CKZIft0=318YeXf2iSMiY=V)o_c$Nz4KhFUK7ZL zwQ7kKZ{gi+T;#Tiw%`?^G29H#*$ZOC{HD=6!>7OSAiA^h-@hme%Cxp?gl_Y>*lZ%L z7z;0_3W(BiaS4J!XRLP6=>oT`$J8+wCf$9})!C}1FpJ7YkqmjU`rdcz z_e`UA+IOA6ZJE~b4d?(M$OZ7P%zRrb@S>zHq*)2foM zg*;yNC+ooN3M#X}>y@Ofyij&E!&yjnrd7*X;|v;>Q`jkbj#K^&8s<~*j11FR=Zp;7 z+0f=8{6PIYtUdZ1^H~VJ1l4xo+|tT~3Z_$BA0=gYgm+vYJ>^J*_Ywx3fIVjXA!XN) zao4V;UDGan|Co4U4K2h)|Hrfbo-g0k>q88#raG6>aS%>0j42*>gu6EY#;F zx%%|6!Dx)~hqB1^3>5wkjI75-RjIm3FqnA?v#!D@GJfTdHQo z;J-@mgA-Snr+$#klA{&ooAB+$BE*Vy`|ymLuPgd=HPd@}n>=HdJO4SBnwvH>qRLur z(B^Ri=WO%fx2N0uT7A*Uz3_BJOGzB(hgugnGC|<)4BMp|QQB(g7~aM6$joeIVKMfi zVnDE)?~O;CkuPC2{SFz~=I;)5bX5j1ncv3TCchGIsoTQ4gX;b=R2}-XJuWQk(n?P6 z)90t@x=o`GIMW4V|I~MBcY<8txk=+Zl>33wE1w)UCieFX_H?D^@qOvB`svrmEK&HY zr8xR;YC?h->UHK4{gxqavQ){nD_GEp9GG$QI?SywN{7`zKtxTVivf0?iHUXm5b6bX zeErBc&=KDZi3I1_WF|&^hAy*_@cvARP3=_J#u^ygO5-Op@{)VE!ojwTl%vV`(W7SO z3B1pgiW*6X-*d?0Eq``_+8JoYR3Rb7$|=nzX3xg1z5!CPCfWIEe8!_8uA%|a(Ttgg ze3X%Z1Ale7sWWSMc~OX>p9m%gm(6aeV>B1&aa5+B;&Fu`=GD1^zvq_C2r4$nF0R0(ED27Mqa zZIPaJ$*&%T(*&6Fc)Nup_V*aFglV_zF^@Nohw2G9JWg;W)n|u~8Ndj%^zgL!y`8`D zZneB|e9o9Ebi;3P#4gx6tCjesroTe&Vg<&wCQWsbq(1Og{{ZlnbM*Ck>T9T|T`}bN zJ$ZLmr?~Od@@t$rtnocR<0_QRt8bh%E`?mc^AG2JTmbpOlbt85RrD+;GjLMvqWE z7R_^k_p{Nke!;5D!x|9XtKCTBsIfbu8|*STC)u!8T3F_zSo_VC{cpqULF4WU)fes& zr(e%|v$T&;vwr4Gjmo9xu)0smMcPwDw{Sh)eK9AEYvDi-?Bx{2r4D*aHQYw@u03$6 zXR8t1XZ>(3T)-BplRUK>xB&NTHzQZqRXxv~YrWx2dwJ)QCsJz8NOKwZ_p9po$L~q} zTNnSGD{dIySY-z;jee9FB~`9t?ie^u(SCXg$W|Ii%DlWuXJu*_!gkkK{AQ%6^=)fL z%wI<)HKW-IzGCTL6Nrul6WSC60MkBEdWhJIP0X@H&_!MAYR2x&F_VjTRA{hecu~&- zjIpe|ymP=cI-UF&Nt2PMkf(8|r|B6nj|cvrBsRUa3t8nH;6X4dFGnOC8`+z zd)FfC(7OA#c^aqbtB!-haK$$AOhf z_7=O$MT)k1&bXyO(M9Srbm+jJi)kL>aXOLe-7Ber^^W3oodzS%RvVA@%F1>hg6oco z4tBuBIme2B#g1-k&31QwkJT2gR81xsr)&Zq<+xnpFB*w zM<{W7#m8UboK&7@7CQ`_3jqw>D#5X&KLeIJE$>aRGQ-StJxNk8!iEOCBtpFg!vj5o z885;PoC+w@!p3&WMQXJ)j?*|KES20$h%sejDDv7NHvY^inmdpc4_HJF4}>3ED26dH z*lv0BhP&l_W4VAFVpAIK(nV2#755p zB=K%X+Jf!2T}As%UU>lTsW->c}po#>xnu_-S#2}XNT>$^Y^|FTz=Yqu}7R;kU#6YtW+jPJh zV0ha@RixDYLJAg*OcuYk|3CO2-hBoc66v&k=a--rCNqt{KpG z<_RUHKIGl{-~6PJguwc+1>i>bW^D2RTpR&>lLcxyRu3b9_M*p;IQfvCV z_x@XxgF6IGFU*!Etf~{x2MECkgZl4n{?J02G<)dNwS@)aq{czkYAjs)r&`Bk1DDpPf`dpt*Q7-CsuiaccVBR11|^q^lWR^kk{|%PjP_ zVJHb_NN#YGiXGkIble%aC%36J$H<*R8q@NjaS&5YDoc#CLJs5sTTW!y?Ofp|vljO` zt5ueM2`y%f=hkHz`x`M!OafwE*O4^!dX|%FzRTQ&nUHoO264OKbekmYC)kr_+o@S& zI@xgQIaw`Bs?X$kL2$S!I3>yu5;_&$P`$6^D`6T2JGPT8C4+TZ2gA? z_L?13My^W4LllJWx=GyT$ytT-yMv1UOa;5rB{kmqT3syyPI6bxn6nC zQHgue!)VD4TuX9Wk$`&0#c0W2EqmWrO=#u6XnkAfo8XMxQ9M4$yl)N)Zq7PMQdXE?~8Iw8!#R|A$cF z8H*X|n1tc-0aNe}ft!FaZt#n(+JO1(0!LfvdhE+XG`jw$SrV@imd6uBZTG+v(ys=c$zq^_%N6Z_Jhj-D}F?1AkO=cu@T zL<<8llm9tn?K!03KH**~FxvSN(V3R-!Hid7ik4SP-~^Sdoh-`=5dFBxY}?=Q*}p7G!^7Zg!VKl7;fIrL+sUMP|scqfkM zTgL~z6pxiup(o5}E6`Na3!Z=SObS<8hUc1*krk8w7m@v;CkHhzSyxO-_%BLXO^UY| zla!Q}`7bJjn~&8ZJVEa(+&S>+A~OGRa?0xslBRci&e;yHisJnNVPTxRt8 z21f)&E{N4V#-As>`*8BcRHp3y2}T6UE;HTc(j^Kn0q7D<=W7^11|k13=Udb{M#?93Xkg){=zfYlzc zxT{@-o;Ab@qat5W={d(viQ+3_aZUEc2aR>)0W@49N6Kg|e=nl4pWD+v9nWgGgJs(x zW1M5eelF**D@ZWbGaXn3=o&J}@v!JV&X(O-Yg2Gk-5_L)5us) zesn9+K@=%|=%<67AsPW%{uX54x;0?%lLAjjdyJZgH+c2wLz#lH?;KwU2Em+P-wg08 za^L3H4LyGdr3@zpBDcMF92;8iJ~m##ZT|}U)wGk69U0cbqXr{1O6WVt1V9gLJwJ$_EbqY7S^*gZxoWB z-ii_MV79;`MBZja&%zHu}Bm8?tUM)N> z@?UD<9pp#3ejtROHWS@v%KSI0upriB$!;n{VZH&(d-!gol^)$_mOE6BU+m)}E%8Wl zA?4TqQ&)>T+n+lD3IwF#2M7q&{~T}suRy!9sjwWI;`LQ_Nk-WoTaXA6!~ z&KDPEw#rm4t&Whs1%)KD4O>rwE7&K)S+|@|=V7I;o_Y7Z)N2D2$^al4l4fq44^DP= z4feD@x5kTP0op^^jcqTRqueiDGwB9juV2t0l(*@9dmY_X>?;7^f|Y0Yn!_bVbyY3i zbd^$TcF5kOJ57*~tCYb~bBZ0k#msU_6px!R*k_C%Py##CdKF7S-(7RJbFaZJK4DFB zLDF~F6|vgZDrJkQN#z$%bC$n*nT19di`1Th>wTg?%dI)2&;W4p40i{kM(zS1m2#-( zMM1pFs-DHbXHD3_?BjhYSjI3r=mC6;Pm^-im_9s?l4`;*;Y~2|Ek`QD&3~612XGdXT~^A5I<~p2pw@`8Imb~+g7B=UoX&_F@@!` zJsqN4HxcuEqmyRY|`I*|31Q`#VPW_1bht#0RA3nerB* zzrkP!;%!*H1?TiUsdh*v7#n3 zl_ylT88)dU-ybk`UYfW|U1STf%GZ|39?PLC<=;@o?o6ZSd9YCu!3*-3YRr4k$yNBq z3lH0qV#!B_0vR`QBHzrW=C{a%*TZClD2jMnFar2zt2!=2d?+5&4Fb&A-|_wot(16~ z!*(nQ%Ows<7_z&Vl1jM0QC+825J8#g9o#rETUiVH{1uVTQjWa`+X`BBa{6ZGIKox? zui)EQe@y#Sy4i^_?(L^&x#(*;Q*ti(w|%Ew&|LKK%6)T*a1CgQHtDjdLl zT!6O=SBIF*0Ujvf!8bP_V|s3Jt{8JKYfl8Xf74+x9_pLz>nbWb&g4O`e%2_YQ76ge zM*+Vnd;{GY<9>pepQUvq@|7%MElXmkCn^TJziPR1Hm_1T5B=;4;NhX+ z_jn#?Av8djWI4jP%1%11z!(~XFa2c$G(8a-!;W*@nH&Qz zV|sfPTX{rW7$;VNq7imrd|Xv}ydp?B@uPuX^t0VPru2(yWI@m;T~)c;+}>uA=3qjZ z1@AySIP*@GV&i3DWn3>IQ_6}a2|DP|Jrn^40z`ZYFx}=uCb!=l=QbmO=Qp zwU(oF;IDCQcD+P5j?4)(&JDE=nWY29Np2r<;`o|%+futM<|EQT(C9a$VEVAN#Fnde zLO)!dr>$X#LOuriC~PmT5dIUl_V=u~36Fv}83R#Qw$uZ6vewiC!=G-@r337n9(3HE zhdKR+hWlPo;46-7{`TQTyZkYo-U(XI3SRC!K`-quWaES)NU{6OB4n|IeA8MYql!gK zWQ(x}MI`x@VI+EXVBqc%mp($8B*sKe0&%-f=>E6ZRSo>^8Q(qwsYmF!2`@+pWJ|5R zLOZ8`EX=l$^hC&U-+kO^K-*VXsu8jm#~QTfnZ!eZA${J=vC zX+ejaf#ftA6~0gyxWapHqq@|qeUw>@x05AZw5iCnd(Eg>sJiYDa}2*RG#G{3 zGzZ_wFJ13CA~|*Wt_w!R=fry>4;XSB?n)v-vFzL3H^c|T=ZAS;lNMQve1jMXhq~Kt z{+}K}~J}%3{D(pFm=DtH&*4iz+*o@x&NbQZB3iL^Ug}@@v zm9d{*YdqooxY;nz-T8jMGY4@-Mp$91EV<@8D5*?oZz5RwJup(g5d&z8J+N~XjoRw{|(WiEXh&zot$wXVR1LGb=QG!w{0}Qv^wj%Z> zL*@n@d`1)ADmOaWmzeZ^XLeO!%C1l_CPf8M4PsRPY$BMMf@}4VLyp^1?|7YYGUa8n zo*%;uCK`m)jUo<_L5NtYaKr9KNbCVTN+;=(3|&Z-B}WtFWl6lbbt63AkgoR~^4d{2ur!AA<{yF>3Cp z)~Z@@q>m8p>SwyfyK(fX3Mq%z>(4}}m#+wGXT?tN-t3bbA7vUuG{9>3MvqbiuI}Rv zF5(1OdVFEdD-|(GBasuZ{K2G5cjmS<4o_o_l3)sg4S`sBGNz?${(8>R=Byp%=6ATf zvC;jQmsT0~7^Ij!Q#Lgaj*g&Cf3d9o9@uCQ-F|B&xED$r;9!Gv z(|(Z{xpMZCA4VSZau*E1ncc;H4iO*s)I~Y=$;{L?4ewlZ_ViUi18DI;5{ zIJ8ge$(MpRZxMIXg0@2(fkCy1zVnOv%EcF{23zMdJSySYDxAnIEc^KZfz`W~oviM(Q982%_a6OX&YC3X*Oa%zTRbPAFv`?G}gK<==Ukx6z8XJ%j$^t$C~ zE|;nEj{n%#LrOmIHDQ@b#P4UZq2psk0A-P6erSmuHkPFVNdRoXCW%}Q;TXn{QobMH zagTNYDIO()@Fl)bPr5s;6u&rW6wGv7st2DeSwzGOF%e%ZeK|Af3jwoU-c)M#EA5$N zU9k9*)v4FR9t3v}>=mEqin~E6ufV4%+z|8!JQZeL5Nfz4gQ7c;eE_%S@H)i+QU^Yt zQ=Iech2ggFc7H&i!WY#4ZKGa6-=fXIKtKxrjnV$kHcA;_=VEDVs$%i`-`!jEx3R_l zm+TU)vSGI%gv@V!vWy-fV|f-VBYJ~%p0FYSDb`PwR1xof29M{JX}7=H-x;&s8dkf9 z=Psn?aZreC%$XkqRi4o8Wp;dd=Wh0VQft2hs#-T#w;y8L377`AU1{IAFQ_vn8um}I zRpLF$3Db9cQ9bO(uA;bc+Bgv&{8F@s(>VFfQWb;jzNX>n<0R6Qf#)ho0<81kty#E! z^*F@g_;qVf;U-RGoCbH10W=Kjuj|$CmhUPd#bJB4^W>=D%gas8*$Q(%`CpWsQ*>|N zm*sO~+qP}z#i>2)adT>boSGJIb+WG*=wD(Robmu zVZC9CejQC73nE%on_{kt?>p0yYLiw?A(qMb=P)N7)E&_ow+3%Nh%tFybUb$Cvs zakx!6O^?s!&|yGNj+32Utsh~S9m5!>>N^h+bm7W|>?LC@m$#e~n97gQ!jfTz(p zPRA)*A|&=l#}P1j754Z_TQN(C>|l!$U{^e3&ORig50z$fXpxTwj+8L<* z!6leze{lE?k?djU@`<$S(1+t`o{JbTkKfJtV`ekCQ5ChV=sLA@+^Gz>QDbvna5LyB@+kyxeiAwk zG^(VucG)u7T7N=q-xl0FM#(|$ekA{Ssj{^>ZZX63t9V&Yha@4Ai^uJpq|;2>Nsf2- zOxxR6tiB%@BZ#sAq5AqV3y|pur9@sZsNLM`&zqwnfH%D^JsIi%wmi+Ee-{zmZ#kQ9syG zZr3O85RFcIXMJm#Zi5AHgYq0pTkCQjAMFH8j-{_n=;^+G4|9VY3ygk+M|14iQI0th z>d~t8PozxE1q4YHY4j>1Ux4(81XgLJs;UEMszV#tRjW|J51$RS5^KAj4I4;uAoe_C zm|^)2mGYMd-;N?hd6F^QJQFdT6pRdcRbf1CvA|3AQ9Z||0vrTf0&lTOLMI(F@D$+6 z&yZWvTAk2s(HosZl~;b56*Bm%@Z%FtQnMApoS$wGlmJN$PFOfq&4RjEL%c2Dk`qaW zg!aoTSE|^F7QP2>sqg~pLX;&?yOkWrOwFL#sB|asvbBu6qoUku=|dije@d%U&v<=d z&4Y(*WwAonTO(fcyg4G}h1ght18-#VA*WfHMpS*7ACw!te5upIOKkfV)`@DP<6b2m zh9aI)A~;BDt$~zXh;|0or|suHzu1K)S)cPS)tuk=die*Tqv(b|7W&o)gLpEMqqOgb zDy^5oR2=h; zAC6VBT2y_39J|*kdYA0t{9-Y0=9fbwc@4V8Pnp&vHu>*n6*s42Tj#D$NAnpL zE*PojLd27XNS2B9x`amWZ^AjHH4(VX1#pzJ!MjZg{JJEj3))lm^G92|lR9K3cerWF3had%1SFOFixwNnlK3}}FzRT&TV z2B)>{DnQ-s)hVaSE2qw#ZpG+g5*OB?o4IdIDM#kYXJWGG{<-~(H|~n8qbG?aq5@z% z=9|1a4nB%@YV4Z(*>M?ExA|A3;?QwAAy6JKRVBbfb@3dDB;G6&LerAyjek5e!9kWC z<$9OOFwEQ>>cEK{QA&@0RYzctJ_v~EFUu3O);S!pyk4Fw!IePXgLeX&&p6w#^xs`V zZZ0S8wwqoSCF0}?&X9#os4PK@A;rotB!FZlK~&cR%R9j6OGjBC-`UcE7_)Vb6&Im< z_)lQC0`DUZasbXb4lF;GS4ag4_00acbrkq5Wwl>Wu?OS_=4Z@2@N1Mc@OW-oNV$;W zUCxi~3AKvlQIt;6l=_^|NBe8rAHD6iLDxSWtb3UBPJYjd5&}U<28iuG{yF*SyoNdY z#$m&=5f`n3l`!0P3Cn{WUzikhBDvys^aqHybx`vrQO6Z^ajRT1?|13q32FNj1=+Qk ziyq*7erm9KR;m*5ROOX;PR%H(XIK=d!5L9o);@#|X=u^)z1~xcE@^O8R)T3iu+~aO z-LNS_te+ru(oprV%Wc#iQMhP70xai0q!YiE7+K=XEAg2lu$Fs2v4OERCk%*!Iy5E? zpk7!shQ+NB>DA(Ng)<`;=bc1z8ba@|pD(I(MIZuesjMhtd?9|*y&273TU$$COFy!Isrh+*&<8TW@w4eW5@fx`mEB@Id)(@a z#}FUM{sLJ^tb6x#f~JMZ2)zbrL22D9eGvRnfUk8O3fz_;_nsqtENlt2^>)JS2pWv- zkNrH{ZLWz?1nRCU{UUuHhzDcXsM<iVU{*5P>3&3WjE$v~0{G65FZZ;UQ+iIHj*-Q)mW{emSvV;i}L?vgI zX~xDf#1yfcM;ZSSv76!$8^}u-&4QlmTv3$39ru`d^6`%$-=DO)k@R{%?CLaMS$P8h+@aJc`?PcHr{8&)}lZvU`A=mUNz$mqKxQjW`ssrvJaQX z0rvmQt7Ti`LbBgM)h)?XidZc3m{2fIm)Tc{-Q!uHqYLpc{YyGcDgGH89-B6J;7e6T zF_Z8AbpCdP;^Rg#e&&g&T&MP>)xQnSiT=cu|`2IOOA zml?fjRma_~D3U1hmNU2=04qKgz~?5fAVJ<-1t$aC=HAUv4SUV~Jn-f>Ex9q6zC4}Y ztbzQ(Atn#eL#(l=W|o(Ox##h*1H|IkYWh4HDChI6=@>}XXHi`CshGxXrX1XwwFSh- zW6x}Cg+lv4G3u_?9u`1= z0Q}bNJsChPH&4hK*w*mQDQDxyibOX*HcS&|I>| zLsnpaygt7u+dkPJ5yhlPnJ{ePokvp5#j)N_#>PN*dFiPoLEdSh*iSo%q3&2j7M|>C zk*Dt*0@{=YMe8Cjp8HmvD(8$&y?#K7bTo@;BbL*WY7665T1_pGo*;`sgZA8X)xVks z$sTDK&IK=CiT7;xW>zXqL~6 zhZfxr&Im_7*l0t6u1*=n=W`myvG6p?%2CuN@e#hDtH2Q9=q{#uqkVNQc84^~(htn<;%fl}X^5dkE(4NT2R?asP$aGV_ zjjuzyObx*4N{xNP~17*cAlg1mZTC#&ebJVaW;o8&-}9o>!B!~EYeCst;xz> zC78K$$HLdfjRsARp5219fNaXf;}Ed@vx`}Q>GnMLtNlmD+s{_<${rbv4V=bnYd93} zPh4F*uSH$tk;i@vX+FAj>sFQoj9Sai)=txI)27zWME5rgJh_%R`P^ zI8$NkII|~RA_kE`uGyc}OJj#{rcGAL17u?jBUsQRAFwE*4K3@*?n@Y4_qW!AZ+LvK z%@kpGWYQa=Q+&i*J<48d{|V^YHTmS;FXvXjT{$E@Q5`79z!V92AgA3vUjA5~V1sW+ zL0H{SYJB+A-+XvfXoNBiiMr@>rbu5m7>at!`T~>Ft7j|WRmzQwOPymd;DArH>o2ts2VAsA%r(cA31E&&1jgG#J# zoL&|SV_Bj1GTdyPn!bdT?kF?8G8DR~?6s6Ou^;^SJ}RgoJp%rU5)(l7k11F?n)p** z69t;tM_R4G<|8skCA+Xf44d$~OCn>t0xxTgldor@x%+jS96Sfkg`^t%kH7fwH=07! zE5X<6+~(0g5fJ1lnIoI_ywP>MR`6oCY;PR$6PT96`d@&3a*_e&Aa1~siBXR3KVW-< zCSP;`?HJRK9(RYa=BFA75%>ghgSPmClL`JgrO zuG^{M7j5B@8s@g224FYIOQ$Q)U%)VxBsKzfXH}8%+iWm?4~LEg0YBDL4bfU%-(C%G zhj(IC)?bZoH)l2cKJOb(*h+Ox#W=aV-7k-+ninC*dO~evC2zTzzj`HIj-35Vl1}3a3EV^7* z|B%lvHPLr3?eZ!CP46wVD6FNT`QXSI4v4nK8Nb_cEr4$Y4(OpF(;t)SlpD2Y8G98) zoGsxlSRcu-sd&Pfs|Vqi`(rib=Pn8K!}N^wpm&D%thio?YXR{sh+H^;>kXQpKSdOi z?7TVq2V#8J&enjh)K!7wwFdaR$9=fsK16@i)`RTq1Q1;IBJ7F3LO#Pc8a(i8Kv)0% z;Acf;MblAr5ERty%LsG@!$8wfyDbMk$^F-Cp(C<1+42ud?!f{9(f_yE!ogI*$==QK zpNK)~{BNuO7%yF)#iebF@-k*wN%iQn~syh z*T8h0$zHMERU@yl)-Ir#v>V&1QDIG|6){fWqEp)>A(?|?uu}G7m|+K*Gs9(vOU?p$ z>~BvIC0Dcb#88N3Z}Zao2qNVt;%1Qrfnu3JK?wkTvTZ8SvdpZac)APG`PYmtRx#D> z?;?F{aMJQJdVEgCW3o)BMrSg78!#rt35uzgf!^bU?A36U1GY_d#V~g|!U4~Y zGd@{1eX6lf{GJR+{F_A@FNoyLmxs`4wgF^CjNiMJARC9P3s+gfoisBlER;Bbj&jlp zb%xdNIk897H2!d5p@o6N#t8XfN@zW#E5-ZqwI8}l><*z#We=?|o_cb*-wGu3TYp-? zokhivM}iy8F`E~G?KW>|txJ)MOnDX(3XaMYfpMLhqZAYtwiD0RBUGJ z<~YIc~~cR#Av1KLMkbYYSVyJ5%$hb zKz=G58tM!CUoWlTZLU42dG9s}i(FBCY3%1k}iE(Vbvz=Y7_g-RF4t3p4m>O#I1 z`eQgvzC-kqT6KA5!z{@xA*5A8BY01|n94TcI?dSwW3U+vb8~(H7Lh`Bf;B+|T&+qZ zDwb&s3 zQssGhh2i~l5y4`>*>9Fj^hF}eA zz;^JRke`}6eQ5*vRin9$EL(}{2;#-K*+i1`)*+W`U44pjN)Zo*Y_fAUcir+rirIQr z26Ujs_Oy~}T0KlmaV%D#>C0Q-5w@rA`Y!k#ubpOdMFe@o0imnMuYP*adSBM0*e!?2cflM#NiKS}k%+%8d9= zuY#vreoZjTPF_#vIH_N6^Sw(wj|0KtYl_32+#9!IH}y0!S*ei@SsD$|i7n^{vJneG zp|+|dhIH#Y$=TJ&Nsj4v4Lj~h+usK7~g4;KCmxl-X(#k0Q725X4pPE_$?iq zq&w0JuWOjkvNR7o=hP@p1%C%bsvVPk4T@+2g`BrTG$Zsbj22WZH0$r|gVaa3xXRdD zv?-rgO#KCq9Lj2xog%DP<<|Iq9IDRnOsdWhZ7SM>D=fws_uFe&t=2TBm={hv=T_Vs zXBQ__E-%h%N1LP5-Pc`$f3*PjgI$O;a3CO8Bp@L2{|5{Bcj;HZamEov_9dB^*Ge(g z#f|=RA;N!#rh7a^pIlfnF=PCOzk>it!{*UeQw8^%(tYle zQ^j>x-eK>8TdB1eueBXv0dA0vX<4*lJqBe~vOTgIC|hQOh0Ka-wOq?NTNe{%*vOHF z)RHh(*!6Ios9Y9~+5lLy%F?5H)Tx}A7}67KDiUTs(nS*CP7p>K26QK6ycm-Pk=~nh zV90C28n^&K;bM4H(_j?~`)~634!w`c90d3FvMHIalIj?=^8_en?|rGTyYM~!a3l{xCI`#8Y0ni-Pa2kfoaP`=xSZZ30z-B~ zZ(r|?`aQoRsarA)6pQ>c3N>Z~{}9v~EC6X?iw$}&)?_86S!lus`53eA_zzZc z=Rhkov|@u^Vw32?5-+pU6%AyKRVw!qPL)X zKcz~W@~~F)-Rr87hh$Y$uzNgr^PxosMVHUFA?S110^%)dHDOR8>f|2tdzF#40CnOR zHQMwHgeg2=4Ud^$Hh1UqG@(f8cWqDbG{A|vFKem-kvtlzlij{)YZ+r7LmSHYxv@ug zeOy`=oB;8W#wWyJAF&wCTdqj9&XUV+|K8@@9iojAV@zF@wvA$KDtE-ZtR6HF3V!Wz zgU2ut4KKEsJh=qa9CrO9riYG%5PUXvw>R2vt(rcufmMsND1~-PKDI+u^=q%ZR=SOb zjAA`IcI|H|>gdL7pM}S$j6JL*vWyPj%NgYJxe|Or6J5B~2(oK!E7k!;TeYx0afE48 zWaDrd;+IoFz|U7*hBcM{Cb}i6Vr6j~Yfmv7li1c^RI3+n?i3k$qYra#n&k8&-#2mc z;=6O%5elAC^g4R(CsHB#_aLB-S zzmAI`KgT}ILwu1uZS0{~Sx#r`ZxSkOfgBnIH=!{~C_d~7G<^SbZ1topl!z*y%%vds zBweeO$;~KsaZ&+eQF^JWEtCevS9kLc8h-7URnIDo%;vo&9l+<<5n-#T_euhK!|qcM zAZe0hL8jj5DzDZ)%6 zH&K-VlQQ0WhiLQ^#*${5F2NkcdZ^kkwzk71y^H_d=N@rs`JKfEz47^v>R|0wP`btv?6=uYV~C1@ax1VH4$p@pmi(bC^4QLuFd#c+tv zunNKu>LmhuC0S)~Ajs}5=wA4Bs{pwfBMIbTO2Bx&XG5Vt1ia{vO34u;aF3C9%NGf( zdjmR-3+yF4CvgP!Nu|;~ctsVVOs%zo5QvP3W`PlicH6Y1wW**fn8l3Vu}3>JVhBJ! zLAP^Xn^fwY`ox;}7>9}p{G0n75o9sj!BQ94@|=vod~C-%akINcKTa^HtT&=BbR<2g z=_*}Fp1a2tKBGH#GTl4OFGrfE2Zd8VmpeW z&X}ssm}h-3yI}$x(%8}{mTU_1X|&GJa4MYwsJbja0*3z2 zI;sgnsS2eXCH^0bKO=u%z2WCM(>a?0s-Q8>&jxmW_C64{s(QXY8U#SnhGsCgX*XP} z+H169n)6aCk^x-{=MJ{hxl(~llpF8W)k`kc${k&Gwn^Ef+?aBrj28A6CK8irDQ7yy zt^DmipjVGhOKw)hMYcD~O%SpbmubyIO`<*OS@<0jw=+KxLz9Q~cJU!*rt&1RZZ>un zvdSAar##wb>DI}B@3_<}JYns>+Zm^_^7vJ1d#WDbv^x|!#WhW^FQNU@4HIt0${Xxi z(EvMS=yub!m}{bXBOaP5E!zx1XkK!nAx6ypnMT|K!!x!j6D~NT-^*lkQ?uIJ%;T!L z19)9|D{#anoC58imChx?EY^WzY7woqxLxaOyrXx5UC<6#A{hPx8- zCGkT37)GOvuwVwAqgR%=BUFMu8F@2@56xSpX(`v3K7Ff9l@W$q)t|BRn1ku4$e2b3 z*A^l+JQIlS%`$RgVpM3qS7Tg(*_$jm1z}mTu<#<+u@Dh`G3IvM<_s{2&f_TZiPQIL z7|1f-KJ&a9R~ZPEu;z(PfeA7F_}-zl#EVQ4nT53(DIK!E&Lnc!e0S`)*Xuu^z(i>> z^AE~)Q{w%*zP-9Dt2+-c=zx1IJc_7q{YzsGW6DWXL>{9v1n)A_kg%L&`%#4xn5BB@ z^F|%$^Xa2Ppo84uWoTMOn#gcr+A{QZN>90rUSmfy@Z#O${l0-oh?KVDLj`FXbi)=r zVDwYU26gyTdeH!D>S5&rxM*=KV(ZDPjc!-vf`at5ou-`cAYGTM(O;z7#l)gbJZT26 z-mA*r{Ofh5>M4|cR^TNHwG~s@VfFa;Cwb8qA1>cr^Z69_r(JuD_e0YcP3swFm5=P;BPOG_o86iGcHreQINTRLm)SVkMoSdXpT+TQ2A z1p|=Dt~4=pgqIi6dJ9{DX2GbB2>OT>6JO)5rHYc9p4O7zaA3DWp-r&~_Vh5iBWjoq zoy_EBWcgXMtHyR!c!Kq+<7j5D@^yKiOm$e_PpW9NHJc9t5T`q!23BogNj01s>R)fqWna@t=T9!as;V6kM+r~Op z=3&i7UzB|9UPDQ>pEik7VkC4{Y9NQ`EhLFORcN zIVKNZ?|24xw0b`O^P4R@`&0pPuyvL*;02VTaTR*sL9*G$3<*rY=k} zKIyQc@6YRS4#_ogGuow-JZk&!zcs**Li34OIB(`N2=BeELJ2GoSMw*`3U=$2&U+IQ zNFZ`8dcjt$O^K~(FtTT4(k)3?omA^tC#>pMZ)J3mguae)Y+;mT>El3qMc%(E7*fih z+!b!!>Q7y9fL&3;^UYR(TT+DTLC`IsTV55DoTMK0uk{ZOO{l{xQt5Shh)(asoyB*Z zAIXiav$aX4Hm^Mg#pMuKACq^5Wxz2L^V_SRI@_!@MkSm14p>x1s-SGwPk3EbZ!-?9 z=1%fWv>e3?1DhbUi7GR4BY}|!51YGk2nm`_H{)H;CNFg)ILOR3QOVUM{Dx4u!!7)N zp{zl9Og^iKP$!u45&FW6GG7K~+Fu|iLJ9@JzCz9}g)KO+dY4zASVUPo{s~KbyAvNh zrocBO%>6-K8^JX52;RI2X9wIUZ-mMfmZ?fa>Y2n1S!Rt$e*Reo@aA}+oLGo#`s#`@ zR2pLVNmOhx>HE%v;Ty3?3Bg-P7NCv8yFlaliQ~yP*J6;Wk4kxic2_cD^I-~ax3ElF z>}raoLyF-CAA}MXk`7@ZyZ<&CfOYOE3a>RQA;DyJoPJ-wj342)oEdG^a!~A{RQN#! zId}^nllf%n1mPWag5b9}psSv=%o<{JqJNRb4@Dm)X$?Wch(tnl%q$HTxZdSk!64BX z!XAn-Uk1Z?iGn0UVi&Av^PMvrBHLkwvBm3`m*|Kc%aEP-xWMO@Vw7H`&x_E4 za@8PPU2TIHzN2Hk-a{SXit&p<9dU%8uIt}(VC8b-!gsoKlyM;CB8_>#ExSpD<7LGgWtEaBmDp_KV{AoJtkshC zHR{(_Wj9bfi{(gTFSit%7t~a@@_+yQ{&PqL8C8rxrvNHRP4S!PuxnarjqEeF$dxmbmRM(mz-6}Dr zu!ZoJiZa_$Y2mik#;nxf6XMF^xt6aii>@W$N~11T&z(=dlKnEnu8^JmqU?jNz~lmM zKx*q$A>BujZ4|NVlTlPF15R2xoo-a zYfLIx_9;NsinVFcH9v(bIoIQ^X?{n%q~bm%Z{v+dccjfg2C>`9ExpO!_$-@gbOgvq$Spmh9uh7N(Avz!XdZ+YRU|Am1xJ z8KZitfo*WE`@)Q27}CK>w7v7-_P**5IWux2KLPfvw~CduH(O>%A;YD>z{7^m5vRb^ zYrv*!C`60;fNQvZbx>rh$Y`^vD4t?O;h`nRCj0%P9kW{)#i(b@nXgS#JTBj6%1&Ku zrU{#)@o9%x;*c>Rf?2KLC(FGiaJJnnt&#ZQiyZ!>!<*WTn0m`SK*KIz>ZicJ8M#j@@Sb(z;XpCv;AJ=SaA&^BU7*fREequcU^!Ng+@)PlLM$X6~#Aw`Xr2IX$r7t;>usHqr8L!1WrEbRr>mb4wQ0q*f@CB));AQpm+%67 ztsoUIL9lxRB~($(22= zgFQjo&~}#27nId88h&GsCa|}!k+;AEsn{xZ!rez+}oZeDbrzpk@4gMN-Jx;2}k z^;x#DM>!-WSyfeDJWa+e^HZ4b<1|(?ep`tMC=D=Szm;_pv$hBi(VL@}~P0QbBz17`etl=U_TTS`a@u-h~aiP*Ks`& z&L^behIIHRpiPJI5(Xl7KNPsXPo5F+8Kk2%14rW8fQUElf&2K~?KWlL_Im%SXJLx> z6)hSZe5AY2^0$c6qmHrrsx13RcM+Q6;0Ez>nEaar38>g73*_Yi3wggZF@?G1H+s!8 zbXe>8XF@fipKM0Wtn&k;P}tln{`5htNLMT;|DV8EOTWN>_}0-!D%4NxB6r~hZ0tMp z`7`u*CoYB@nDl1EmKNnHxEpO#FwRBRDA|MAPrmMp>F(2I*z_~l0~uc-)DOa)YCNkw zG)rn??L_g{P*k2sn_4%7{4cFvFEFJB*%GHa{()S_zgGySJEf*)X5jd=da7Hx~GLNh_@n)Zp9}G#DSO zq5TFjEJ_IZSy{=4Ps&+mm3DErHnmcZb#o8vzP+*(& zy6D35CuWzafj!FU$RFN!T|jWQ1Q@>pmzOG$+ zIQZPf9np*oXS zGeYJjv0_wWz3B}XS>m2v;xLZwY3I6ZinIZ6q9gC0={VdJK-(hAk*>x} zXG|I&ZDA{7t*L4)AF527IQeXacSf(@&`>PD3AAVWGQNv;YIS+ah^MCAv?^7CPO&;@ zKB7+_x3(*+)8Y6jTPU5wHQ~^)W~kX=8 zX_dj+u~JjLWxI8~*r6kCQ0hvX_1McFE+700lZc#O(N)~4D~uR)P>VRHmHM?Yuv5W| zA(TSiO4H)&Fe+xy+e6mtB#(4r-b(O@>qR!G)Iu8hEx=<`jn)TagEJB_U%}ONcLee8 zIUdj{mR*twNG?cX2+`MeokTf}g50N~8E%r29kjI@5wYPIMdL4UEd>ZIz|P!vlzA3d zAHT|O!b;cT_ycdr-15m04OZlwJ|-ngaUDVn3xA^j1nuO2$Z-5B>|uq4*j_d14slUB z7(?Rz3_47M{pHF24Z{r0&j%+IMT*iYO%MN`w!qev2vo=zQhDP6UEpyVN)ckoODr86 zJA#_yfSBX{dHXodJKDLp;>}ODf~aPpdW)oHSra#?%>1$qF64Q5pEh>jJ9&Kfi+^$< z_IeXMt(O;_eU>f`-Gd$xLj!dbx)afZ3K2~K>>bn&>hWWe2Ur-m02l$R7z`hZ7kw0_ zlN;(0W!L^+mGb0VMbPCR?zX)TPh+Q(xTsad9q6D zwh?d%KuXdyd6s3U-m`7l zyTA^DVy8`J-ln;Bu6$M!1-?I@+<}Z={}?iwCmrqMiMAYxlBrK-iWU9RxNps0LA?lh z?@OW!WCB%B${>Dr|_5b87l5ml;=ql}?^2x3e}?#oy7$Jd>f_5K&aGV;xwjD$F9-q$@5T15+-e zwdRtZ&K%!Rn=UQAHw`@IOmai(DYJHa-}lcyDcjPr>8Y&OP&(u9(j1^2r77>SpvKme zo^u^gW2faXUS(NQ3a=&p2^OVvHS-fbNrnEKy|;>PZqZ(q(XN!h(r%l8S|1d~2Mggm zVO6bx!BUYXrAa0N__lB>xmnP5JLXIj;%46o=z^3w9ps;JN9g_v-@Er{^z;}s%g1RS zLM})Z3q@I*n_GMa!lqvDGPfd`Fn{=kOW4sEg@#`girG$AV0FA0)oqeyI^wVpp{X*( zrOjjuaGq0(QJ^}{(7$HvN6rz%7~r33H>B1Xue3y!S*pk6om;(vcR8RO;Tm>G26i7E zt0m$X!2ZLwxJk=5rz<8wmJA z>Q#4c2lxTx9N~yo*0SH#;_fL@NGDjICv4QX7*rP8XcQFY^!uJr7!KhwnpKBr+Fg1z zrEtz{Y)qybFqkLL?US=lNu{~7W5T{aZ5{qLYkKVvS+QSd&yLO5iOJPf2)=AVwd8!MHQbhauRp31Mo+kXP_1fCHRP>2ki& zFS+Oe(wvx5iGHF7%Vrko;I>A`{#mTLRJMwkk@S~lc-r!&1hKB#!XrGuCA)C!27b=+ z3Z)N;34u6MyKG@RwYIfGB7kz=m70$J#d1lF5*t-XkaC50u@#Kp;}wWmCH`5Rts9%1 zQocD;w`YZ}Y9}#xAdtsV0djuilT-Q?9QRVTM82&} z5^dX90xK+7$J89}lDX64tYQaE-AZ(9pYYU~aMoeDN!i#bsg71j*+SOY z#M8CtHBtf{KKJD?s!Wfl$JRV)Eor(7)VaY1rF;&@cNym7IOOp76EAT&3T5+YJL;27 zxriSQY$waHZBQuA?4*4ZjIBbW4OG4+3(ptHeAhtyO}wp^&Prkxyrr8 z-=z$wMOOhyH9V`H9H&1LpU~dm`>{-IJgg_MLQ0AYM&TUMe76I4j;xckdQ3`!3?-S%Z@}xcpAdwm)VYw zw>5?tH4rO~SKjkWLDfbDtRSY;vMH z4t{X3owA?w^TyTL-HDL<*m8!yd@1t@xS!)Y{VItg5Re8&76bV69^}tHq8x5eri3%p z!$*)w`w!LOS3&|nY9ufQb4upb&Jh^6cGi&1hxw)$=F;XMcQ zNdEj)KTEO`nza@o+J)}}R_qpT5JA+8_5it=Z&?v8^${$}5{nDkDNyXy_Q+-bk^jQ` zdCC%MV0&^*on(_d_D(efLM1>{gSb03myz7vdo%gEv#C821noM7;PZmi`G{x z^Q}~ALh!XH60xOj67syX_80zO$;i?4Zfh6uv3)wtBDMu}Fje2m|>(?Z@Vx_!v!LUA z7=L2>1Z>j)d;<=?l-_|h?}Jl&Xn;t~iuPi=Q@8GNEOK?Wg+`kxcG~<@I`1|$`VuTH zozMhkRDd(t1U*i1d!4Ex+`@82v6>o7u`U^C!S5I_taj)Nue`-SttF~W5psdB>VSH2 zT{(i?L(owzFAuUXWefFL!0fLlDl9|rnYdN>zsdm^y0GT*kIax@d~|WMI3lfbPb@At zKHK%>q63RI*$k(d?V5|S8^8Q<2eGnjLkLbV(;m=~fG{vSpWFMe5~f(&nXFQ*BMcp6 zTefh?v8EB=I6xf9VsItq-i%bekL_4|Sdx}x%9#pYbx^m9hg_S?O089GowJ?(LR;$9 zvb(wllxF>%mz7^EjavlEW;VCyNTsyiP@7U~n06wxI=c+u_RwO1d8*g6QIH%b9?<{s zmK2VgxNc6j?Qm z0%Hm`RCIy@#Frv1YB)T-Fq zhmbDq9av#0QHVEa8ZyJO^SP9=V`;&BsHYfKp2hpqsey+)Kc4HXQ72&%+tcU)P94FP zGAwb>bf;9P**fCq;4v&ETblCCtdmU3>W}i;2*X3gd4GFSLj`(uKW-wT7Ku&Sn-b*QXdu?Fw_kM& zyLg(BC#c6F$Kp+hnUYDV*^*^QlUL!e*Fjz`nlhPMVC@uGGPVewSXYm$h~Jh|8qOX} zBsY8rBQo>&q<3g1nxyn<*g5wqRcFU(w3ZN&;mjT>pSODj3Jb~1E9W|^RG~D>#69N_ zt++Eovhr$vbvR8LkXqn-m!*B)&Z?9^$v=X$tN3YYTfjwX`bW8gBdtFm2>_)d;?Hl? zG)7!-;vuwrN5#b7T6zup3AxjLBm36Hr(%W;>HWPyn`ZF|am$CmeU}F9-08pY4DTa2 z>x7_r#0@!t+YX`tq5=~53PR$N@8J+K%az?g9vB(CBo7k#Kp+Cp56&T=S;8J5U5TC- z?-A!eiixr7pd~pd^ivGlCSvQY_}=uA>DYOc%)@VSzXrnyAQEwLi>Cd44~{X={hN1S zfXL%eL^a#1`1e)&PulBWVm9J!2s-S~?=Vqg**|HR(WnrPuLu#Uf0oBQJQgKWs1ro1 zW#4!YzWMciOd4lCpd|^o#67vE(bCI9cbS(Y5f4zsvitdCU>

*_4Wvk*m4 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 D#FhOM literal 128338 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


x{1fNJD}srXoi#pZgw=#sDu&TB!6s}zj!l{a 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 diff --git a/android/app/libs/backdrop-release.aar b/android/app/libs/backdrop-release.aar index bfddcab76ff0869a2edcf7b00575696401502644..238bbf25e8953ae6d1deb81945dc5fb94faf98dc 100644 GIT binary patch literal 125139 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

SR?Wn9V>GFtP)c>rJT&c^-Yp-GY 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

    sNhan@M51#f2^a1WH3i!DgTuT;)qJ2 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 z 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 O@6aWAS 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 z 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 QFGC3t0b%OJ83Mw80n35ge*gdg diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro index 481bb43..ffbc0f3 100644 --- a/android/app/proguard-rules.pro +++ b/android/app/proguard-rules.pro @@ -18,4 +18,7 @@ # If you keep the line number information, uncomment this to # hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +#-renamesourcefileattribute SourceFile + +-keep class androidx.compose.** { *; } +-dontwarn androidx.compose.** diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 20b58c8..61fc138 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -14,9 +14,9 @@ - + + + - + - - + + + + + + tools:ignore="UnusedAttribute" > @@ -114,17 +114,17 @@ - - - - - - + + + + + + + + + + + { - val queryParams = data.queryParameterNames - queryParams.forEach { param -> - val value = data.getQueryParameter(param) - Log.d("LibrePods", "Parameter: $param = $value") - } - - handleAddMagicKeys(data) - } - } - } - } - - private fun handleAddMagicKeys(uri: Uri) { - val sharedPreferences = getSharedPreferences("settings", MODE_PRIVATE) - - val irkHex = uri.getQueryParameter("irk") - val encKeyHex = uri.getQueryParameter("enc_key") - - try { - if (irkHex != null && validateHexInput(irkHex)) { - val irkBytes = hexStringToByteArray(irkHex) - val irkBase64 = Base64.encode(irkBytes) - sharedPreferences.edit {putString("IRK", irkBase64)} - } - - if (encKeyHex != null && validateHexInput(encKeyHex)) { - val encKeyBytes = hexStringToByteArray(encKeyHex) - val encKeyBase64 = Base64.encode(encKeyBytes) - sharedPreferences.edit { putString("ENC_KEY", encKeyBase64)} - } - - Toast.makeText(this, "Magic keys added successfully!", Toast.LENGTH_SHORT).show() - } catch (e: Exception) { - Toast.makeText(this, "Error processing magic keys: ${e.message}", Toast.LENGTH_LONG).show() - } - } - - private fun validateHexInput(input: String): Boolean { - val hexPattern = Regex("^[0-9a-fA-F]{32}$") - return hexPattern.matches(input) - } - - private fun hexStringToByteArray(hex: String): ByteArray { - val result = ByteArray(16) - for (i in 0 until 16) { - val hexByte = hex.substring(i * 2, i * 2 + 2) - result[i] = hexByte.toInt(16).toByte() - } - return result - } } @ExperimentalHazeMaterialsApi @@ -265,12 +206,34 @@ class MainActivity : ComponentActivity() { @OptIn(ExperimentalPermissionsApi::class) @Composable fun Main() { + if (!isSupported()) { + Box( + modifier = Modifier + .fillMaxSize() + .background(if (isSystemInDarkTheme()) Color.Black else Color(0xFFF2F2F7)), + contentAlignment = Alignment.Center + ) { + Text( + text = "Not supported. Device Info: BUILD_ID: ${Build.ID} SDK_INT_FULL: ${Build.VERSION.SDK_INT_FULL}, MANUFACTURER: ${Build.MANUFACTURER}.\nCheck out the repository for more info.", + color = if (isSystemInDarkTheme()) Color.White else Color.Black, + textAlign = TextAlign.Center, + modifier = Modifier.padding(16.dp) + ) + } + return + } + val isConnected = remember { mutableStateOf(false) } - val isRemotelyConnected = remember { mutableStateOf(false) } -// val hookAvailable = RadareOffsetFinder(LocalContext.current).isHookOffsetAvailable() val context = LocalContext.current var canDrawOverlays by remember { mutableStateOf(Settings.canDrawOverlays(context)) } - val overlaySkipped = remember { mutableStateOf(context.getSharedPreferences("settings", MODE_PRIVATE).getBoolean("overlay_permission_skipped", false)) } + val overlaySkipped = remember { + mutableStateOf( + context.getSharedPreferences("settings", MODE_PRIVATE) + .getBoolean("overlay_permission_skipped", false) + ) + } + + BillingManager.provider = BillingProviderFactory.create(context) val bluetoothPermissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { listOf( @@ -297,23 +260,33 @@ fun Main() { val permissionState = rememberMultiplePermissionsState( permissions = allPermissions ) + val airPodsService = remember { mutableStateOf(null) } + val viewModel = remember(airPodsService.value) { + airPodsService.value?.let { service -> + AirPodsViewModel( + service = service, + sharedPreferences = context.getSharedPreferences("settings", MODE_PRIVATE), + controlRepo = ControlCommandRepository(service.aacpManager), + appContext = context.applicationContext + ) + } + } + LaunchedEffect(Unit) { canDrawOverlays = Settings.canDrawOverlays(context) } if (permissionState.allPermissionsGranted && (canDrawOverlays || overlaySkipped.value)) { - val context = LocalContext.current val navController = rememberNavController() - Box ( - modifier = Modifier - .fillMaxSize() - ){ + Box( + modifier = Modifier.fillMaxSize() + ) { val backButtonBackdrop = rememberLayerBackdrop() - Box ( + Box( modifier = Modifier .fillMaxSize() .background(if (isSystemInDarkTheme()) Color.Black else Color(0xFFF2F2F7)) @@ -321,129 +294,125 @@ fun Main() { ) { NavHost( navController = navController, - startDestination = "settings", // if (hookAvailable) "settings" else "onboarding", + startDestination = "settings", enterTransition = { slideInHorizontally( - initialOffsetX = { it }, - animationSpec = tween(durationMillis = 300) - ) // + fadeIn(animationSpec = tween(durationMillis = 300)) + initialOffsetX = { it }, animationSpec = tween(durationMillis = 300) + ) }, exitTransition = { slideOutHorizontally( - targetOffsetX = { -it/4 }, - animationSpec = tween(durationMillis = 300) - ) // + fadeOut(animationSpec = tween(durationMillis = 150)) + targetOffsetX = { -it / 4 }, animationSpec = tween(durationMillis = 300) + ) }, popEnterTransition = { slideInHorizontally( - initialOffsetX = { -it/4 }, + initialOffsetX = { -it / 4 }, animationSpec = tween(durationMillis = 300) - ) // + fadeIn(animationSpec = tween(durationMillis = 300)) + ) }, popExitTransition = { slideOutHorizontally( - targetOffsetX = { it }, - animationSpec = tween(durationMillis = 300) - ) // + fadeOut(animationSpec = tween(durationMillis = 150)) - } - ) { + targetOffsetX = { it }, animationSpec = tween(durationMillis = 300) + ) + }) { composable("settings") { - if (airPodsService.value != null) { - AirPodsSettingsScreen( - dev = airPodsService.value?.device, - service = airPodsService.value!!, - navController = navController, - isConnected = isConnected.value, - isRemotelyConnected = isRemotelyConnected.value - ) - } + if (viewModel != null) AirPodsSettingsScreen(viewModel, navController) } composable("debug") { DebugScreen(navController = navController) } composable("long_press/{bud}") { navBackStackEntry -> - LongPress( - navController = navController, + if (viewModel != null) LongPress( + viewModel = viewModel, name = navBackStackEntry.arguments?.getString("bud")!! ) } composable("rename") { - RenameScreen(navController) + if (viewModel != null) RenameScreen(viewModel) } composable("app_settings") { - AppSettingsScreen(navController) - } - composable("troubleshooting") { - TroubleshootingScreen(navController) + val appSettingsViewModel: AppSettingsViewModel = viewModel() + AppSettingsScreen(navController, appSettingsViewModel) } +// composable("troubleshooting") { +// TroubleshootingScreen(navController) +// } composable("head_tracking") { - HeadTrackingScreen() + if (viewModel != null) HeadTrackingScreen(viewModel) } - /*composable("onboarding") { - Onboarding(navController, context) - }*/ composable("accessibility") { - AccessibilitySettingsScreen(navController) + if (viewModel != null) AccessibilitySettingsScreen(viewModel, navController) } composable("transparency_customization") { - TransparencySettingsScreen(navController) + if (viewModel != null) TransparencySettingsScreen(viewModel) } composable("hearing_aid") { - HearingAidScreen(navController) + if (viewModel != null) HearingAidScreen(viewModel, navController) } composable("hearing_aid_adjustments") { - HearingAidAdjustmentsScreen(navController) + if (viewModel != null) HearingAidAdjustmentsScreen(viewModel) } composable("adaptive_strength") { - AdaptiveStrengthScreen(navController) + if (viewModel != null) AdaptiveStrengthScreen(viewModel) } composable("camera_control") { - CameraControlScreen(navController) + if (viewModel != null) CameraControlScreen(viewModel) } composable("open_source_licenses") { OpenSourceLicensesScreen(navController) } composable("update_hearing_test") { - UpdateHearingTestScreen(navController) + if (viewModel != null) UpdateHearingTestScreen() } composable("version_info") { - VersionScreen(navController) + if (viewModel != null) VersionScreen(viewModel) } composable("hearing_protection") { - HearingProtectionScreen(navController) + if (viewModel != null) HearingProtectionScreen(viewModel) } } } - val showBackButton = remember{ mutableStateOf(false) } + val showBackButton = remember { mutableStateOf(false) } LaunchedEffect(navController) { navController.addOnDestinationChangedListener { _, destination, _ -> - showBackButton.value = destination.route != "settings" // && destination.route != "onboarding" - Log.d("MainActivity", "Navigated to ${destination.route}, showBackButton: ${showBackButton.value}") + showBackButton.value = + destination.route != "settings" // && destination.route != "onboarding" + Log.d( + "MainActivity", + "Navigated to ${destination.route}, showBackButton: ${showBackButton.value}" + ) } } AnimatedVisibility( visible = showBackButton.value, - enter = fadeIn(animationSpec = tween()) + scaleIn(initialScale = 0f, animationSpec = tween()), - exit = fadeOut(animationSpec = tween()) + scaleOut(targetScale = 0.5f, animationSpec = tween(100)), + enter = fadeIn(animationSpec = tween()) + scaleIn( + initialScale = 0f, + animationSpec = tween() + ), + exit = fadeOut(animationSpec = tween()) + scaleOut( + targetScale = 0.5f, + animationSpec = tween(100) + ), modifier = Modifier .align(Alignment.TopStart) .padding( - start = 8.dp, - top = (LocalWindowInfo.current.containerSize.width * 0.05f).dp + start = 8.dp, top = (LocalWindowInfo.current.containerSize.width * 0.05f).dp ) ) { StyledIconButton( - onClick = { navController.popBackStack() }, - icon = "􀯶", - darkMode = isSystemInDarkTheme(), - backdrop = backButtonBackdrop - ) + onClick = { navController.popBackStack() }, + icon = "􀯶", + backdrop = backButtonBackdrop + ) } } + context.startForegroundService(Intent(context, AirPodsService::class.java)) + serviceConnection = remember { object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, service: IBinder?) { @@ -457,17 +426,20 @@ fun Main() { } } - context.bindService(Intent(context, AirPodsService::class.java), serviceConnection, Context.BIND_AUTO_CREATE) + context.bindService( + Intent(context, AirPodsService::class.java), + serviceConnection, + Context.BIND_AUTO_CREATE + ) - if (airPodsService.value?.isConnectedLocally == true) { + if (airPodsService.value?.isConnected() == true) { isConnected.value = true } } else { PermissionsScreen( permissionState = permissionState, canDrawOverlays = canDrawOverlays, - onOverlaySettingsReturn = { canDrawOverlays = Settings.canDrawOverlays(context) } - ) + onOverlaySettingsReturn = { canDrawOverlays = Settings.canDrawOverlays(context) }) } } @@ -490,13 +462,9 @@ fun PermissionsScreen( val infiniteTransition = rememberInfiniteTransition(label = "pulse") val pulseScale by infiniteTransition.animateFloat( - initialValue = 1f, - targetValue = 1.05f, - animationSpec = infiniteRepeatable( - animation = tween(1000), - repeatMode = RepeatMode.Reverse - ), - label = "pulse scale" + initialValue = 1f, targetValue = 1.05f, animationSpec = infiniteRepeatable( + animation = tween(1000), repeatMode = RepeatMode.Reverse + ), label = "pulse scale" ) Column( @@ -504,18 +472,15 @@ fun PermissionsScreen( .fillMaxSize() .background(if (isDarkTheme) Color.Black else Color(0xFFF2F2F7)) .padding(16.dp) - .verticalScroll(scrollState), - horizontalAlignment = Alignment.CenterHorizontally + .verticalScroll(scrollState), horizontalAlignment = Alignment.CenterHorizontally ) { Box( modifier = Modifier .fillMaxWidth() - .height(180.dp), - contentAlignment = Alignment.Center + .height(180.dp), contentAlignment = Alignment.Center ) { Text( - text = "\uDBC2\uDEB7", - style = TextStyle( + text = "\uDBC2\uDEB7", style = TextStyle( fontSize = 48.sp, fontWeight = FontWeight.Bold, fontFamily = FontFamily(Font(R.font.sf_pro)), @@ -551,29 +516,25 @@ fun PermissionsScreen( Spacer(modifier = Modifier.height(16.dp)) Text( - text = "Permission Required", - style = TextStyle( + text = "Permission Required", style = TextStyle( fontSize = 24.sp, fontWeight = FontWeight.Bold, fontFamily = FontFamily(Font(R.font.sf_pro)), color = textColor, textAlign = TextAlign.Center - ), - modifier = Modifier.fillMaxWidth() + ), modifier = Modifier.fillMaxWidth() ) Spacer(modifier = Modifier.height(8.dp)) Text( - text = stringResource(R.string.permissions_required), - style = TextStyle( + text = stringResource(R.string.permissions_required), style = TextStyle( fontSize = 16.sp, fontWeight = FontWeight.Normal, fontFamily = FontFamily(Font(R.font.sf_pro)), color = textColor.copy(alpha = 0.7f), textAlign = TextAlign.Center - ), - modifier = Modifier.fillMaxWidth() + ), modifier = Modifier.fillMaxWidth() ) Spacer(modifier = Modifier.height(32.dp)) @@ -746,8 +707,7 @@ fun PermissionCard( if (isGranted) accentColor.copy(alpha = 0.15f) else Color.Gray.copy( alpha = 0.15f ) - ), - contentAlignment = Alignment.Center + ), contentAlignment = Alignment.Center ) { Icon( imageVector = icon, @@ -763,8 +723,7 @@ fun PermissionCard( .padding(start = 16.dp) ) { Text( - text = title, - style = TextStyle( + text = title, style = TextStyle( fontSize = 16.sp, fontWeight = FontWeight.Medium, fontFamily = FontFamily(Font(R.font.sf_pro)), @@ -773,8 +732,7 @@ fun PermissionCard( ) Text( - text = description, - style = TextStyle( + text = description, style = TextStyle( fontSize = 14.sp, fontWeight = FontWeight.Normal, fontFamily = FontFamily(Font(R.font.sf_pro)), @@ -791,11 +749,8 @@ fun PermissionCard( contentAlignment = Alignment.Center ) { Text( - text = if (isGranted) "✓" else "!", - style = TextStyle( - fontSize = 14.sp, - fontWeight = FontWeight.Bold, - color = Color.White + text = if (isGranted) "✓" else "!", style = TextStyle( + fontSize = 14.sp, fontWeight = FontWeight.Bold, color = Color.White ) ) } diff --git a/android/app/src/main/java/me/kavishdevar/librepods/billing/BillingManager.kt b/android/app/src/main/java/me/kavishdevar/librepods/billing/BillingManager.kt new file mode 100644 index 0000000..cfea382 --- /dev/null +++ b/android/app/src/main/java/me/kavishdevar/librepods/billing/BillingManager.kt @@ -0,0 +1,5 @@ +package me.kavishdevar.librepods.billing + +object BillingManager { + lateinit var provider: BillingProvider +} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/billing/BillingProvider.kt b/android/app/src/main/java/me/kavishdevar/librepods/billing/BillingProvider.kt new file mode 100644 index 0000000..027ad94 --- /dev/null +++ b/android/app/src/main/java/me/kavishdevar/librepods/billing/BillingProvider.kt @@ -0,0 +1,28 @@ +/* + LibrePods - AirPods liberated from Apple’s ecosystem + Copyright (C) 2025 LibrePods contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package me.kavishdevar.librepods.billing + +import android.app.Activity +import kotlinx.coroutines.flow.StateFlow + +interface BillingProvider { + val isPremium: StateFlow + + fun purchase(activity: Activity) +} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/billing/BillingProviderFactory.kt b/android/app/src/main/java/me/kavishdevar/librepods/billing/BillingProviderFactory.kt new file mode 100644 index 0000000..2b83717 --- /dev/null +++ b/android/app/src/main/java/me/kavishdevar/librepods/billing/BillingProviderFactory.kt @@ -0,0 +1,33 @@ +/* + LibrePods - AirPods liberated from Apple’s ecosystem + Copyright (C) 2025 LibrePods contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package me.kavishdevar.librepods.billing + +import android.content.Context +import me.kavishdevar.librepods.BuildConfig + +object BillingProviderFactory { + + fun create(context: Context): BillingProvider { + return if (BuildConfig.PLAY_BUILD) { + PlayBillingProvider(context) + } else { + FOSSBillingProvider() + } + } +} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/billing/FOSSBillingProvider.kt b/android/app/src/main/java/me/kavishdevar/librepods/billing/FOSSBillingProvider.kt new file mode 100644 index 0000000..9b06964 --- /dev/null +++ b/android/app/src/main/java/me/kavishdevar/librepods/billing/FOSSBillingProvider.kt @@ -0,0 +1,30 @@ +/* + LibrePods - AirPods liberated from Apple’s ecosystem + Copyright (C) 2025 LibrePods contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package me.kavishdevar.librepods.billing + +import android.app.Activity +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +class FOSSBillingProvider : BillingProvider { + private val _isPremium = MutableStateFlow(true) + override val isPremium: StateFlow = _isPremium + + override fun purchase(activity: Activity) { } +} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/billing/PlayBillingProvider.kt b/android/app/src/main/java/me/kavishdevar/librepods/billing/PlayBillingProvider.kt new file mode 100644 index 0000000..790be60 --- /dev/null +++ b/android/app/src/main/java/me/kavishdevar/librepods/billing/PlayBillingProvider.kt @@ -0,0 +1,187 @@ +/* + LibrePods - AirPods liberated from Apple’s ecosystem + Copyright (C) 2025 LibrePods contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package me.kavishdevar.librepods.billing + +import android.app.Activity +import android.content.Context +import android.util.Log +import com.android.billingclient.api.AcknowledgePurchaseParams +import com.android.billingclient.api.BillingClient +import com.android.billingclient.api.BillingClientStateListener +import com.android.billingclient.api.BillingFlowParams +import com.android.billingclient.api.BillingResult +import com.android.billingclient.api.PendingPurchasesParams +import com.android.billingclient.api.ProductDetails +import com.android.billingclient.api.Purchase +import com.android.billingclient.api.PurchasesUpdatedListener +import com.android.billingclient.api.QueryProductDetailsParams +import com.android.billingclient.api.QueryPurchasesParams +import com.android.billingclient.api.acknowledgePurchase +import com.android.billingclient.api.queryProductDetails +import com.android.billingclient.api.queryPurchasesAsync +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch + +const val TAG = "PlayBillingProvider" + +private const val PREMIUM_PRODUCT_ID = "librepods.advanced_features.v2" + +class PlayBillingProvider( + context: Context +) : BillingProvider, PurchasesUpdatedListener { + + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + + private val _isPremium = MutableStateFlow(false) + override val isPremium: StateFlow = _isPremium + + private var productDetails: ProductDetails? = null + + private val billingClient = BillingClient.newBuilder(context) + .setListener(this) + .enablePendingPurchases( + PendingPurchasesParams.newBuilder().enableOneTimeProducts().build() + ) + .build() + + init { + connect() + } + + private fun connect() { + billingClient.startConnection(object : BillingClientStateListener { + override fun onBillingSetupFinished(result: BillingResult) { + if (result.responseCode == BillingClient.BillingResponseCode.OK) { + scope.launch { + queryProductDetails() + queryExistingPurchases() + } + } else { + Log.w(TAG, "Billing setup failed: ${result.debugMessage}") + } + } + + override fun onBillingServiceDisconnected() { + connect() + } + }) + } + + private suspend fun queryProductDetails() { + val params = QueryProductDetailsParams.newBuilder() + .setProductList( + listOf( + QueryProductDetailsParams.Product.newBuilder() + .setProductId(PREMIUM_PRODUCT_ID) + .setProductType(BillingClient.ProductType.INAPP) + .build() + ) + ).build() + + val result = billingClient.queryProductDetails(params) + if (result.billingResult.responseCode == BillingClient.BillingResponseCode.OK) { + productDetails = result.productDetailsList?.firstOrNull() + Log.d(TAG, "Product loaded: ${productDetails?.name}") + } else { + Log.w(TAG, "queryProductDetails failed: ${result.billingResult.debugMessage}") + } + } + + private suspend fun queryExistingPurchases() { + val result = billingClient.queryPurchasesAsync( + QueryPurchasesParams.newBuilder() + .setProductType(BillingClient.ProductType.INAPP) + .build() + ) + processPurchases(result.purchasesList) + } + + override fun purchase(activity: Activity) { + val details = productDetails ?: run { + Log.e(TAG, "Product details not loaded yet") + return + } + + val billingFlowParams = BillingFlowParams.newBuilder() + .setProductDetailsParamsList( + listOf( + BillingFlowParams.ProductDetailsParams.newBuilder() + .setProductDetails(details) + .build() + ) + ).build() + + val result = billingClient.launchBillingFlow(activity, billingFlowParams) + if (result.responseCode != BillingClient.BillingResponseCode.OK) { + Log.e(TAG, "launchBillingFlow failed: ${result.debugMessage}") + } + } + + override fun onPurchasesUpdated(result: BillingResult, purchases: List?) { + when (result.responseCode) { + BillingClient.BillingResponseCode.OK -> purchases?.let { processPurchases(it) } + BillingClient.BillingResponseCode.USER_CANCELED -> Log.d(TAG, "User cancelled") + else -> Log.w(TAG, "Purchase error ${result.responseCode}: ${result.debugMessage}") + } + } + + private fun processPurchases(purchases: List) { + val hasPremium = purchases.any { + it.products.contains(PREMIUM_PRODUCT_ID) && + it.purchaseState == Purchase.PurchaseState.PURCHASED + } + + +// val purchase = purchases.find { +// it.products.contains(PREMIUM_PRODUCT_ID) && it.purchaseState == Purchase.PurchaseState.PURCHASED +// } +// +// if (purchase != null) { +// val consumeParams = ConsumeParams.newBuilder() +// .setPurchaseToken(purchase.purchaseToken) +// .build() +// scope.launch { +// billingClient.consumeAsync(consumeParams) { _, _ ->} +// } +// } + + + _isPremium.value = hasPremium + + scope.launch { + purchases + .filter { it.purchaseState == Purchase.PurchaseState.PURCHASED && !it.isAcknowledged } + .forEach { acknowledge(it) } + } + } + + private suspend fun acknowledge(purchase: Purchase) { + val params = AcknowledgePurchaseParams.newBuilder() + .setPurchaseToken(purchase.purchaseToken) + .build() + val result = billingClient.acknowledgePurchase(params) + if (result.responseCode != BillingClient.BillingResponseCode.OK) { + Log.e(TAG, "Acknowledgement failed: ${result.debugMessage}") + } + } +} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/AboutCard.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/AboutCard.kt index f4c2067..416abf1 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/composables/AboutCard.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/AboutCard.kt @@ -22,8 +22,8 @@ package me.kavishdevar.librepods.composables import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -34,35 +34,35 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.stringResource 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.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController import me.kavishdevar.librepods.R -import me.kavishdevar.librepods.composables.NavigationButton -import me.kavishdevar.librepods.services.ServiceManager import kotlin.io.encoding.ExperimentalEncodingApi @Composable -fun AboutCard(navController: NavController) { +fun AboutCard( + navController: NavController, + modelName: String, + actualModel: String, + serialNumbers: List, + version: String? +) { val isDarkTheme = isSystemInDarkTheme() val textColor = if (isDarkTheme) Color.White else Color.Black - val service = ServiceManager.getService() - if (service == null) return - val airpodsInstance = service.airpodsInstance - if (airpodsInstance == null) return val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF) Box( @@ -108,7 +108,7 @@ fun AboutCard(navController: NavController) { ) ) Text( - text = airpodsInstance.model.displayName, + text = modelName, style = TextStyle( fontSize = 16.sp, color = if (isDarkTheme) Color.White.copy(alpha = 0.6f) else Color.Black.copy(alpha = 0.8f), @@ -137,7 +137,7 @@ fun AboutCard(navController: NavController) { ) ) Text( - text = airpodsInstance.actualModelNumber, + text = actualModel, style = TextStyle( fontSize = 16.sp, color = if (isDarkTheme) Color.White.copy(alpha = 0.6f) else Color.Black.copy(alpha = 0.8f), @@ -152,11 +152,11 @@ fun AboutCard(navController: NavController) { .padding(horizontal = 12.dp) ) val serialNumbers = listOf( - airpodsInstance.serialNumber?: "", - "􀀛 ${airpodsInstance.leftSerialNumber}", - "􀀧 ${airpodsInstance.rightSerialNumber}" + serialNumbers[0], + "􀀛 ${serialNumbers[1]}", + "􀀧 ${serialNumbers[2]}" ) - val serialNumber = remember { mutableStateOf(0) } + val serialNumber = remember { mutableIntStateOf(0) } Row( modifier = Modifier .fillMaxWidth() @@ -172,7 +172,7 @@ fun AboutCard(navController: NavController) { ), ) Text( - text = serialNumbers[serialNumber.value], + text = serialNumbers[serialNumber.intValue], style = TextStyle( fontSize = 16.sp, color = if (isDarkTheme) Color.White.copy(alpha = 0.6f) else Color.Black.copy(alpha = 0.8f), @@ -183,7 +183,7 @@ fun AboutCard(navController: NavController) { interactionSource = remember { MutableInteractionSource() }, indication = null ) { - serialNumber.value = (serialNumber.value + 1) % serialNumbers.size + serialNumber.intValue = (serialNumber.intValue + 1) % serialNumbers.size } ) } @@ -197,9 +197,9 @@ fun AboutCard(navController: NavController) { to = "version_info", navController = navController, name = stringResource(R.string.version), - currentState = airpodsInstance.version3, + currentState = version, independent = false, height = rowHeight.value + 32.dp ) } -} \ No newline at end of file +} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/AudioSettings.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/AudioSettings.kt index f6dbaa6..e4ead08 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/composables/AudioSettings.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/AudioSettings.kt @@ -42,25 +42,32 @@ import androidx.compose.ui.unit.sp import androidx.navigation.NavController import androidx.navigation.compose.rememberNavController import me.kavishdevar.librepods.R -import me.kavishdevar.librepods.services.ServiceManager -import me.kavishdevar.librepods.utils.AACPManager -import me.kavishdevar.librepods.utils.ATTHandles -import me.kavishdevar.librepods.utils.Capability import kotlin.io.encoding.ExperimentalEncodingApi @Composable -fun AudioSettings(navController: NavController) { +fun AudioSettings( + navController: NavController, + adaptiveVolumeCapability: Boolean, + conversationalAwarenessCapability: Boolean, + loudSoundReductionCapability: Boolean, + adaptiveAudioCapability: Boolean, + + adaptiveVolumeChecked: Boolean, + onAdaptiveVolumeCheckedChange: (Boolean) -> Unit, + + conversationalAwarenessChecked: Boolean, + onConversationalAwarenessCheckedChange: (Boolean) -> Unit, + + loudSoundReductionChecked: Boolean, + onLoudSoundReductionCheckedChange: (Boolean) -> Unit, + + isXposed: Boolean, + isPremium: Boolean +) { val isDarkTheme = isSystemInDarkTheme() val textColor = if (isDarkTheme) Color.White else Color.Black - val service = ServiceManager.getService() - if (service == null) return - val airpodsInstance = service.airpodsInstance - if (airpodsInstance == null) return - if (!airpodsInstance.model.capabilities.contains(Capability.ADAPTIVE_VOLUME) && - !airpodsInstance.model.capabilities.contains(Capability.CONVERSATION_AWARENESS) && - !airpodsInstance.model.capabilities.contains(Capability.LOUD_SOUND_REDUCTION) && - !airpodsInstance.model.capabilities.contains(Capability.ADAPTIVE_AUDIO) - ) { + + if (!adaptiveVolumeCapability && !conversationalAwarenessCapability && !loudSoundReductionCapability && !adaptiveAudioCapability) { return } Box( @@ -88,12 +95,14 @@ fun AudioSettings(navController: NavController) { .padding(top = 2.dp) ) { - if (airpodsInstance.model.capabilities.contains(Capability.ADAPTIVE_VOLUME)) { + if (adaptiveVolumeCapability) { StyledToggle( label = stringResource(R.string.personalized_volume), description = stringResource(R.string.personalized_volume_description), - controlCommandIdentifier = AACPManager.Companion.ControlCommandIdentifiers.ADAPTIVE_VOLUME_CONFIG, - independent = false + independent = false, + checked = adaptiveVolumeChecked, + onCheckedChange = onAdaptiveVolumeCheckedChange, + enabled = isPremium ) HorizontalDivider( @@ -104,12 +113,14 @@ fun AudioSettings(navController: NavController) { ) } - if (airpodsInstance.model.capabilities.contains(Capability.CONVERSATION_AWARENESS)) { + if (conversationalAwarenessCapability) { StyledToggle( label = stringResource(R.string.conversational_awareness), description = stringResource(R.string.conversational_awareness_description), - controlCommandIdentifier = AACPManager.Companion.ControlCommandIdentifiers.CONVERSATION_DETECT_CONFIG, - independent = false + independent = false, + checked = conversationalAwarenessChecked, + onCheckedChange = onConversationalAwarenessCheckedChange, + enabled = isPremium ) HorizontalDivider( thickness = 1.dp, @@ -119,12 +130,13 @@ fun AudioSettings(navController: NavController) { ) } - if (airpodsInstance.model.capabilities.contains(Capability.LOUD_SOUND_REDUCTION)){ + if (loudSoundReductionCapability && isXposed){ StyledToggle( label = stringResource(R.string.loud_sound_reduction), description = stringResource(R.string.loud_sound_reduction_description), - attHandle = ATTHandles.LOUD_SOUND_REDUCTION, - independent = false + independent = false, + checked = loudSoundReductionChecked, + onCheckedChange = onLoudSoundReductionCheckedChange ) HorizontalDivider( thickness = 1.dp, @@ -134,7 +146,7 @@ fun AudioSettings(navController: NavController) { ) } - if (airpodsInstance.model.capabilities.contains(Capability.ADAPTIVE_AUDIO)) { + if (adaptiveAudioCapability) { NavigationButton( to = "adaptive_strength", name = stringResource(R.string.adaptive_audio), @@ -148,5 +160,19 @@ fun AudioSettings(navController: NavController) { @Preview @Composable fun AudioSettingsPreview() { - AudioSettings(rememberNavController()) + AudioSettings( + navController = rememberNavController(), + adaptiveVolumeCapability = true, + conversationalAwarenessCapability = true, + loudSoundReductionCapability = true, + adaptiveAudioCapability = true, + adaptiveVolumeChecked = true, + onAdaptiveVolumeCheckedChange = { }, + conversationalAwarenessChecked = true, + onConversationalAwarenessCheckedChange = { }, + loudSoundReductionChecked = true, + onLoudSoundReductionCheckedChange = { }, + isXposed = true, + isPremium = true + ) } diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/BatteryView.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/BatteryView.kt index c98729d..e79ca7c 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/composables/BatteryView.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/BatteryView.kt @@ -20,13 +20,7 @@ package me.kavishdevar.librepods.composables -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter import android.content.res.Configuration -import android.os.Build -import android.util.Log import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme @@ -39,169 +33,101 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.imageResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import me.kavishdevar.librepods.R -import me.kavishdevar.librepods.constants.AirPodsNotifications import me.kavishdevar.librepods.constants.Battery import me.kavishdevar.librepods.constants.BatteryComponent import me.kavishdevar.librepods.constants.BatteryStatus -import me.kavishdevar.librepods.services.AirPodsService import kotlin.io.encoding.ExperimentalEncodingApi @Composable -fun BatteryView(service: AirPodsService, preview: Boolean = false) { - val batteryStatus = remember { mutableStateOf>(listOf()) } +fun BatteryView( + batteryList: List, + budsRes: Int, + caseRes: Int +) { + val left = batteryList.find { it.component == BatteryComponent.LEFT } + val right = batteryList.find { it.component == BatteryComponent.RIGHT } + val case = batteryList.find { it.component == BatteryComponent.CASE } - val previousBatteryStatus = remember { mutableStateOf>(listOf()) } - - @Suppress("DEPRECATION") val batteryReceiver = remember { - object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - if (intent.action == AirPodsNotifications.BATTERY_DATA) { - batteryStatus.value = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - intent.getParcelableArrayListExtra("data", Battery::class.java) - } else { - intent.getParcelableArrayListExtra("data") - }?.toList() ?: listOf() - } - else if (intent.action == AirPodsNotifications.DISCONNECT_RECEIVERS) { - try { - context.unregisterReceiver(this) - } - catch (_: IllegalArgumentException) { - Log.wtf("BatteryReceiver", "Receiver already unregistered") - } - } - } - } - } - val context = LocalContext.current - - LaunchedEffect(context) { - val batteryIntentFilter = IntentFilter() - .apply { - addAction(AirPodsNotifications.BATTERY_DATA) - addAction(AirPodsNotifications.DISCONNECT_RECEIVERS) - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - context.registerReceiver( - batteryReceiver, - batteryIntentFilter, - Context.RECEIVER_EXPORTED - ) - } - } - - previousBatteryStatus.value = batteryStatus.value - batteryStatus.value = service.getBattery() - - if (preview) { - batteryStatus.value = listOf( - Battery(BatteryComponent.LEFT, 100, BatteryStatus.NOT_CHARGING), - Battery(BatteryComponent.RIGHT, 94, BatteryStatus.CHARGING), - Battery(BatteryComponent.CASE, 40, BatteryStatus.CHARGING) - ) - previousBatteryStatus.value = batteryStatus.value - } - - val left = batteryStatus.value.find { it.component == BatteryComponent.LEFT } - val right = batteryStatus.value.find { it.component == BatteryComponent.RIGHT } - val case = batteryStatus.value.find { it.component == BatteryComponent.CASE } val leftLevel = left?.level ?: 0 val rightLevel = right?.level ?: 0 val caseLevel = case?.level ?: 0 - val leftCharging = left?.status == BatteryStatus.CHARGING || left?.status == BatteryStatus.OPTIMIZED_CHARGING - val rightCharging = right?.status == BatteryStatus.CHARGING || right?.status == BatteryStatus.OPTIMIZED_CHARGING - val caseCharging = case?.status == BatteryStatus.CHARGING || case?.status == BatteryStatus.OPTIMIZED_CHARGING - val prevLeft = previousBatteryStatus.value.find { it.component == BatteryComponent.LEFT } - val prevRight = previousBatteryStatus.value.find { it.component == BatteryComponent.RIGHT } - val prevCase = previousBatteryStatus.value.find { it.component == BatteryComponent.CASE } - val prevLeftCharging = prevLeft?.status == BatteryStatus.CHARGING - val prevRightCharging = prevRight?.status == BatteryStatus.CHARGING - val prevCaseCharging = prevCase?.status == BatteryStatus.CHARGING + val leftCharging = left?.status == BatteryStatus.CHARGING || + left?.status == BatteryStatus.OPTIMIZED_CHARGING + + val rightCharging = right?.status == BatteryStatus.CHARGING || + right?.status == BatteryStatus.OPTIMIZED_CHARGING + + val caseCharging = case?.status == BatteryStatus.CHARGING || + case?.status == BatteryStatus.OPTIMIZED_CHARGING val singleDisplayed = remember { mutableStateOf(false) } - val airpodsInstance = service.airpodsInstance - if (airpodsInstance == null) { - return - } - val budsRes = airpodsInstance.model.budsRes - val caseRes = airpodsInstance.model.caseRes - Row { - Column ( - modifier = Modifier - .fillMaxWidth(0.5f), + Column( + modifier = Modifier.fillMaxWidth(0.5f), horizontalAlignment = Alignment.CenterHorizontally ) { - Image ( + Image( bitmap = ImageBitmap.imageResource(budsRes), contentDescription = stringResource(R.string.buds), modifier = Modifier .fillMaxWidth() .padding(8.dp) ) + if ( leftCharging == rightCharging && (leftLevel - rightLevel) in -3..3 - ) - { + ) { BatteryIndicator( leftLevel.coerceAtMost(rightLevel), - leftCharging, - previousCharging = (prevLeftCharging && prevRightCharging) + leftCharging ) singleDisplayed.value = true - } - else { + } else { singleDisplayed.value = false - Row ( - modifier = Modifier - .fillMaxWidth(), + + Row( + modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center ) { if (leftLevel > 0 || left?.status != BatteryStatus.DISCONNECTED) { BatteryIndicator( leftLevel, leftCharging, - "\uDBC6\uDCE5", - previousCharging = prevLeftCharging + "\uDBC6\uDCE5" ) } - if (leftLevel > 0 && rightLevel > 0) - { + + if (leftLevel > 0 && rightLevel > 0) { Spacer(modifier = Modifier.width(16.dp)) } - if (rightLevel > 0 || right?.status != BatteryStatus.DISCONNECTED) - { + + if (rightLevel > 0 || right?.status != BatteryStatus.DISCONNECTED) { BatteryIndicator( rightLevel, rightCharging, - "\uDBC6\uDCE8", - previousCharging = prevRightCharging + "\uDBC6\uDCE8" ) } } } } - Column ( - modifier = Modifier - .fillMaxWidth(), + Column( + modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally ) { Image( @@ -211,14 +137,14 @@ fun BatteryView(service: AirPodsService, preview: Boolean = false) { .fillMaxWidth() .padding(8.dp) ) - if (caseLevel > 0 || case?.status != BatteryStatus.DISCONNECTED) { - BatteryIndicator( - caseLevel, - caseCharging, - prefix = if (!singleDisplayed.value) "\uDBC3\uDE6C" else "", - previousCharging = prevCaseCharging - ) - } + + if (caseLevel > 0 || case?.status != BatteryStatus.DISCONNECTED) { + BatteryIndicator( + caseLevel, + caseCharging, + prefix = if (!singleDisplayed.value) "\uDBC3\uDE6C" else "" + ) + } } } } @@ -226,10 +152,23 @@ fun BatteryView(service: AirPodsService, preview: Boolean = false) { @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable fun BatteryViewPreview() { + val fakeBattery = listOf( + Battery(BatteryComponent.LEFT, 85, BatteryStatus.CHARGING), + Battery(BatteryComponent.RIGHT, 40, BatteryStatus.CHARGING), + Battery(BatteryComponent.CASE, 60, BatteryStatus.NOT_CHARGING) + ) + val bg = if (isSystemInDarkTheme()) Color.Black else Color(0xFFF2F2F7) + Box( - modifier = Modifier.background(bg) + modifier = Modifier + .background(bg) + .padding(16.dp) ) { - BatteryView(AirPodsService(), preview = true) + BatteryView( + batteryList = fakeBattery, + budsRes = R.drawable.airpods_pro_2_buds, + caseRes = R.drawable.airpods_pro_2_case + ) } } diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/CallControlSettings.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/CallControlSettings.kt index 09b80ff..ec8ae16 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/composables/CallControlSettings.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/CallControlSettings.kt @@ -36,7 +36,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableLongStateOf @@ -56,19 +55,20 @@ 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.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import dev.chrisbanes.haze.HazeState import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi import me.kavishdevar.librepods.R -import me.kavishdevar.librepods.services.ServiceManager -import me.kavishdevar.librepods.utils.AACPManager import kotlin.io.encoding.ExperimentalEncodingApi @ExperimentalHazeMaterialsApi @Composable -fun CallControlSettings(hazeState: HazeState) { +fun CallControlSettings( + hazeState: HazeState, + flipped: Boolean, + onCallControlValueChanged: (Boolean) -> Unit +) { val isDarkTheme = isSystemInDarkTheme() val textColor = if (isDarkTheme) Color.White else Color.Black val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF) @@ -93,24 +93,9 @@ fun CallControlSettings(hazeState: HazeState) { .background(backgroundColor, RoundedCornerShape(28.dp)) .padding(top = 2.dp) ) { - val service = ServiceManager.getService()!! - val callControlEnabledValue = service.aacpManager.controlCommandStatusList.find { - it.identifier == AACPManager.Companion.ControlCommandIdentifiers.CALL_MANAGEMENT_CONFIG - }?.value ?: byteArrayOf(0x00, 0x03) - val pressOnceText = stringResource(R.string.press_once) val pressTwiceText = stringResource(R.string.press_twice) - var flipped by remember { - mutableStateOf( - callControlEnabledValue.contentEquals( - byteArrayOf( - 0x00, - 0x02 - ) - ) - ) - } var singlePressAction by remember { mutableStateOf(if (flipped) pressTwiceText else pressOnceText) } var doublePressAction by remember { mutableStateOf(if (flipped) pressOnceText else pressTwiceText) } @@ -128,35 +113,6 @@ fun CallControlSettings(hazeState: HazeState) { var parentHoveredIndexDouble by remember { mutableStateOf(null) } var parentDragActiveDouble by remember { mutableStateOf(false) } - LaunchedEffect(Unit) { - val listener = object : AACPManager.ControlCommandListener { - override fun onControlCommandReceived(controlCommand: AACPManager.ControlCommand) { - if (AACPManager.Companion.ControlCommandIdentifiers.fromByte(controlCommand.identifier) == - AACPManager.Companion.ControlCommandIdentifiers.CALL_MANAGEMENT_CONFIG - ) { - val newFlipped = controlCommand.value.contentEquals(byteArrayOf(0x00, 0x02)) - flipped = newFlipped - singlePressAction = if (newFlipped) pressTwiceText else pressOnceText - doublePressAction = if (newFlipped) pressOnceText else pressTwiceText - Log.d( - "CallControlSettings", - "Control command received, flipped: $newFlipped" - ) - } - } - } - - service.aacpManager.registerControlCommandListener( - AACPManager.Companion.ControlCommandIdentifiers.CALL_MANAGEMENT_CONFIG, - listener - ) - } - - DisposableEffect(Unit) { - onDispose { - service.aacpManager.controlCommandListeners[AACPManager.Companion.ControlCommandIdentifiers.CALL_MANAGEMENT_CONFIG]?.clear() - } - } LaunchedEffect(flipped) { Log.d("CallControlSettings", "Call control flipped: $flipped") } @@ -244,11 +200,8 @@ fun CallControlSettings(hazeState: HazeState) { if (option == pressOnceText) pressTwiceText else pressOnceText showSinglePressDropdown = false lastDismissTimeSingle = System.currentTimeMillis() - val bytes = if (option == pressOnceText) byteArrayOf( - 0x00, - 0x03 - ) else byteArrayOf(0x00, 0x02) - service.aacpManager.sendControlCommand(0x24, bytes) + onCallControlValueChanged(option != pressOnceText) + } } parentHoveredIndexSingle = null @@ -313,11 +266,8 @@ fun CallControlSettings(hazeState: HazeState) { doublePressAction = if (option == pressOnceText) pressTwiceText else pressOnceText showSinglePressDropdown = false - val bytes = if (option == pressOnceText) byteArrayOf( - 0x00, - 0x03 - ) else byteArrayOf(0x00, 0x02) - service.aacpManager.sendControlCommand(0x24, bytes) + val flipped = option != pressOnceText + onCallControlValueChanged(flipped) }, hazeState = hazeState ) @@ -379,11 +329,8 @@ fun CallControlSettings(hazeState: HazeState) { if (option == pressOnceText) pressTwiceText else pressOnceText showDoublePressDropdown = false lastDismissTimeDouble = System.currentTimeMillis() - val bytes = if (option == pressOnceText) byteArrayOf( - 0x00, - 0x02 - ) else byteArrayOf(0x00, 0x03) - service.aacpManager.sendControlCommand(0x24, bytes) + val flipped = option == pressOnceText + onCallControlValueChanged (flipped) } } parentHoveredIndexDouble = null @@ -448,11 +395,8 @@ fun CallControlSettings(hazeState: HazeState) { singlePressAction = if (option == pressOnceText) pressTwiceText else pressOnceText showDoublePressDropdown = false - val bytes = if (option == pressOnceText) byteArrayOf( - 0x00, - 0x02 - ) else byteArrayOf(0x00, 0x03) - service.aacpManager.sendControlCommand(0x24, bytes) + val flipped = option == pressOnceText + onCallControlValueChanged(flipped) }, hazeState = hazeState ) @@ -461,10 +405,3 @@ fun CallControlSettings(hazeState: HazeState) { } } } - -@ExperimentalHazeMaterialsApi -@Preview -@Composable -fun CallControlSettingsPreview() { - CallControlSettings(HazeState()) -} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/ConnectionSettings.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/ConnectionSettings.kt index a21bfd1..b95807c 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/composables/ConnectionSettings.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/ConnectionSettings.kt @@ -20,7 +20,6 @@ package me.kavishdevar.librepods.composables -import android.content.Context.MODE_PRIVATE import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Column @@ -31,16 +30,18 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import me.kavishdevar.librepods.R -import me.kavishdevar.librepods.utils.AACPManager import kotlin.io.encoding.ExperimentalEncodingApi @Composable -fun ConnectionSettings() { +fun ConnectionSettings( + automaticEarDetectionEnabled: Boolean, + onAutomaticEarDetectionChanged: (Boolean) -> Unit, + automaticConnectionEnabled: Boolean, + onAutomaticConnectionChanged: (Boolean) -> Unit, +) { val isDarkTheme = isSystemInDarkTheme() val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF) @@ -52,10 +53,9 @@ fun ConnectionSettings() { ) { StyledToggle( label = stringResource(R.string.ear_detection), - controlCommandIdentifier = AACPManager.Companion.ControlCommandIdentifiers.EAR_DETECTION_CONFIG, - sharedPreferenceKey = "automatic_ear_detection", - sharedPreferences = LocalContext.current.getSharedPreferences("settings", MODE_PRIVATE), - independent = false + independent = false, + checked = automaticEarDetectionEnabled, + onCheckedChange = onAutomaticEarDetectionChanged ) HorizontalDivider( thickness = 1.dp, @@ -67,16 +67,9 @@ fun ConnectionSettings() { StyledToggle( label = stringResource(R.string.automatically_connect), description = stringResource(R.string.automatically_connect_description), - controlCommandIdentifier = AACPManager.Companion.ControlCommandIdentifiers.AUTOMATIC_CONNECTION_CONFIG, - sharedPreferenceKey = "automatic_connection_ctrl_cmd", - sharedPreferences = LocalContext.current.getSharedPreferences("settings", MODE_PRIVATE), - independent = false + independent = false, + checked = automaticConnectionEnabled, + onCheckedChange = onAutomaticConnectionChanged ) } } - -@Preview -@Composable -fun ConnectionSettingsPreview() { - ConnectionSettings() -} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/HearingHealthSettings.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/HearingHealthSettings.kt index fe75489..1379d27 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/composables/HearingHealthSettings.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/HearingHealthSettings.kt @@ -40,70 +40,76 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController import me.kavishdevar.librepods.R -import me.kavishdevar.librepods.composables.NavigationButton -import me.kavishdevar.librepods.services.ServiceManager -import me.kavishdevar.librepods.utils.Capability import kotlin.io.encoding.ExperimentalEncodingApi @Composable -fun HearingHealthSettings(navController: NavController) { - val service = ServiceManager.getService() - if (service == null) return - val airpodsInstance = service.airpodsInstance - if (airpodsInstance == null) return - if (airpodsInstance.model.capabilities.contains(Capability.HEARING_AID)) { - val isDarkTheme = isSystemInDarkTheme() - val textColor = if (isDarkTheme) Color.White else Color.Black - val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF) +fun HearingHealthSettings( + navController: NavController, + hasPPECapability: Boolean, + hasHearingAidCapability: Boolean, + isXposed: Boolean +) { + val isDarkTheme = isSystemInDarkTheme() + val textColor = if (isDarkTheme) Color.White else Color.Black + val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF) + val shouldShowHearingAid = hasHearingAidCapability && isXposed - if (airpodsInstance.model.capabilities.contains(Capability.PPE)) { - Box( + if (hasPPECapability && shouldShowHearingAid) { + Box( + modifier = Modifier + .background(if (isDarkTheme) Color(0xFF000000) else Color(0xFFF2F2F7)) + .padding(horizontal = 16.dp, vertical = 4.dp) + ){ + Text( + text = stringResource(R.string.hearing_health), + style = TextStyle( + fontSize = 14.sp, + fontWeight = FontWeight.Bold, + color = textColor.copy(alpha = 0.6f) + ) + ) + } + Column( + modifier = Modifier + .clip(RoundedCornerShape(28.dp)) + .fillMaxWidth() + .background(backgroundColor, RoundedCornerShape(28.dp)) + .padding(top = 2.dp) + ) { + NavigationButton( + to = "hearing_protection", + name = stringResource(R.string.hearing_protection), + navController = navController, + independent = false + ) + + HorizontalDivider( + thickness = 1.dp, + color = Color(0x40888888), modifier = Modifier - .background(if (isDarkTheme) Color(0xFF000000) else Color(0xFFF2F2F7)) - .padding(horizontal = 16.dp, vertical = 4.dp) - ){ - Text( - text = stringResource(R.string.hearing_health), - style = TextStyle( - fontSize = 14.sp, - fontWeight = FontWeight.Bold, - color = textColor.copy(alpha = 0.6f) - ) - ) - } - Column( - modifier = Modifier - .clip(RoundedCornerShape(28.dp)) - .fillMaxWidth() - .background(backgroundColor, RoundedCornerShape(28.dp)) - .padding(top = 2.dp) - ) { - NavigationButton( - to = "hearing_protection", - name = stringResource(R.string.hearing_protection), - navController = navController, - independent = false - ) - HorizontalDivider( - thickness = 1.dp, - color = Color(0x40888888), - modifier = Modifier - .padding(horizontal = 12.dp) - ) - - NavigationButton( - to = "hearing_aid", - name = stringResource(R.string.hearing_aid), - navController = navController, - independent = false - ) - } - } else { + .padding(horizontal = 12.dp) + ) + + NavigationButton( to = "hearing_aid", name = stringResource(R.string.hearing_aid), - navController = navController + navController = navController, + independent = false ) } + } else if (shouldShowHearingAid) { + NavigationButton( + to = "hearing_aid", + name = stringResource(R.string.hearing_aid), + navController = navController + ) + } else if (hasPPECapability) { + NavigationButton( + to = "hearing_protection", + name = stringResource(R.string.hearing_protection), + title = stringResource(R.string.hearing_health), + navController = navController + ) } -} \ No newline at end of file +} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/MicrophoneSettings.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/MicrophoneSettings.kt index bba8c70..5ade04d 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/composables/MicrophoneSettings.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/MicrophoneSettings.kt @@ -35,8 +35,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.mutableStateOf @@ -54,19 +52,21 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import dev.chrisbanes.haze.HazeState import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi import me.kavishdevar.librepods.R -import me.kavishdevar.librepods.services.ServiceManager import me.kavishdevar.librepods.utils.AACPManager import kotlin.io.encoding.ExperimentalEncodingApi @ExperimentalHazeMaterialsApi @Composable -fun MicrophoneSettings(hazeState: HazeState) { +fun MicrophoneSettings( + hazeState: HazeState, + micModeValue: Byte, + onMicModeValueChanged: (Byte) -> Unit +) { val isDarkTheme = isSystemInDarkTheme() val textColor = if (isDarkTheme) Color.White else Color.Black val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF) @@ -77,11 +77,6 @@ fun MicrophoneSettings(hazeState: HazeState) { .background(backgroundColor, RoundedCornerShape(28.dp)) .padding(top = 2.dp) ) { - val service = ServiceManager.getService()!! - val micModeValue = service.aacpManager.controlCommandStatusList.find { - it.identifier == AACPManager.Companion.ControlCommandIdentifiers.MIC_MODE - }?.value?.get(0) ?: 0x00.toByte() - var selectedMode by remember { mutableStateOf( when (micModeValue) { @@ -114,22 +109,6 @@ fun MicrophoneSettings(hazeState: HazeState) { } } - LaunchedEffect(Unit) { - service.aacpManager.registerControlCommandListener( - AACPManager.Companion.ControlCommandIdentifiers.MIC_MODE, - listener - ) - } - - DisposableEffect(Unit) { - onDispose { - service.aacpManager.unregisterControlCommandListener( - AACPManager.Companion.ControlCommandIdentifiers.MIC_MODE, - listener - ) - } - } - val density = LocalDensity.current val itemHeightPx = with(density) { 48.dp.toPx() } var parentHoveredIndex by remember { mutableStateOf(null) } @@ -194,10 +173,11 @@ fun MicrophoneSettings(hazeState: HazeState) { options[2] -> 0x02 else -> 0x00 } - service.aacpManager.sendControlCommand( - AACPManager.Companion.ControlCommandIdentifiers.MIC_MODE.value, - byteArrayOf(byteValue.toByte()) - ) +// service.aacpManager.sendControlCommand( +// AACPManager.Companion.ControlCommandIdentifiers.MIC_MODE.value, +// byteArrayOf(byteValue.toByte()) +// ) + onMicModeValueChanged(byteValue.toByte()) } } parentHoveredIndex = null @@ -277,10 +257,7 @@ fun MicrophoneSettings(hazeState: HazeState) { microphoneAlwaysLeftText -> 0x02 else -> 0x00 } - service.aacpManager.sendControlCommand( - AACPManager.Companion.ControlCommandIdentifiers.MIC_MODE.value, - byteArrayOf(byteValue.toByte()) - ) + onMicModeValueChanged(byteValue.toByte()) }, hazeState = hazeState ) @@ -288,10 +265,3 @@ fun MicrophoneSettings(hazeState: HazeState) { } } } - -@ExperimentalHazeMaterialsApi -@Preview -@Composable -fun MicrophoneSettingsPreview() { - MicrophoneSettings(HazeState()) -} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/NoiseControlSettings.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/NoiseControlSettings.kt index 7188100..699ed37 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/composables/NoiseControlSettings.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/NoiseControlSettings.kt @@ -21,11 +21,6 @@ package me.kavishdevar.librepods.composables import android.annotation.SuppressLint -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.os.Build import androidx.compose.animation.core.AnimationSpec import androidx.compose.animation.core.Spring import androidx.compose.animation.core.SpringSpec @@ -60,48 +55,28 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.imageResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.zIndex import me.kavishdevar.librepods.R -import me.kavishdevar.librepods.constants.AirPodsNotifications import me.kavishdevar.librepods.constants.NoiseControlMode -import me.kavishdevar.librepods.services.AirPodsService -import me.kavishdevar.librepods.utils.AACPManager import kotlin.io.encoding.ExperimentalEncodingApi import kotlin.math.roundToInt @SuppressLint("UnspecifiedRegisterReceiverFlag", "UnusedBoxWithConstraintsScope") @Composable fun NoiseControlSettings( - service: AirPodsService, + showOffListeningMode: Boolean, + noiseControlModeValue: Int, + onNoiseControlModeChanged: (Int) -> Unit ) { - val context = LocalContext.current - val offListeningModeConfigValue = service.aacpManager.controlCommandStatusList.find { - it.identifier == AACPManager.Companion.ControlCommandIdentifiers.ALLOW_OFF_OPTION - }?.value?.takeIf { it.isNotEmpty() }?.get(0) != 2.toByte() - val offListeningMode = remember { mutableStateOf(offListeningModeConfigValue) } - - val offListeningModeListener = object: AACPManager.ControlCommandListener { - override fun onControlCommandReceived(controlCommand: AACPManager.ControlCommand) { - offListeningMode.value = controlCommand.value[0] != 2.toByte() - } - } - - service.aacpManager.registerControlCommandListener( - AACPManager.Companion.ControlCommandIdentifiers.ALLOW_OFF_OPTION, - offListeningModeListener - ) - val isDarkTheme = isSystemInDarkTheme() val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFE3E3E8) val textColor = if (isDarkTheme) Color.White else Color.Black @@ -109,7 +84,6 @@ fun NoiseControlSettings( val selectedBackground = if (isDarkTheme) Color(0xBF5C5A5F) else Color(0xFFFFFFFF) - val noiseControlModeFromService = service.aacpManager.getControlCommandStatus(AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE) val noiseControlMode = remember { mutableStateOf(NoiseControlMode.OFF) } @@ -117,10 +91,11 @@ fun NoiseControlSettings( val d2a = remember { mutableFloatStateOf(0f) } val d3a = remember { mutableFloatStateOf(0f) } + // this function exists solely for the dividers, should get rid of it fun onModeSelected(mode: NoiseControlMode, received: Boolean = false) { val previousMode = noiseControlMode.value - val targetMode = if (!offListeningMode.value && mode == NoiseControlMode.OFF) { + val targetMode = if (!showOffListeningMode && mode == NoiseControlMode.OFF) { NoiseControlMode.TRANSPARENCY } else { mode @@ -128,9 +103,8 @@ fun NoiseControlSettings( noiseControlMode.value = targetMode - if (!received && targetMode != previousMode) { - service.aacpManager.sendControlCommand(identifier = AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE.value, value = targetMode.ordinal + 1) - } + if (!received && targetMode != previousMode) onNoiseControlModeChanged(targetMode.ordinal + 1) + when (noiseControlMode.value) { NoiseControlMode.NOISE_CANCELLATION -> { @@ -157,42 +131,11 @@ fun NoiseControlSettings( } - if (noiseControlModeFromService != null) { - val value = noiseControlModeFromService.value - if (value.isNotEmpty()) { - val index = (value[0].toInt() - 1).coerceIn(0, NoiseControlMode.entries.size - 1) - noiseControlMode.value = NoiseControlMode.entries[index] + val index = (noiseControlModeValue - 1).coerceIn(0, NoiseControlMode.entries.size - 1) + noiseControlMode.value = NoiseControlMode.entries[index] - onModeSelected(noiseControlMode.value, received = true) - } - } + onModeSelected(noiseControlMode.value, received = true) - val noiseControlReceiver = remember { - object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - if (intent.action == AirPodsNotifications.ANC_DATA) { - noiseControlMode.value = NoiseControlMode.entries.toTypedArray()[intent.getIntExtra("data", 3) - 1] - onModeSelected(noiseControlMode.value, true) - } else if (intent.action == AirPodsNotifications.DISCONNECT_RECEIVERS) { - try { - context.unregisterReceiver(this) - } catch (e: IllegalArgumentException) { - e.printStackTrace() - } - } - } - } - } - - val noiseControlIntentFilter = IntentFilter().apply { - addAction(AirPodsNotifications.ANC_DATA) - addAction(AirPodsNotifications.DISCONNECT_RECEIVERS) - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - context.registerReceiver(noiseControlReceiver, noiseControlIntentFilter, Context.RECEIVER_EXPORTED) - } else { - context.registerReceiver(noiseControlReceiver, noiseControlIntentFilter) - } Box( modifier = Modifier .background(if (isDarkTheme) Color(0xFF000000) else Color(0xFFF2F2F7)) @@ -207,14 +150,14 @@ fun NoiseControlSettings( ) ) } - @Suppress("COMPOSE_APPLIER_CALL_MISMATCH") + BoxWithConstraints( modifier = Modifier .fillMaxWidth() .padding(vertical = 8.dp) ) { val density = LocalDensity.current - val buttonCount = if (offListeningMode.value) 4 else 3 + val buttonCount = if (showOffListeningMode) 4 else 3 val buttonWidth = maxWidth / buttonCount val isDragging = remember { mutableStateOf(false) } @@ -222,10 +165,10 @@ fun NoiseControlSettings( mutableFloatStateOf( with(density) { when(noiseControlMode.value) { - NoiseControlMode.OFF -> if (offListeningMode.value) 0f else buttonWidth.toPx() - NoiseControlMode.TRANSPARENCY -> if (offListeningMode.value) buttonWidth.toPx() else 0f - NoiseControlMode.ADAPTIVE -> if (offListeningMode.value) (buttonWidth * 2).toPx() else buttonWidth.toPx() - NoiseControlMode.NOISE_CANCELLATION -> if (offListeningMode.value) (buttonWidth * 3).toPx() else (buttonWidth * 2).toPx() + NoiseControlMode.OFF -> if (showOffListeningMode) 0f else buttonWidth.toPx() + NoiseControlMode.TRANSPARENCY -> if (showOffListeningMode) buttonWidth.toPx() else 0f + NoiseControlMode.ADAPTIVE -> if (showOffListeningMode) (buttonWidth * 2).toPx() else buttonWidth.toPx() + NoiseControlMode.NOISE_CANCELLATION -> if (showOffListeningMode) (buttonWidth * 3).toPx() else (buttonWidth * 2).toPx() } } ) @@ -238,10 +181,10 @@ fun NoiseControlSettings( ) val targetOffset = buttonWidth * when(noiseControlMode.value) { - NoiseControlMode.OFF -> if (offListeningMode.value) 0 else 1 - NoiseControlMode.TRANSPARENCY -> if (offListeningMode.value) 1 else 0 - NoiseControlMode.ADAPTIVE -> if (offListeningMode.value) 2 else 1 - NoiseControlMode.NOISE_CANCELLATION -> if (offListeningMode.value) 3 else 2 + NoiseControlMode.OFF -> if (showOffListeningMode) 0 else 1 + NoiseControlMode.TRANSPARENCY -> if (showOffListeningMode) 1 else 0 + NoiseControlMode.ADAPTIVE -> if (showOffListeningMode) 2 else 1 + NoiseControlMode.NOISE_CANCELLATION -> if (showOffListeningMode) 3 else 2 } val animatedOffset by animateFloatAsState( @@ -264,7 +207,7 @@ fun NoiseControlSettings( Row( modifier = Modifier.fillMaxWidth() ) { - if (offListeningMode.value) { + if (showOffListeningMode) { NoiseControlButton( icon = ImageBitmap.imageResource(R.drawable.noise_cancellation), onClick = { onModeSelected(NoiseControlMode.OFF) }, @@ -337,13 +280,12 @@ fun NoiseControlSettings( val position = dragOffset / with(density) { buttonWidth.toPx() } val newIndex = position.roundToInt() val newMode = when(newIndex) { - 0 -> if (offListeningMode.value) NoiseControlMode.OFF else NoiseControlMode.TRANSPARENCY - 1 -> if (offListeningMode.value) NoiseControlMode.TRANSPARENCY else NoiseControlMode.ADAPTIVE - 2 -> if (offListeningMode.value) NoiseControlMode.ADAPTIVE else NoiseControlMode.NOISE_CANCELLATION + 0 -> if (showOffListeningMode) NoiseControlMode.OFF else NoiseControlMode.TRANSPARENCY + 1 -> if (showOffListeningMode) NoiseControlMode.TRANSPARENCY else NoiseControlMode.ADAPTIVE + 2 -> if (showOffListeningMode) NoiseControlMode.ADAPTIVE else NoiseControlMode.NOISE_CANCELLATION 3 -> NoiseControlMode.NOISE_CANCELLATION else -> noiseControlMode.value // Keep current if index is invalid } - // Call onModeSelected which now handles service call but not callback onModeSelected(newMode) } ) @@ -361,7 +303,7 @@ fun NoiseControlSettings( .fillMaxWidth() .zIndex(1f) ) { - if (offListeningMode.value) { + if (showOffListeningMode) { NoiseControlButton( icon = ImageBitmap.imageResource(R.drawable.noise_cancellation), onClick = { onModeSelected(NoiseControlMode.OFF) }, @@ -420,7 +362,7 @@ fun NoiseControlSettings( .fillMaxWidth() .padding(top = 4.dp) ) { - if (offListeningMode.value) { + if (showOffListeningMode) { Text( text = stringResource(R.string.off), style = TextStyle(fontSize = 12.sp, color = textColor), @@ -450,9 +392,3 @@ fun NoiseControlSettings( } } } - -@Preview -@Composable -fun NoiseControlSettingsPreview() { - NoiseControlSettings(AirPodsService()) -} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/PressAndHoldSettings.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/PressAndHoldSettings.kt index 1eddfaf..1861298 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/composables/PressAndHoldSettings.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/PressAndHoldSettings.kt @@ -18,15 +18,11 @@ package me.kavishdevar.librepods.composables -import android.content.Context -import android.content.res.Configuration import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.HorizontalDivider @@ -35,13 +31,11 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource 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.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController @@ -49,24 +43,22 @@ import me.kavishdevar.librepods.R import me.kavishdevar.librepods.constants.StemAction @Composable -fun PressAndHoldSettings(navController: NavController) { +fun PressAndHoldSettings( + navController: NavController, + leftAction: StemAction, + rightAction: StemAction +) { val isDarkTheme = isSystemInDarkTheme() val textColor = if (isDarkTheme) Color.White else Color.Black val dividerColor = Color(0x40888888) - val context = LocalContext.current - val sharedPreferences = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - - val leftAction = sharedPreferences.getString("left_long_press_action", StemAction.CYCLE_NOISE_CONTROL_MODES.name) - val rightAction = sharedPreferences.getString("right_long_press_action", StemAction.CYCLE_NOISE_CONTROL_MODES.name) - - val leftActionText = when (StemAction.valueOf(leftAction ?: StemAction.CYCLE_NOISE_CONTROL_MODES.name)) { + val leftActionText = when (leftAction) { StemAction.CYCLE_NOISE_CONTROL_MODES -> stringResource(R.string.noise_control) StemAction.DIGITAL_ASSISTANT -> "Digital Assistant" else -> "INVALID!!" } - val rightActionText = when (StemAction.valueOf(rightAction ?: StemAction.CYCLE_NOISE_CONTROL_MODES.name)) { + val rightActionText = when (rightAction) { StemAction.CYCLE_NOISE_CONTROL_MODES -> stringResource(R.string.noise_control) StemAction.DIGITAL_ASSISTANT -> "Digital Assistant" else -> "INVALID!!" @@ -114,9 +106,3 @@ fun PressAndHoldSettings(navController: NavController) { ) } } - -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) -@Composable -fun PressAndHoldSettingsPreview() { - PressAndHoldSettings(navController = NavController(LocalContext.current)) -} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledButton.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledButton.kt index 93ea96e..771dd24 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledButton.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledButton.kt @@ -55,7 +55,7 @@ import androidx.compose.ui.util.lerp import com.kyant.backdrop.Backdrop import com.kyant.backdrop.drawBackdrop import com.kyant.backdrop.effects.blur -import com.kyant.backdrop.effects.refraction +import com.kyant.backdrop.effects.lens import com.kyant.backdrop.effects.vibrancy import com.kyant.backdrop.highlight.Highlight import kotlinx.coroutines.launch @@ -146,7 +146,12 @@ half4 main(float2 coord) { effects = { vibrancy() blur(2f.dp.toPx()) - refraction(12f.dp.toPx(), 24f.dp.toPx()) + lens( + refractionHeight = 12f.dp.toPx(), + refractionAmount = 24f.dp.toPx(), + depthEffect = true, + chromaticAberration = true + ) }, layerBlock = { val width = size.width diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledIconButton.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledIconButton.kt index 6454ee5..9a12561 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledIconButton.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledIconButton.kt @@ -63,8 +63,7 @@ import androidx.compose.ui.util.lerp import com.kyant.backdrop.backdrops.LayerBackdrop import com.kyant.backdrop.backdrops.rememberLayerBackdrop import com.kyant.backdrop.drawBackdrop -import com.kyant.backdrop.effects.blur -import com.kyant.backdrop.effects.refractionWithDispersion +import com.kyant.backdrop.effects.lens import com.kyant.backdrop.highlight.Highlight import com.kyant.backdrop.shadow.Shadow import kotlinx.coroutines.launch @@ -78,13 +77,13 @@ import kotlin.math.tanh @Composable fun StyledIconButton( - onClick: () -> Unit, + modifier: Modifier = Modifier, icon: String, - darkMode: Boolean, tint: Color = Color.Unspecified, backdrop: LayerBackdrop = rememberLayerBackdrop(), - modifier: Modifier = Modifier, + onClick: () -> Unit ) { + val darkMode = isSystemInDarkTheme() val animationScope = rememberCoroutineScope() val progressAnimationSpec = spring(0.5f, 300f, 0.001f) val offsetAnimationSpec = spring(1f, 300f, Offset.VisibilityThreshold) @@ -218,8 +217,12 @@ half4 main(float2 coord) { } }, effects = { - refractionWithDispersion(6f.dp.toPx(), size.height / 2f) - // blur(24f, TileMode.Decal) + lens( + refractionHeight = 6f.dp.toPx(), + refractionAmount = size.height / 2f, + depthEffect = true, + chromaticAberration = true + ) }, ) .pointerInput(animationScope) { diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledScaffold.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledScaffold.kt index 21fdc19..bca74ab 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledScaffold.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledScaffold.kt @@ -61,7 +61,6 @@ import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi import dev.chrisbanes.haze.rememberHazeState import me.kavishdevar.librepods.R -@ExperimentalHazeMaterialsApi @Composable fun StyledScaffold( title: String, @@ -133,7 +132,6 @@ fun StyledScaffold( } -@ExperimentalHazeMaterialsApi @Composable fun StyledScaffold( title: String, @@ -150,7 +148,6 @@ fun StyledScaffold( } } -@ExperimentalHazeMaterialsApi @Composable fun StyledScaffold( title: String, diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledSelectList.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledSelectList.kt index c91fa1b..90af965 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledSelectList.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledSelectList.kt @@ -48,7 +48,6 @@ import androidx.compose.ui.res.painterResource 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.unit.dp import androidx.compose.ui.unit.sp import me.kavishdevar.librepods.R @@ -59,19 +58,10 @@ data class SelectItem( val iconRes: Int? = null, val selected: Boolean, val onClick: () -> Unit, + val visible: Boolean = true, val enabled: Boolean = true ) -data class SelectItem2( - val name: String, - val description: String? = null, - val iconRes: Int? = null, - val selected: () -> Boolean, - val onClick: () -> Unit, - val enabled: Boolean = true -) - - @Composable fun StyledSelectList( items: List, @@ -87,18 +77,19 @@ fun StyledSelectList( .background(backgroundColor, RoundedCornerShape(28.dp)), horizontalAlignment = Alignment.CenterHorizontally ) { - val visibleItems = items.filter { it.enabled } + val visibleItems = items.filter { it.visible } visibleItems.forEachIndexed { index, item -> val isFirst = index == 0 val isLast = index == visibleItems.size - 1 val hasIcon = item.iconRes != null val shape = when { + isFirst && isLast -> RoundedCornerShape(28.dp) isFirst -> RoundedCornerShape(topStart = 28.dp, topEnd = 28.dp) isLast -> RoundedCornerShape(bottomStart = 28.dp, bottomEnd = 28.dp) else -> RoundedCornerShape(0.dp) } - var itemBackgroundColor by remember { mutableStateOf(backgroundColor) } + var itemBackgroundColor by remember { mutableStateOf(if (item.enabled) backgroundColor else if (isDarkTheme) Color(0x40050505) else Color(0x40D9D9D9)) } val animatedBackgroundColor by animateColorAsState(targetValue = itemBackgroundColor, animationSpec = tween(durationMillis = 500)) Row( @@ -108,10 +99,13 @@ fun StyledSelectList( .pointerInput(Unit) { detectTapGestures( onPress = { - itemBackgroundColor = if (isDarkTheme) Color(0x40888888) else Color(0x40D9D9D9) - tryAwaitRelease() - itemBackgroundColor = backgroundColor - item.onClick() + if (item.enabled) { + itemBackgroundColor = + if (isDarkTheme) Color(0x40888888) else Color(0x40D9D9D9) + tryAwaitRelease() + itemBackgroundColor = backgroundColor + item.onClick() + } } ) } @@ -121,7 +115,7 @@ fun StyledSelectList( ) { if (hasIcon) { Icon( - painter = painterResource(item.iconRes!!), + painter = painterResource(item.iconRes), contentDescription = "Icon", tint = Color(0xFF007AFF), modifier = Modifier @@ -181,4 +175,4 @@ fun StyledSelectList( } } } -} \ No newline at end of file +} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledSlider.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledSlider.kt index 495b599..78829e2 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledSlider.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledSlider.kt @@ -18,6 +18,7 @@ package me.kavishdevar.librepods.composables +import android.annotation.SuppressLint import android.content.res.Configuration import android.util.Log import androidx.compose.animation.core.Animatable @@ -43,7 +44,6 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableFloatState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf @@ -81,7 +81,7 @@ import com.kyant.backdrop.backdrops.rememberCombinedBackdrop import com.kyant.backdrop.backdrops.rememberLayerBackdrop import com.kyant.backdrop.drawBackdrop import com.kyant.backdrop.effects.blur -import com.kyant.backdrop.effects.refractionWithDispersion +import com.kyant.backdrop.effects.lens import com.kyant.backdrop.highlight.Highlight import com.kyant.backdrop.shadow.InnerShadow import com.kyant.backdrop.shadow.Shadow @@ -203,10 +203,11 @@ class MomentumAnimation( } } +@SuppressLint("UnrememberedMutableState") @Composable fun StyledSlider( label: String? = null, - mutableFloatState: MutableFloatState, + value: Float, onValueChange: (Float) -> Unit, valueRange: ClosedFloatingPointRange, backdrop: Backdrop = rememberLayerBackdrop(), @@ -217,23 +218,26 @@ fun StyledSlider( startLabel: String? = null, endLabel: String? = null, independent: Boolean = false, - description: String? = null + description: String? = null, + enabled: Boolean = true ) { val backgroundColor = if (isSystemInDarkTheme()) Color(0xFF1C1C1E) else Color(0xFFFFFFFF) val isLightTheme = !isSystemInDarkTheme() - val accentColor = - if (isLightTheme) Color(0xFF0088FF) - else Color(0xFF0091FF) val trackColor = if (isLightTheme) Color(0xFF787878).copy(0.2f) else Color(0xFF787880).copy(0.36f) + val accentColor = + if (enabled) { + if (isLightTheme) Color(0xFF0088FF) + else Color(0xFF0091FF) + } else { + trackColor + } val labelTextColor = if (isLightTheme) Color.Black else Color.White - val fraction by remember { - derivedStateOf { - ((mutableFloatState.floatValue - valueRange.start) / (valueRange.endInclusive - valueRange.start)) - .fastCoerceIn(0f, 1f) - } + val fraction by derivedStateOf { + ((value - valueRange.start) / (valueRange.endInclusive - valueRange.start)) + .fastCoerceIn(0f, 1f) } val sliderBackdrop = rememberLayerBackdrop() @@ -427,71 +431,87 @@ fun StyledSlider( ) translationY = if (startLabel != null || endLabel != null) trackPositionState.floatValue + with(density) { 26.dp.toPx() } + size.height / 2f else trackPositionState.floatValue + with(density) { 8.dp.toPx() } } - .draggable( - rememberDraggableState { delta -> - val trackWidth = trackWidthState.floatValue - if (trackWidth > 0f) { - val targetFraction = fraction + delta / trackWidth - val targetValue = - lerp(valueRange.start, valueRange.endInclusive, targetFraction) - .fastCoerceIn(valueRange.start, valueRange.endInclusive) - val snappedValue = if (snapPoints.isNotEmpty()) snapIfClose( - targetValue, - snapPoints, - snapThreshold - ) else targetValue - onValueChange(snappedValue) - } - }, - Orientation.Horizontal, - startDragImmediately = true, - onDragStarted = { - // Remove this block as momentumAnimation handles pressing - }, - onDragStopped = { - // Remove this block as momentumAnimation handles pressing - onValueChange((mutableFloatState.floatValue * 100).roundToInt() / 100f) - } - ) - .then(momentumAnimation.modifier) - .drawBackdrop( - rememberCombinedBackdrop(backdrop, sliderBackdrop), - { RoundedCornerShape(28.dp) }, - highlight = { - val progress = momentumAnimation.progress - Highlight.Ambient.copy(alpha = progress) - }, - shadow = { - Shadow( - radius = 4f.dp, - color = Color.Black.copy(0.05f) - ) - }, - innerShadow = { - val progress = momentumAnimation.progress - InnerShadow( - radius = 4f.dp * progress, - alpha = progress - ) - }, - layerBlock = { - scaleX = momentumAnimation.scaleX - scaleY = momentumAnimation.scaleY - val velocity = momentumAnimation.velocity / 5000f - scaleX /= 1f - (velocity * 0.75f).fastCoerceIn(-0.15f, 0.15f) - scaleY *= 1f - (velocity * 0.25f).fastCoerceIn(-0.15f, 0.15f) - }, - onDrawSurface = { - val progress = momentumAnimation.progress - drawRect(Color.White.copy(alpha = 1f - progress)) - }, - effects = { - val progress = momentumAnimation.progress - blur(8f.dp.toPx() * (1f - progress)) - refractionWithDispersion( - height = 6f.dp.toPx() * progress, - amount = size.height / 2f * progress - ) + .then( + if (enabled) { + Modifier + .draggable( + rememberDraggableState { delta -> + val trackWidth = trackWidthState.floatValue + if (trackWidth > 0f) { + val targetFraction = fraction + delta / trackWidth + val targetValue = + lerp( + valueRange.start, + valueRange.endInclusive, + targetFraction + ) + .fastCoerceIn( + valueRange.start, + valueRange.endInclusive + ) + val snappedValue = if (snapPoints.isNotEmpty()) snapIfClose( + targetValue, + snapPoints, + snapThreshold + ) else targetValue + onValueChange(snappedValue) + } + }, + Orientation.Horizontal, + startDragImmediately = true, + onDragStarted = { + // Remove this block as momentumAnimation handles pressing + }, + onDragStopped = { + // Remove this block as momentumAnimation handles pressing + onValueChange((value * 100).roundToInt() / 100f) + } + ) + .then(momentumAnimation.modifier) + .drawBackdrop( + rememberCombinedBackdrop(backdrop, sliderBackdrop), + { RoundedCornerShape(28.dp) }, + highlight = { + val progress = momentumAnimation.progress + Highlight.Ambient.copy(alpha = progress) + }, + shadow = { + Shadow( + radius = 4f.dp, + color = Color.Black.copy(0.05f) + ) + }, + innerShadow = { + val progress = momentumAnimation.progress + InnerShadow( + radius = 4f.dp * progress, + alpha = progress + ) + }, + layerBlock = { + scaleX = momentumAnimation.scaleX + scaleY = momentumAnimation.scaleY + val velocity = momentumAnimation.velocity / 5000f + scaleX /= 1f - (velocity * 0.75f).fastCoerceIn(-0.15f, 0.15f) + scaleY *= 1f - (velocity * 0.25f).fastCoerceIn(-0.15f, 0.15f) + }, + onDrawSurface = { + val progress = momentumAnimation.progress + drawRect(Color.White.copy(alpha = 1f - progress)) + }, + effects = { + val progress = momentumAnimation.progress + blur(8f.dp.toPx() * (1f - progress)) + lens( + refractionHeight = 6f.dp.toPx() * progress, + refractionAmount = size.height / 2f * progress, + depthEffect = true, + chromaticAberration = true + ) + } + ) + } else { + Modifier.background(trackColor, RoundedCornerShape(28.dp)) } ) .size(40f.dp, 24f.dp) @@ -566,12 +586,13 @@ fun StyledSliderPreview() { .padding(16.dp) .fillMaxSize() ) { - Box ( - Modifier.align(Alignment.Center) + Column ( + Modifier.align(Alignment.Center), + verticalArrangement = Arrangement.spacedBy(16.dp) ) { StyledSlider( - mutableFloatState = a, + value = a.floatValue, onValueChange = { a.floatValue = it }, @@ -582,6 +603,19 @@ fun StyledSliderPreview() { startIcon = "A", endIcon = "B", ) + StyledSlider( + value = a.floatValue, + onValueChange = { + a.floatValue = it + }, + valueRange = 0f..2f, + snapPoints = listOf(1f), + snapThreshold = 0.1f, + independent = true, + startIcon = "A", + endIcon = "B", + enabled = false + ) } } } diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledSwitch.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledSwitch.kt index 0799281..7d8450b 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledSwitch.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledSwitch.kt @@ -19,7 +19,7 @@ package me.kavishdevar.librepods.composables import android.content.res.Configuration -import androidx.compose.animation.Animatable +import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.spring @@ -68,7 +68,7 @@ import com.kyant.backdrop.backdrops.layerBackdrop import com.kyant.backdrop.backdrops.rememberCombinedBackdrop import com.kyant.backdrop.backdrops.rememberLayerBackdrop import com.kyant.backdrop.drawBackdrop -import com.kyant.backdrop.effects.refractionWithDispersion +import com.kyant.backdrop.effects.lens import com.kyant.backdrop.highlight.Highlight import com.kyant.backdrop.shadow.Shadow import kotlinx.coroutines.coroutineScope @@ -100,22 +100,18 @@ fun StyledSwitch( val density = LocalDensity.current val animationScope = rememberCoroutineScope() val progressAnimationSpec = spring(0.5f, 300f, 0.001f) - val colorAnimationSpec = tween(200, easing = FastOutSlowInEasing) val progressAnimation = remember { Animatable(0f) } val innerShadowLayer = rememberGraphicsLayer().apply { compositingStrategy = CompositingStrategy.Offscreen } - val animatedTrackColor = remember { Animatable(if (checked) onColor else offColor) } + val targetColor = if (checked) onColor else offColor + val animatedTrackColor by animateColorAsState(targetColor) val totalDrag = remember { mutableFloatStateOf(0f) } val tapThreshold = 10f val isFirstComposition = remember { mutableStateOf(true) } LaunchedEffect(checked) { if (!isFirstComposition.value) { coroutineScope { - launch { - val targetColor = if (checked) onColor else offColor - animatedTrackColor.animateTo(targetColor, colorAnimationSpec) - } launch { val targetFrac = if (checked) 1f else 0f animatedFraction.animateTo(targetFrac, progressAnimationSpec) @@ -140,7 +136,7 @@ fun StyledSwitch( modifier = Modifier .layerBackdrop(switchBackdrop) .clip(RoundedCornerShape(trackHeight / 2)) - .background(animatedTrackColor.value) + .background(animatedTrackColor) .width(trackWidth) .height(trackHeight) .onSizeChanged { trackWidthPx.floatValue = it.width.toFloat() } @@ -262,7 +258,12 @@ fun StyledSwitch( drawRect(Color.White.copy(1f - progress)) }, effects = { - refractionWithDispersion(6f.dp.toPx(), size.height / 2f) + lens( + refractionHeight = 6f.dp.toPx(), + refractionAmount = size.height / 2f, + depthEffect = true, + chromaticAberration = true + ) } ) .width(thumbWidth) diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledToggle.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledToggle.kt index 4b578e7..b7453ae 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledToggle.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledToggle.kt @@ -20,8 +20,6 @@ package me.kavishdevar.librepods.composables -import android.content.SharedPreferences -import android.util.Log import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.tween import androidx.compose.foundation.background @@ -39,18 +37,15 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput -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 @@ -58,11 +53,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 androidx.core.content.edit import me.kavishdevar.librepods.R -import me.kavishdevar.librepods.services.ServiceManager -import me.kavishdevar.librepods.utils.AACPManager -import me.kavishdevar.librepods.utils.ATTHandles import kotlin.io.encoding.ExperimentalEncodingApi @Composable @@ -70,32 +61,27 @@ fun StyledToggle( title: String? = null, label: String, description: String? = null, - checkedState: MutableState = remember { mutableStateOf(false) } , - sharedPreferenceKey: String? = null, - sharedPreferences: SharedPreferences? = null, + checked: Boolean = false, independent: Boolean = true, enabled: Boolean = true, - onCheckedChange: ((Boolean) -> Unit)? = null, + onCheckedChange: (Boolean) -> Unit, ) { + val currentChecked by rememberUpdatedState(checked) + val isDarkTheme = isSystemInDarkTheme() val textColor = if (isDarkTheme) Color.White else Color.Black - var checked by checkedState - var backgroundColor by remember { mutableStateOf(if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)) } - val animatedBackgroundColor by animateColorAsState(targetValue = backgroundColor, animationSpec = tween(durationMillis = 500)) - if (sharedPreferenceKey != null && sharedPreferences != null) { - checked = sharedPreferences.getBoolean(sharedPreferenceKey, checked) - } - fun cb() { - if (sharedPreferences != null) { - if (sharedPreferenceKey == null) { - Log.e("StyledToggle", "SharedPreferenceKey is null but SharedPreferences is provided.") - return - } - sharedPreferences.edit { putBoolean(sharedPreferenceKey, checked) } - } - onCheckedChange?.invoke(checked) + + var backgroundColor by remember { + mutableStateOf( + if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF) + ) } + val animatedBackgroundColor by animateColorAsState( + targetValue = backgroundColor, + animationSpec = tween(durationMillis = 500) + ) + if (independent) { Column(modifier = Modifier.padding(vertical = 8.dp)) { if (title != null) { @@ -106,9 +92,15 @@ fun StyledToggle( fontWeight = FontWeight.Bold, color = textColor.copy(alpha = 0.6f) ), - modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 4.dp) + modifier = Modifier.padding( + start = 16.dp, + end = 16.dp, + top = 8.dp, + bottom = 4.dp + ) ) } + Box( modifier = Modifier .background(animatedBackgroundColor, RoundedCornerShape(28.dp)) @@ -124,8 +116,7 @@ fun StyledToggle( }, onTap = { if (enabled) { - checked = !checked - cb() + onCheckedChange(!currentChecked) } } ) @@ -148,24 +139,29 @@ fun StyledToggle( color = textColor ) ) + StyledSwitch( checked = checked, enabled = enabled, onCheckedChange = { if (enabled) { - checked = it - cb() + onCheckedChange(it) } } ) } } + if (description != null) { Spacer(modifier = Modifier.height(8.dp)) + Box( modifier = Modifier .padding(horizontal = 16.dp) - .background(if (isDarkTheme) Color(0xFF000000) else Color(0xFFF2F2F7)) + .background( + if (isDarkTheme) Color(0xFF000000) + else Color(0xFFF2F2F7) + ) ) { Text( text = description, @@ -181,6 +177,7 @@ fun StyledToggle( } } else { val isPressed = remember { mutableStateOf(false) } + Row( modifier = Modifier .fillMaxWidth() @@ -203,8 +200,7 @@ fun StyledToggle( interactionSource = remember { MutableInteractionSource() } ) { if (enabled) { - checked = !checked - cb() + onCheckedChange(!currentChecked) } }, verticalAlignment = Alignment.CenterVertically @@ -223,7 +219,9 @@ fun StyledToggle( color = textColor ) ) + Spacer(modifier = Modifier.height(4.dp)) + if (description != null) { Text( text = description, @@ -235,438 +233,13 @@ fun StyledToggle( ) } } + StyledSwitch( checked = checked, enabled = enabled, onCheckedChange = { if (enabled) { - checked = it - cb() - } - } - ) - } - } -} - -@Composable -fun StyledToggle( - title: String? = null, - label: String, - description: String? = null, - controlCommandIdentifier: AACPManager.Companion.ControlCommandIdentifiers, - independent: Boolean = true, - enabled: Boolean = true, - sharedPreferenceKey: String? = null, - sharedPreferences: SharedPreferences? = null, - onCheckedChange: ((Boolean) -> Unit)? = null, -) { - val service = ServiceManager.getService() ?: return - val isDarkTheme = isSystemInDarkTheme() - val textColor = if (isDarkTheme) Color.White else Color.Black - val checkedValue = service.aacpManager.controlCommandStatusList.find { - it.identifier == controlCommandIdentifier - }?.value?.takeIf { it.isNotEmpty() }?.get(0) - var checked by remember { mutableStateOf(checkedValue == 1.toByte()) } - var backgroundColor by remember { mutableStateOf(if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)) } - val animatedBackgroundColor by animateColorAsState(targetValue = backgroundColor, animationSpec = tween(durationMillis = 500)) - if (sharedPreferenceKey != null && sharedPreferences != null) { - checked = sharedPreferences.getBoolean(sharedPreferenceKey, checked) - } - fun cb() { - service.aacpManager.sendControlCommand(identifier = controlCommandIdentifier.value, value = checked) - if (sharedPreferences != null) { - if (sharedPreferenceKey == null) { - Log.e("StyledToggle", "SharedPreferenceKey is null but SharedPreferences is provided.") - return - } - sharedPreferences.edit { putBoolean(sharedPreferenceKey, checked) } - } - onCheckedChange?.invoke(checked) - } - - val listener = remember { - object : AACPManager.ControlCommandListener { - override fun onControlCommandReceived(controlCommand: AACPManager.ControlCommand) { - if (controlCommand.identifier == controlCommandIdentifier.value) { - Log.d("StyledToggle", "Received control command for $label: ${controlCommand.value}") - checked = controlCommand.value.takeIf { it.isNotEmpty() }?.get(0) == 1.toByte() - } - } - } - } - LaunchedEffect(Unit) { - service.aacpManager.registerControlCommandListener(controlCommandIdentifier, listener) - } - DisposableEffect(Unit) { - onDispose { - service.aacpManager.unregisterControlCommandListener(controlCommandIdentifier, listener) - } - } - - if (independent) { - Column(modifier = Modifier.padding(vertical = 8.dp)) { - if (title != null) { - Text( - text = title, - style = TextStyle( - fontSize = 14.sp, - fontWeight = FontWeight.Bold, - color = textColor.copy(alpha = 0.6f) - ), - modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 4.dp) - ) - } - Box( - modifier = Modifier - .background(animatedBackgroundColor, RoundedCornerShape(28.dp)) - .padding(4.dp) - .pointerInput(Unit) { - detectTapGestures( - onPress = { - backgroundColor = - if (isDarkTheme) Color(0x40888888) else Color(0x40D9D9D9) - tryAwaitRelease() - backgroundColor = - if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF) - }, - onTap = { - if (enabled) { - checked = !checked - cb() - } - } - ) - } - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .height(55.dp) - .padding(horizontal = 12.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Text( - text = label, - modifier = Modifier.weight(1f), - style = TextStyle( - fontSize = 16.sp, - fontFamily = FontFamily(Font(R.font.sf_pro)), - fontWeight = FontWeight.Normal, - color = textColor - ) - ) - StyledSwitch( - checked = checked, - enabled = enabled, - onCheckedChange = { - if (enabled) { - checked = it - cb() - } - } - ) - } - } - if (description != null) { - Spacer(modifier = Modifier.height(8.dp)) - Box( - modifier = Modifier - .padding(horizontal = 16.dp) - .background(if (isDarkTheme) Color(0xFF000000) else Color(0xFFF2F2F7)) - ) { - Text( - text = description, - style = TextStyle( - fontSize = 12.sp, - fontWeight = FontWeight.Light, - color = textColor.copy(alpha = 0.6f), - fontFamily = FontFamily(Font(R.font.sf_pro)) - ) - ) - } - } - } - } else { - val isPressed = remember { mutableStateOf(false) } - Row( - modifier = Modifier - .fillMaxWidth() - .background( - shape = RoundedCornerShape(28.dp), - color = if (isPressed.value) Color(0xFFE0E0E0) else Color.Transparent - ) - .padding(16.dp) - .pointerInput(Unit) { - detectTapGestures( - onPress = { - isPressed.value = true - tryAwaitRelease() - isPressed.value = false - } - ) - } - .clickable( - indication = null, - interactionSource = remember { MutableInteractionSource() } - ) { - if (enabled) { - checked = !checked - cb() - } - }, - verticalAlignment = Alignment.CenterVertically - ) { - Column( - modifier = Modifier - .weight(1f) - .padding(end = 4.dp) - ) { - Text( - text = label, - style = TextStyle( - fontSize = 16.sp, - fontFamily = FontFamily(Font(R.font.sf_pro)), - fontWeight = FontWeight.Normal, - color = textColor - ) - ) - Spacer(modifier = Modifier.height(4.dp)) - if (description != null) { - Text( - text = description, - style = TextStyle( - fontSize = 12.sp, - color = textColor.copy(0.6f), - fontFamily = FontFamily(Font(R.font.sf_pro)), - ) - ) - } - } - StyledSwitch( - checked = checked, - enabled = enabled, - onCheckedChange = { - if (enabled) { - checked = it - cb() - } - } - ) - } - } -} - -@Composable -fun StyledToggle( - title: String? = null, - label: String, - description: String? = null, - attHandle: ATTHandles, - independent: Boolean = true, - enabled: Boolean = true, - sharedPreferenceKey: String? = null, - sharedPreferences: SharedPreferences? = null, - onCheckedChange: ((Boolean) -> Unit)? = null, -) { - val attManager = ServiceManager.getService()?.attManager ?: return - val isDarkTheme = isSystemInDarkTheme() - val textColor = if (isDarkTheme) Color.White else Color.Black - val checkedValue = try { - attManager.read(attHandle).getOrNull(0)?.toInt() - } catch (e: Exception) { - Log.w("StyledToggle", "Error reading initial value for $label: ${e.message}") - null - } ?: 0 - var checked by remember { mutableStateOf(checkedValue !=0) } - var backgroundColor by remember { mutableStateOf(if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)) } - val animatedBackgroundColor by animateColorAsState(targetValue = backgroundColor, animationSpec = tween(durationMillis = 500)) - - attManager.enableNotifications(attHandle) - - if (sharedPreferenceKey != null && sharedPreferences != null) { - checked = sharedPreferences.getBoolean(sharedPreferenceKey, checked) - } - - fun cb() { - if (sharedPreferences != null) { - if (sharedPreferenceKey == null) { - Log.e("StyledToggle", "SharedPreferenceKey is null but SharedPreferences is provided.") - return - } - sharedPreferences.edit { putBoolean(sharedPreferenceKey, checked) } - } - onCheckedChange?.invoke(checked) - } - - LaunchedEffect(checked) { - if (attManager.socket?.isConnected != true) return@LaunchedEffect - attManager.write(attHandle, if (checked) byteArrayOf(1) else byteArrayOf(0)) - } - - val listener = remember { - object : (ByteArray) -> Unit { - override fun invoke(value: ByteArray) { - if (value.isNotEmpty()) { - checked = value[0].toInt() != 0 - Log.d("StyledToggle", "Updated from notification for $label: enabled=$checked") - } else { - Log.w("StyledToggle", "Empty value in notification for $label") - } - } - } - } - - LaunchedEffect(Unit) { - attManager.registerListener(attHandle, listener) - } - - DisposableEffect(Unit) { - onDispose { - attManager.unregisterListener(attHandle, listener) - } - } - - if (independent) { - Column(modifier = Modifier.padding(vertical = 8.dp)) { - if (title != null) { - Text( - text = title, - style = TextStyle( - fontSize = 14.sp, - fontWeight = FontWeight.Bold, - color = textColor.copy(alpha = 0.6f) - ), - modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 4.dp) - ) - } - Box( - modifier = Modifier - .background(animatedBackgroundColor, RoundedCornerShape(28.dp)) - .padding(4.dp) - .pointerInput(Unit) { - detectTapGestures( - onPress = { - backgroundColor = - if (isDarkTheme) Color(0x40888888) else Color(0x40D9D9D9) - tryAwaitRelease() - backgroundColor = - if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF) - }, - onTap = { - if (enabled) { - checked = !checked - cb() - } - } - ) - } - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .height(55.dp) - .padding(horizontal = 12.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Text( - text = label, - modifier = Modifier.weight(1f), - style = TextStyle( - fontSize = 16.sp, - fontFamily = FontFamily(Font(R.font.sf_pro)), - fontWeight = FontWeight.Normal, - color = textColor - ) - ) - StyledSwitch( - checked = checked, - enabled = enabled, - onCheckedChange = { - if (enabled) { - checked = it - cb() - } - } - ) - } - } - if (description != null) { - Spacer(modifier = Modifier.height(8.dp)) - Box( - modifier = Modifier - .padding(horizontal = 16.dp) - .background(if (isDarkTheme) Color(0xFF000000) else Color(0xFFF2F2F7)) - ) { - Text( - text = description, - style = TextStyle( - fontSize = 12.sp, - fontWeight = FontWeight.Light, - color = textColor.copy(alpha = 0.6f), - fontFamily = FontFamily(Font(R.font.sf_pro)) - ) - ) - } - } - } - } else { - val isPressed = remember { mutableStateOf(false) } - Row( - modifier = Modifier - .fillMaxWidth() - .background( - shape = RoundedCornerShape(28.dp), - color = if (isPressed.value) Color(0xFFE0E0E0) else Color.Transparent - ) - .padding(16.dp) - .pointerInput(Unit) { - detectTapGestures( - onPress = { - isPressed.value = true - tryAwaitRelease() - isPressed.value = false - } - ) - } - .clickable( - indication = null, - interactionSource = remember { MutableInteractionSource() } - ) { - if (enabled) { - checked = !checked - cb() - } - }, - verticalAlignment = Alignment.CenterVertically - ) { - Column( - modifier = Modifier - .weight(1f) - .padding(end = 4.dp) - ) { - Text( - text = label, - fontSize = 16.sp, - color = textColor - ) - Spacer(modifier = Modifier.height(4.dp)) - if (description != null) { - Text( - text = description, - fontSize = 12.sp, - color = textColor.copy(0.6f), - lineHeight = 14.sp, - ) - } - } - StyledSwitch( - checked = checked, - enabled = enabled, - onCheckedChange = { - if (enabled) { - checked = it - cb() + onCheckedChange(it) } } ) @@ -677,11 +250,11 @@ fun StyledToggle( @Preview @Composable fun StyledTogglePreview() { - val context = LocalContext.current - val sharedPrefs = context.getSharedPreferences("preview", 0) + val checked = remember { mutableStateOf(false) } StyledToggle( label = "Example Toggle", description = "This is an example description for the styled toggle.", - sharedPreferences = sharedPrefs + checked = checked.value, + onCheckedChange = { checked.value = !checked.value } ) } diff --git a/android/app/src/main/java/me/kavishdevar/librepods/constants/Packets.kt b/android/app/src/main/java/me/kavishdevar/librepods/constants/Packets.kt index b4487aa..1c179d0 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/constants/Packets.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/constants/Packets.kt @@ -80,6 +80,8 @@ class AirPodsNotifications { const val AIRPODS_DISCONNECTED = "me.kavishdevar.librepods.AIRPODS_DISCONNECTED" const val AIRPODS_CONNECTION_DETECTED = "me.kavishdevar.librepods.AIRPODS_CONNECTION_DETECTED" const val DISCONNECT_RECEIVERS = "me.kavishdevar.librepods.DISCONNECT_RECEIVERS" + const val EQ_DATA = "me.kavishdevar.librepods.EQ_DATA" + const val AIRPODS_INFORMATION_UPDATED = "me.kavishdevar.librepods.AIRPODS_INFORMATION_UPDATED" } class EarDetection { diff --git a/android/app/src/main/java/me/kavishdevar/librepods/data/ControlCommandRepository.kt b/android/app/src/main/java/me/kavishdevar/librepods/data/ControlCommandRepository.kt new file mode 100644 index 0000000..3a0727f --- /dev/null +++ b/android/app/src/main/java/me/kavishdevar/librepods/data/ControlCommandRepository.kt @@ -0,0 +1,63 @@ +/* + LibrePods - AirPods liberated from Apple’s ecosystem + Copyright (C) 2025 LibrePods contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package me.kavishdevar.librepods.data + +import me.kavishdevar.librepods.utils.AACPManager + +class ControlCommandRepository( + private val aacpManager: AACPManager +) { + fun getValue( + identifier: AACPManager.Companion.ControlCommandIdentifiers + ): ByteArray? { + return aacpManager.controlCommandStatusList + .find { it.identifier == identifier } + ?.value + } + + fun setValue( + id: AACPManager.Companion.ControlCommandIdentifiers, + value: ByteArray + ) { + aacpManager.sendControlCommand(id.value, value) + } + + + fun observe( + identifier: AACPManager.Companion.ControlCommandIdentifiers, + onChange: (ByteArray) -> Unit + ): AACPManager.ControlCommandListener { + + val listener = object : AACPManager.ControlCommandListener { + override fun onControlCommandReceived(controlCommand: AACPManager.ControlCommand) { + onChange(controlCommand.value) + } + } + + aacpManager.registerControlCommandListener(identifier, listener) + return listener + } + + fun remove( + identifier: AACPManager.Companion.ControlCommandIdentifiers, + listener: AACPManager.ControlCommandListener + ) { + aacpManager.unregisterControlCommandListener(identifier, listener) + } +} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/screens/AccessibilitySettingsScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/AccessibilitySettingsScreen.kt index 8ded7d6..2bdccdf 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/screens/AccessibilitySettingsScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/AccessibilitySettingsScreen.kt @@ -18,8 +18,8 @@ package me.kavishdevar.librepods.screens +// import me.kavishdevar.librepods.utils.RadareOffsetFinder import android.annotation.SuppressLint -import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress import androidx.compose.foundation.gestures.detectTapGestures @@ -39,10 +39,8 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -69,74 +67,35 @@ import com.kyant.backdrop.backdrops.rememberLayerBackdrop import dev.chrisbanes.haze.HazeState import dev.chrisbanes.haze.hazeSource import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch +import me.kavishdevar.librepods.BuildConfig import me.kavishdevar.librepods.R import me.kavishdevar.librepods.composables.NavigationButton import me.kavishdevar.librepods.composables.StyledDropdown import me.kavishdevar.librepods.composables.StyledScaffold import me.kavishdevar.librepods.composables.StyledSlider import me.kavishdevar.librepods.composables.StyledToggle -import me.kavishdevar.librepods.services.ServiceManager import me.kavishdevar.librepods.utils.AACPManager import me.kavishdevar.librepods.utils.ATTHandles import me.kavishdevar.librepods.utils.Capability -// import me.kavishdevar.librepods.utils.RadareOffsetFinder +import me.kavishdevar.librepods.viewmodel.AirPodsViewModel import kotlin.io.encoding.ExperimentalEncodingApi -private var phoneMediaDebounceJob: Job? = null -private var toneVolumeDebounceJob: Job? = null -private const val TAG = "AccessibilitySettings" +//private var phoneMediaDebounceJob: Job? = null +//private var toneVolumeDebounceJob: Job? = null +//private const val TAG = "AccessibilitySettings" @SuppressLint("DefaultLocale") @ExperimentalHazeMaterialsApi @OptIn(ExperimentalMaterial3Api::class, ExperimentalEncodingApi::class) @Composable -fun AccessibilitySettingsScreen(navController: NavController) { +fun AccessibilitySettingsScreen(viewModel: AirPodsViewModel, navController: NavController) { + val state by viewModel.uiState.collectAsState() + val isDarkTheme = isSystemInDarkTheme() val textColor = if (isDarkTheme) Color.White else Color.Black - val aacpManager = remember { ServiceManager.getService()?.aacpManager } - val isSdpOffsetAvailable = remember { mutableStateOf(false) } // always available rn, for testing without radare -// remember { mutableStateOf(RadareOffsetFinder.isSdpOffsetAvailable()) } - val trackColor = if (isDarkTheme) Color(0xFFB3B3B3) else Color(0xFF929491) - val activeTrackColor = if (isDarkTheme) Color(0xFF007AFF) else Color(0xFF3C6DF5) - val thumbColor = if (isDarkTheme) Color(0xFFFFFFFF) else Color(0xFFFFFFFF) + val hearingAidEnabled = state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID]?.getOrNull(1)?.toInt() == 1 && state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID]?.getOrNull(0)?.toInt() == 1 - val capabilities = remember { ServiceManager.getService()?.airpodsInstance?.model?.capabilities ?: emptySet() } - - val hearingAidEnabled = remember { mutableStateOf( - aacpManager?.controlCommandStatusList?.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID }?.value?.getOrNull(1) == 0x01.toByte() && - aacpManager.controlCommandStatusList.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG }?.value?.getOrNull(0) == 0x01.toByte() - ) } - - val hearingAidListener = remember { - object : AACPManager.ControlCommandListener { - override fun onControlCommandReceived(controlCommand: AACPManager.ControlCommand) { - if (controlCommand.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID.value || - controlCommand.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG.value) { - val aidStatus = aacpManager?.controlCommandStatusList?.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID } - val assistStatus = aacpManager?.controlCommandStatusList?.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG } - hearingAidEnabled.value = (aidStatus?.value?.getOrNull(1) == 0x01.toByte()) && (assistStatus?.value?.getOrNull(0) == 0x01.toByte()) - } - } - } - } - - LaunchedEffect(Unit) { - aacpManager?.registerControlCommandListener(AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID, hearingAidListener) - aacpManager?.registerControlCommandListener(AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG, hearingAidListener) - } - - DisposableEffect(Unit) { - onDispose { - aacpManager?.unregisterControlCommandListener(AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID, hearingAidListener) - aacpManager?.unregisterControlCommandListener(AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG, hearingAidListener) - } - } val backdrop = rememberLayerBackdrop() @@ -153,170 +112,73 @@ fun AccessibilitySettingsScreen(navController: NavController) { verticalArrangement = Arrangement.spacedBy(16.dp) ) { Spacer(modifier = Modifier.height(spacerHeight)) - val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF) - val phoneMediaEQ = remember { mutableStateOf(FloatArray(8) { 0.5f }) } - val phoneEQEnabled = remember { mutableStateOf(false) } - val mediaEQEnabled = remember { mutableStateOf(false) } +// val phoneMediaEQ = remember { mutableStateOf(FloatArray(8) { 0.5f }) } +// val phoneEQEnabled = remember { mutableStateOf(false) } +// val mediaEQEnabled = remember { mutableStateOf(false) } val pressSpeedOptions = mapOf( 0.toByte() to stringResource(R.string.default_option), 1.toByte() to stringResource(R.string.slower), 2.toByte() to stringResource(R.string.slowest) ) - val selectedPressSpeedValue = - aacpManager?.controlCommandStatusList?.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.DOUBLE_CLICK_INTERVAL }?.value?.takeIf { it.isNotEmpty() } - ?.get(0) + + val selectedPressSpeedValue = state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.DOUBLE_CLICK_INTERVAL]?.getOrNull(0) var selectedPressSpeed by remember { mutableStateOf( pressSpeedOptions[selectedPressSpeedValue] ?: pressSpeedOptions[0] ) } - val selectedPressSpeedListener = object : AACPManager.ControlCommandListener { - override fun onControlCommandReceived(controlCommand: AACPManager.ControlCommand) { - if (controlCommand.identifier == AACPManager.Companion.ControlCommandIdentifiers.DOUBLE_CLICK_INTERVAL.value) { - val newValue = controlCommand.value.takeIf { it.isNotEmpty() }?.get(0) - selectedPressSpeed = pressSpeedOptions[newValue] ?: pressSpeedOptions[0] - } - } - } - LaunchedEffect(Unit) { - aacpManager?.registerControlCommandListener( - AACPManager.Companion.ControlCommandIdentifiers.DOUBLE_CLICK_INTERVAL, - selectedPressSpeedListener - ) - } - DisposableEffect(Unit) { - onDispose { - aacpManager?.unregisterControlCommandListener( - AACPManager.Companion.ControlCommandIdentifiers.DOUBLE_CLICK_INTERVAL, - selectedPressSpeedListener - ) - } - } val pressAndHoldDurationOptions = mapOf( 0.toByte() to stringResource(R.string.default_option), 1.toByte() to stringResource(R.string.slower), 2.toByte() to stringResource(R.string.slowest) ) - val selectedPressAndHoldDurationValue = - aacpManager?.controlCommandStatusList?.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.CLICK_HOLD_INTERVAL }?.value?.takeIf { it.isNotEmpty() } - ?.get(0) + + val selectedPressAndHoldDurationValue = state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.CLICK_HOLD_INTERVAL]?.getOrNull(0) var selectedPressAndHoldDuration by remember { mutableStateOf( pressAndHoldDurationOptions[selectedPressAndHoldDurationValue] ?: pressAndHoldDurationOptions[0] ) } - val selectedPressAndHoldDurationListener = object : AACPManager.ControlCommandListener { - override fun onControlCommandReceived(controlCommand: AACPManager.ControlCommand) { - if (controlCommand.identifier == AACPManager.Companion.ControlCommandIdentifiers.CLICK_HOLD_INTERVAL.value) { - val newValue = controlCommand.value.takeIf { it.isNotEmpty() }?.get(0) - selectedPressAndHoldDuration = - pressAndHoldDurationOptions[newValue] ?: pressAndHoldDurationOptions[0] - } - } - } - LaunchedEffect(Unit) { - aacpManager?.registerControlCommandListener( - AACPManager.Companion.ControlCommandIdentifiers.CLICK_HOLD_INTERVAL, - selectedPressAndHoldDurationListener - ) - } - DisposableEffect(Unit) { - onDispose { - aacpManager?.unregisterControlCommandListener( - AACPManager.Companion.ControlCommandIdentifiers.CLICK_HOLD_INTERVAL, - selectedPressAndHoldDurationListener - ) - } - } val volumeSwipeSpeedOptions = mapOf( 1.toByte() to stringResource(R.string.default_option), 2.toByte() to stringResource(R.string.longer), 3.toByte() to stringResource(R.string.longest) ) - val selectedVolumeSwipeSpeedValue = - aacpManager?.controlCommandStatusList?.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.VOLUME_SWIPE_INTERVAL }?.value?.takeIf { it.isNotEmpty() } - ?.get(0) + val selectedVolumeSwipeSpeedValue = state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.VOLUME_SWIPE_INTERVAL]?.getOrNull(0) var selectedVolumeSwipeSpeed by remember { mutableStateOf( volumeSwipeSpeedOptions[selectedVolumeSwipeSpeedValue] ?: volumeSwipeSpeedOptions[1] ) } - val selectedVolumeSwipeSpeedListener = object : AACPManager.ControlCommandListener { - override fun onControlCommandReceived(controlCommand: AACPManager.ControlCommand) { - if (controlCommand.identifier == AACPManager.Companion.ControlCommandIdentifiers.VOLUME_SWIPE_INTERVAL.value) { - val newValue = controlCommand.value.takeIf { it.isNotEmpty() }?.get(0) - selectedVolumeSwipeSpeed = - volumeSwipeSpeedOptions[newValue] ?: volumeSwipeSpeedOptions[1] - } - } - } - LaunchedEffect(Unit) { - aacpManager?.registerControlCommandListener( - AACPManager.Companion.ControlCommandIdentifiers.VOLUME_SWIPE_INTERVAL, - selectedVolumeSwipeSpeedListener - ) - } - DisposableEffect(Unit) { - onDispose { - aacpManager?.unregisterControlCommandListener( - AACPManager.Companion.ControlCommandIdentifiers.VOLUME_SWIPE_INTERVAL, - selectedVolumeSwipeSpeedListener - ) - } - } - LaunchedEffect(phoneMediaEQ.value, phoneEQEnabled.value, mediaEQEnabled.value) { - phoneMediaDebounceJob?.cancel() - phoneMediaDebounceJob = CoroutineScope(Dispatchers.IO).launch { - delay(150) - val manager = ServiceManager.getService()?.aacpManager - if (manager == null) { - Log.w(TAG, "Cannot write EQ: AACPManager not available") - return@launch - } - try { - val phoneByte = if (phoneEQEnabled.value) 0x01.toByte() else 0x02.toByte() - val mediaByte = if (mediaEQEnabled.value) 0x01.toByte() else 0x02.toByte() - Log.d( - TAG, - "Sending phone/media EQ (phoneEnabled=${phoneEQEnabled.value}, mediaEnabled=${mediaEQEnabled.value})" - ) - manager.sendPhoneMediaEQ(phoneMediaEQ.value, phoneByte, mediaByte) - } catch (e: Exception) { - Log.w(TAG, "Error sending phone/media EQ: ${e.message}") - } - } - } - val toneVolumeValue = remember { mutableFloatStateOf( - aacpManager?.controlCommandStatusList?.find { - it.identifier == AACPManager.Companion.ControlCommandIdentifiers.CHIME_VOLUME - }?.value?.takeIf { it.isNotEmpty() }?.get(0)?.toFloat() ?: 75f - ) } - LaunchedEffect(toneVolumeValue.floatValue) { - toneVolumeDebounceJob?.cancel() - toneVolumeDebounceJob = CoroutineScope(Dispatchers.IO).launch { - delay(150) - val manager = ServiceManager.getService()?.aacpManager - if (manager == null) { - Log.w(TAG, "Cannot write tone volume: AACPManager not available") - return@launch - } - try { - manager.sendControlCommand( - identifier = AACPManager.Companion.ControlCommandIdentifiers.CHIME_VOLUME.value, - value = byteArrayOf(toneVolumeValue.floatValue.toInt().toByte(), 0x50.toByte()) - ) - } catch (e: Exception) { - Log.w(TAG, "Error sending tone volume: ${e.message}") - } - } - } +// LaunchedEffect(phoneMediaEQ.value, phoneEQEnabled.value, mediaEQEnabled.value) { +// phoneMediaDebounceJob?.cancel() +// phoneMediaDebounceJob = CoroutineScope(Dispatchers.IO).launch { +// delay(150) +// val manager = ServiceManager.getService()?.aacpManager +// if (manager == null) { +// Log.w(TAG, "Cannot write EQ: AACPManager not available") +// return@launch +// } +// try { +// val phoneByte = if (phoneEQEnabled.value) 0x01.toByte() else 0x02.toByte() +// val mediaByte = if (mediaEQEnabled.value) 0x01.toByte() else 0x02.toByte() +// Log.d( +// TAG, +// "Sending phone/media EQ (phoneEnabled=${phoneEQEnabled.value}, mediaEnabled=${mediaEQEnabled.value})" +// ) +// manager.sendPhoneMediaEQ(phoneMediaEQ.value, phoneByte, mediaByte) +// } catch (e: Exception) { +// Log.w(TAG, "Error sending phone/media EQ: ${e.message}") +// } +// } +// } DropdownMenuComponent( label = stringResource(R.string.press_speed), @@ -325,8 +187,8 @@ fun AccessibilitySettingsScreen(navController: NavController) { selectedOption = selectedPressSpeed?: stringResource(R.string.default_option), onOptionSelected = { newValue -> selectedPressSpeed = newValue - aacpManager?.sendControlCommand( - identifier = AACPManager.Companion.ControlCommandIdentifiers.DOUBLE_CLICK_INTERVAL.value, + viewModel.setControlCommandByte( + identifier = AACPManager.Companion.ControlCommandIdentifiers.DOUBLE_CLICK_INTERVAL, value = pressSpeedOptions.filterValues { it == newValue }.keys.firstOrNull() ?: 0.toByte() ) @@ -343,8 +205,8 @@ fun AccessibilitySettingsScreen(navController: NavController) { selectedOption = selectedPressAndHoldDuration?: stringResource(R.string.default_option), onOptionSelected = { newValue -> selectedPressAndHoldDuration = newValue - aacpManager?.sendControlCommand( - identifier = AACPManager.Companion.ControlCommandIdentifiers.CLICK_HOLD_INTERVAL.value, + viewModel.setControlCommandByte( + identifier = AACPManager.Companion.ControlCommandIdentifiers.CLICK_HOLD_INTERVAL, value = pressAndHoldDurationOptions.filterValues { it == newValue }.keys.firstOrNull() ?: 0.toByte() ) @@ -358,19 +220,21 @@ fun AccessibilitySettingsScreen(navController: NavController) { title = stringResource(R.string.noise_control), label = stringResource(R.string.noise_cancellation_single_airpod), description = stringResource(R.string.noise_cancellation_single_airpod_description), - controlCommandIdentifier = AACPManager.Companion.ControlCommandIdentifiers.ONE_BUD_ANC_MODE, independent = true, + checked = state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.ONE_BUD_ANC_MODE]?.getOrNull(0) == 0x01.toByte(), + onCheckedChange = { viewModel.setControlCommandBoolean(AACPManager.Companion.ControlCommandIdentifiers.ONE_BUD_ANC_MODE, it) } ) - if (capabilities.contains(Capability.LOUD_SOUND_REDUCTION)) { + if (state.capabilities.contains(Capability.LOUD_SOUND_REDUCTION) && BuildConfig.FLAVOR == "xposed") { StyledToggle( label = stringResource(R.string.loud_sound_reduction), description = stringResource(R.string.loud_sound_reduction_description), - attHandle = ATTHandles.LOUD_SOUND_REDUCTION + checked = viewModel.getATTCharacteristicValue(ATTHandles.LOUD_SOUND_REDUCTION)?.get(0) == 1.toByte(), + onCheckedChange = { viewModel.setATTCharacteristicValue(ATTHandles.LOUD_SOUND_REDUCTION, if (it) byteArrayOf(0x01) else byteArrayOf(0x00)) } ) } - if (!hearingAidEnabled.value&& isSdpOffsetAvailable.value) { + if (!hearingAidEnabled && BuildConfig.FLAVOR == "xposed") { NavigationButton( to = "transparency_customization", name = stringResource(R.string.customize_transparency_mode), @@ -378,12 +242,13 @@ fun AccessibilitySettingsScreen(navController: NavController) { ) } + val toneVolumeValue = state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.CHIME_VOLUME]?.getOrNull(0)?.toFloat() ?: 75f StyledSlider( label = stringResource(R.string.tone_volume), description = stringResource(R.string.tone_volume_description), - mutableFloatState = toneVolumeValue, + value = toneVolumeValue, onValueChange = { - toneVolumeValue.floatValue = it + viewModel.setControlCommandValue(AACPManager.Companion.ControlCommandIdentifiers.CHIME_VOLUME, byteArrayOf(it.toInt().toByte(), 0x50)) }, valueRange = 0f..100f, snapPoints = listOf(75f), @@ -392,11 +257,13 @@ fun AccessibilitySettingsScreen(navController: NavController) { independent = true ) - if (capabilities.contains(Capability.SWIPE_FOR_VOLUME)) { + if (state.capabilities.contains(Capability.SWIPE_FOR_VOLUME)) { + val volumeSwipeEnabled = state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.VOLUME_SWIPE_MODE]?.getOrNull(0)?.toInt() == 0x01 StyledToggle( label = stringResource(R.string.volume_control), description = stringResource(R.string.volume_control_description), - controlCommandIdentifier = AACPManager.Companion.ControlCommandIdentifiers.VOLUME_SWIPE_MODE, + checked = volumeSwipeEnabled, + onCheckedChange = { viewModel.setControlCommandBoolean(AACPManager.Companion.ControlCommandIdentifiers.VOLUME_SWIPE_MODE, it) } ) DropdownMenuComponent( @@ -406,8 +273,8 @@ fun AccessibilitySettingsScreen(navController: NavController) { selectedOption = selectedVolumeSwipeSpeed?: stringResource(R.string.default_option), onOptionSelected = { newValue -> selectedVolumeSwipeSpeed = newValue - aacpManager?.sendControlCommand( - identifier = AACPManager.Companion.ControlCommandIdentifiers.VOLUME_SWIPE_INTERVAL.value, + viewModel.setControlCommandByte( + identifier = AACPManager.Companion.ControlCommandIdentifiers.VOLUME_SWIPE_INTERVAL, value = volumeSwipeSpeedOptions.filterValues { it == newValue }.keys.firstOrNull() ?: 1.toByte() ) @@ -418,7 +285,7 @@ fun AccessibilitySettingsScreen(navController: NavController) { ) } - if (!hearingAidEnabled.value&& isSdpOffsetAvailable.value) { +// if (!hearingAidEnabled.value&& BuildConfig.FLAVOR == "xposed") { // Text( // text = stringResource(R.string.apply_eq_to), // style = TextStyle( @@ -640,7 +507,7 @@ fun AccessibilitySettingsScreen(navController: NavController) { // } // } // } - } +// } } } } diff --git a/android/app/src/main/java/me/kavishdevar/librepods/screens/AdaptiveStrengthScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/AdaptiveStrengthScreen.kt index 151be9c..677f1b6 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/screens/AdaptiveStrengthScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/AdaptiveStrengthScreen.kt @@ -18,84 +18,37 @@ package me.kavishdevar.librepods.screens -import android.annotation.SuppressLint -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.navigation.NavController import com.kyant.backdrop.backdrops.layerBackdrop import com.kyant.backdrop.backdrops.rememberLayerBackdrop -import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch import me.kavishdevar.librepods.R -import me.kavishdevar.librepods.composables.StyledIconButton import me.kavishdevar.librepods.composables.StyledScaffold import me.kavishdevar.librepods.composables.StyledSlider -import me.kavishdevar.librepods.services.ServiceManager import me.kavishdevar.librepods.utils.AACPManager -import kotlin.io.encoding.ExperimentalEncodingApi +import me.kavishdevar.librepods.viewmodel.AirPodsViewModel -private var debounceJob: Job? = null - -@SuppressLint("DefaultLocale") -@ExperimentalHazeMaterialsApi -@OptIn(ExperimentalMaterial3Api::class, ExperimentalEncodingApi::class) @Composable -fun AdaptiveStrengthScreen(navController: NavController) { - val isDarkTheme = isSystemInDarkTheme() - - val sliderValue = remember { mutableFloatStateOf(0f) } - val service = ServiceManager.getService()!! - - LaunchedEffect(sliderValue) { - val sliderValueFromAACP = service.aacpManager.controlCommandStatusList.find { - it.identifier == AACPManager.Companion.ControlCommandIdentifiers.AUTO_ANC_STRENGTH - }?.value?.takeIf { it.isNotEmpty() }?.get(0) - sliderValueFromAACP?.toFloat()?.let { sliderValue.floatValue = (100 - it) } - } - - val listener = remember { - object : AACPManager.ControlCommandListener { - override fun onControlCommandReceived(controlCommand: AACPManager.ControlCommand) { - if (controlCommand.identifier == AACPManager.Companion.ControlCommandIdentifiers.AUTO_ANC_STRENGTH.value) { - controlCommand.value.takeIf { it.isNotEmpty() }?.get(0)?.toFloat()?.let { - sliderValue.floatValue = (100 - it) - } - } - } - } - } - - DisposableEffect(Unit) { - service.aacpManager.registerControlCommandListener( - AACPManager.Companion.ControlCommandIdentifiers.AUTO_ANC_STRENGTH, - listener - ) - onDispose { - service.aacpManager.unregisterControlCommandListener( - AACPManager.Companion.ControlCommandIdentifiers.AUTO_ANC_STRENGTH, - listener - ) - } - } - +fun AdaptiveStrengthScreen(viewModel: AirPodsViewModel) { + val state by viewModel.uiState.collectAsState() val backdrop = rememberLayerBackdrop() StyledScaffold( @@ -109,17 +62,26 @@ fun AdaptiveStrengthScreen(navController: NavController) { verticalArrangement = Arrangement.spacedBy(16.dp) ) { Spacer(modifier = Modifier.height(spacerHeight)) + val sliderValue = remember { + mutableFloatStateOf( + state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.AUTO_ANC_STRENGTH]?.getOrNull( + 0 + )?.toFloat() ?: 50f + ) + } + var job by remember { mutableStateOf(null) } + val scope = rememberCoroutineScope() StyledSlider( label = stringResource(R.string.customize_adaptive_audio), - mutableFloatState = sliderValue, + value = sliderValue.floatValue, onValueChange = { sliderValue.floatValue = it - debounceJob?.cancel() - debounceJob = CoroutineScope(Dispatchers.Default).launch { - delay(300) - service.aacpManager.sendControlCommand( - AACPManager.Companion.ControlCommandIdentifiers.AUTO_ANC_STRENGTH.value, - (100 - it).toInt() + job?.cancel() + job = scope.launch { + delay(150) + viewModel.setControlCommandValue( + AACPManager.Companion.ControlCommandIdentifiers.AUTO_ANC_STRENGTH, + byteArrayOf((100 - it).toInt().toByte()) ) } }, diff --git a/android/app/src/main/java/me/kavishdevar/librepods/screens/AirPodsSettingsScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/AirPodsSettingsScreen.kt index 3d002db..742575f 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/screens/AirPodsSettingsScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/AirPodsSettingsScreen.kt @@ -20,16 +20,10 @@ package me.kavishdevar.librepods.screens +// import me.kavishdevar.librepods.utils.RadareOffsetFinder import android.annotation.SuppressLint -import android.bluetooth.BluetoothDevice -import android.content.BroadcastReceiver -import android.content.Context import android.content.Context.MODE_PRIVATE -import android.content.Context.RECEIVER_EXPORTED -import android.content.Intent -import android.content.IntentFilter import android.content.SharedPreferences -import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -46,10 +40,10 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -65,23 +59,19 @@ 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 androidx.core.content.edit -import androidx.core.net.toUri import androidx.navigation.NavController -import androidx.navigation.compose.rememberNavController import com.kyant.backdrop.backdrops.rememberLayerBackdrop import com.kyant.backdrop.drawBackdrop import com.kyant.backdrop.highlight.Highlight import dev.chrisbanes.haze.HazeState import dev.chrisbanes.haze.hazeSource import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi -import kotlinx.coroutines.launch +import me.kavishdevar.librepods.BuildConfig import me.kavishdevar.librepods.R import me.kavishdevar.librepods.composables.AboutCard import me.kavishdevar.librepods.composables.AudioSettings import me.kavishdevar.librepods.composables.BatteryView import me.kavishdevar.librepods.composables.CallControlSettings -import me.kavishdevar.librepods.composables.ConfirmationDialog import me.kavishdevar.librepods.composables.ConnectionSettings import me.kavishdevar.librepods.composables.HearingHealthSettings import me.kavishdevar.librepods.composables.MicrophoneSettings @@ -92,39 +82,33 @@ import me.kavishdevar.librepods.composables.StyledButton import me.kavishdevar.librepods.composables.StyledIconButton import me.kavishdevar.librepods.composables.StyledScaffold import me.kavishdevar.librepods.composables.StyledToggle -import me.kavishdevar.librepods.constants.AirPodsNotifications -import me.kavishdevar.librepods.services.AirPodsService import me.kavishdevar.librepods.ui.theme.LibrePodsTheme import me.kavishdevar.librepods.utils.AACPManager +import me.kavishdevar.librepods.utils.ATTHandles +import me.kavishdevar.librepods.utils.AirPodsPro3 import me.kavishdevar.librepods.utils.Capability -// import me.kavishdevar.librepods.utils.RadareOffsetFinder +import me.kavishdevar.librepods.viewmodel.AirPodsViewModel import kotlin.io.encoding.ExperimentalEncodingApi @OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class) @SuppressLint("MissingPermission", "UnspecifiedRegisterReceiverFlag") @Composable -fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService, - navController: NavController, isConnected: Boolean, isRemotelyConnected: Boolean) { - var isLocallyConnected by remember { mutableStateOf(isConnected) } - var isRemotelyConnected by remember { mutableStateOf(isRemotelyConnected) } +fun AirPodsSettingsScreen(viewModel: AirPodsViewModel, navController: NavController) { + val state by viewModel.uiState.collectAsState() val sharedPreferences = LocalContext.current.getSharedPreferences("settings", MODE_PRIVATE) - var device by remember { mutableStateOf(dev) } var deviceName by remember { mutableStateOf( TextFieldValue( - sharedPreferences.getString("name", device?.name ?: "AirPods Pro").toString() + sharedPreferences.getString("name", state.deviceName).toString() ) ) } - LaunchedEffect(service) { - isLocallyConnected = service.isConnectedLocally - } - val nameChangeListener = remember { SharedPreferences.OnSharedPreferenceChangeListener { _, key -> if (key == "name") { - deviceName = TextFieldValue(sharedPreferences.getString("name", "AirPods Pro").toString()) + deviceName = + TextFieldValue(sharedPreferences.getString("name", "AirPods Pro").toString()) } } } @@ -137,113 +121,28 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService, } val snackbarHostState = remember { SnackbarHostState() } - val coroutineScope = rememberCoroutineScope() - fun handleRemoteConnection(connected: Boolean) { - isRemotelyConnected = connected + LaunchedEffect(Unit) { + viewModel.refreshInitialData() } - val context = LocalContext.current - - val connectionReceiver = remember { - object : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - when (intent?.action) { - "me.kavishdevar.librepods.AIRPODS_CONNECTED_REMOTELY" -> { - coroutineScope.launch { - handleRemoteConnection(true) - } - } - "me.kavishdevar.librepods.AIRPODS_DISCONNECTED_REMOTELY" -> { - coroutineScope.launch { - handleRemoteConnection(false) - } - } - AirPodsNotifications.AIRPODS_CONNECTED -> { - coroutineScope.launch { - isLocallyConnected = true - } - } - AirPodsNotifications.AIRPODS_DISCONNECTED -> { - coroutineScope.launch { - isLocallyConnected = false - } - } - AirPodsNotifications.DISCONNECT_RECEIVERS -> { - try { - context?.unregisterReceiver(this) - } catch (e: IllegalArgumentException) { - e.printStackTrace() - } - } - } - } - } - } - - DisposableEffect(Unit) { - val filter = IntentFilter().apply { - addAction("me.kavishdevar.librepods.AIRPODS_CONNECTED_REMOTELY") - addAction("me.kavishdevar.librepods.AIRPODS_DISCONNECTED_REMOTELY") - addAction(AirPodsNotifications.AIRPODS_CONNECTED) - addAction(AirPodsNotifications.AIRPODS_DISCONNECTED) - addAction(AirPodsNotifications.DISCONNECT_RECEIVERS) - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - context.registerReceiver(connectionReceiver, filter, RECEIVER_EXPORTED) - } else { - context.registerReceiver(connectionReceiver, filter) - } - onDispose { - try { - context.unregisterReceiver(connectionReceiver) - } catch (e: Exception) { - e.printStackTrace() - } - } - } - - LaunchedEffect(service) { - service.let { - it.sendBroadcast(Intent(AirPodsNotifications.BATTERY_DATA).apply { - putParcelableArrayListExtra("data", ArrayList(it.getBattery())) - }) - it.sendBroadcast(Intent(AirPodsNotifications.ANC_DATA).apply { - putExtra("data", it.getANC()) - }) - } - } - - val darkMode = isSystemInDarkTheme() + isSystemInDarkTheme() val hazeStateS = remember { mutableStateOf(HazeState()) } - // val showDialog = remember { mutableStateOf(!sharedPreferences.getBoolean("donationDialogShown", false)) } - - val showDialog = remember { mutableStateOf(false) } - StyledScaffold( - title = deviceName.text, - actionButtons = listOf( - {scaffoldBackdrop -> - StyledIconButton( - onClick = { navController.navigate("app_settings") }, - icon = "􀍟", - darkMode = darkMode, - backdrop = scaffoldBackdrop - ) - } - ), - snackbarHostState = snackbarHostState + title = deviceName.text, actionButtons = listOf( + { scaffoldBackdrop -> + StyledIconButton( + onClick = { navController.navigate("app_settings") }, + icon = "􀍟", + backdrop = scaffoldBackdrop + ) + }), snackbarHostState = snackbarHostState ) { spacerHeight, hazeState -> hazeStateS.value = hazeState - if (isLocallyConnected || isRemotelyConnected) { - val instance = service.airpodsInstance - if (instance == null) { - Text("Error: AirPods instance is null") - return@StyledScaffold - } - val capabilities = instance.model.capabilities + + if (state.isLocallyConnected) { + val capabilities = state.capabilities LazyColumn( modifier = Modifier .fillMaxSize() @@ -252,7 +151,11 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService, ) { item(key = "spacer_top") { Spacer(modifier = Modifier.height(spacerHeight)) } item(key = "battery") { - BatteryView(service = service) + BatteryView( + batteryList = state.battery, + budsRes = state.instance?.model?.budsRes ?: R.drawable.airpods_pro_2_case, + caseRes = state.instance?.model?.caseRes ?: R.drawable.airpods_pro_2_case + ) } item(key = "spacer_battery") { Spacer(modifier = Modifier.height(32.dp)) } @@ -265,79 +168,261 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService, independent = true ) } -// val actAsAppleDeviceHookEnabled = RadareOffsetFinder.isSdpOffsetAvailable() -// if (actAsAppleDeviceHookEnabled) { -// item(key = "spacer_hearing_health") { Spacer(modifier = Modifier.height(32.dp)) } -// item(key = "hearing_health") { -// HearingHealthSettings(navController = navController) -// } -// } + + val hasHearingAidCapability = + state.instance?.model?.capabilities?.contains(Capability.HEARING_AID) == true + val hasPPECapability = + state.instance?.model?.capabilities?.contains(Capability.PPE) == true + + if (hasHearingAidCapability || hasPPECapability) { + if (hasPPECapability || (BuildConfig.FLAVOR == "xposed" && hasHearingAidCapability)) item( + key = "spacer_hearing_health" + ) { Spacer(modifier = Modifier.height(24.dp)) } + item(key = "hearing_health") { + HearingHealthSettings( + navController = navController, + hasPPECapability = hasPPECapability, + hasHearingAidCapability = hasHearingAidCapability, + isXposed = BuildConfig.FLAVOR == "xposed" + ) + } + } if (capabilities.contains(Capability.LISTENING_MODE)) { item(key = "spacer_noise") { Spacer(modifier = Modifier.height(16.dp)) } - item(key = "noise_control") { NoiseControlSettings(service = service) } + item(key = "noise_control") { + NoiseControlSettings( + showOffListeningMode = state.offListeningMode, + noiseControlModeValue = state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE]?.getOrNull( + 0 + )?.toInt() ?: 3, + onNoiseControlModeChanged = { + viewModel.setControlCommandInt( + AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE, + it + ) + }, + ) + } } if (capabilities.contains(Capability.STEM_CONFIG)) { item(key = "spacer_press_hold") { Spacer(modifier = Modifier.height(16.dp)) } - item(key = "press_hold") { PressAndHoldSettings(navController = navController) } + item(key = "press_hold") { + PressAndHoldSettings( + navController = navController, + leftAction = state.leftAction, + rightAction = state.rightAction + ) + } } item(key = "spacer_call") { Spacer(modifier = Modifier.height(16.dp)) } - item(key = "call_control") { CallControlSettings(hazeState = hazeState) } + item(key = "call_control") { + val flipped = + state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.CALL_MANAGEMENT_CONFIG]?.take( + 2 + )?.equals(byteArrayOf(0x00.toByte(), 0x02.toByte())) + CallControlSettings( + hazeState = hazeState, + flipped = flipped == true, + onCallControlValueChanged = { + viewModel.setControlCommandValue( + AACPManager.Companion.ControlCommandIdentifiers.CALL_MANAGEMENT_CONFIG, + if (it) byteArrayOf(0x00, 0x02) else byteArrayOf(0x00, 0x03) + ) + }) + } - if (capabilities.contains(Capability.STEM_CONFIG)) { + if (capabilities.contains(Capability.STEM_CONFIG) && !BuildConfig.PLAY_BUILD) { item(key = "spacer_camera") { Spacer(modifier = Modifier.height(16.dp)) } - item(key = "camera_control") { NavigationButton(to = "camera_control", name = stringResource(R.string.camera_remote), description = stringResource(R.string.camera_control_description), title = stringResource(R.string.camera_control), navController = navController) } + item(key = "camera_control") { + NavigationButton( + to = "camera_control", + name = stringResource(R.string.camera_remote), + description = stringResource(R.string.camera_control_description), + title = stringResource(R.string.camera_control), + navController = navController + ) + } + } + + item(key = "upgrade_button") { + val context = LocalContext.current + val textColor = if (isSystemInDarkTheme()) Color.White else Color.Black + + if (!state.isPremium) { + Spacer(modifier = Modifier.height(28.dp)) + StyledButton( + onClick = { + viewModel.purchase(context) + }, + backdrop = rememberLayerBackdrop(), + modifier = Modifier.fillMaxWidth(), + maxScale = 0.05f, + tint = Color(0xFF916100) + ) { + Text( + stringResource(R.string.unlock_all_features), + style = TextStyle( + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + fontFamily = FontFamily(Font(R.font.sf_pro)), + color = textColor + ), + ) + } + Spacer(modifier = Modifier.height(16.dp)) + } } item(key = "spacer_audio") { Spacer(modifier = Modifier.height(16.dp)) } - item(key = "audio") { AudioSettings(navController = navController) } + item(key = "audio") { + val model = state.instance?.model ?: AirPodsPro3() + val adaptiveVolumeCapability = + model.capabilities.contains(Capability.ADAPTIVE_VOLUME) + val conversationalAwarenessCapability = + model.capabilities.contains(Capability.CONVERSATION_AWARENESS) + val loudSoundReductionCapability = + model.capabilities.contains(Capability.LOUD_SOUND_REDUCTION) + val adaptiveAudioCapability = + model.capabilities.contains(Capability.ADAPTIVE_VOLUME) + + val adaptiveVolumeChecked = + state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.ADAPTIVE_VOLUME_CONFIG]?.getOrNull( + 0 + ) == 0x01.toByte() + val conversationalAwarenessChecked = + state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.CONVERSATION_DETECT_CONFIG]?.getOrNull( + 0 + ) == 0x01.toByte() + val loudSoundReduction = + viewModel.getATTCharacteristicValue(ATTHandles.LOUD_SOUND_REDUCTION) + ?.getOrNull(0) == 0x01.toByte() + + val isXposed = BuildConfig.FLAVOR == "xposed" + AudioSettings( + navController = navController, + adaptiveVolumeCapability = adaptiveVolumeCapability, + conversationalAwarenessCapability = conversationalAwarenessCapability, + loudSoundReductionCapability = loudSoundReductionCapability, + adaptiveAudioCapability = adaptiveAudioCapability, + adaptiveVolumeChecked = adaptiveVolumeChecked, + onAdaptiveVolumeCheckedChange = { checked -> + viewModel.setControlCommandBoolean( + AACPManager.Companion.ControlCommandIdentifiers.ADAPTIVE_VOLUME_CONFIG, + checked + ) + }, + conversationalAwarenessChecked = conversationalAwarenessChecked && state.isPremium, + onConversationalAwarenessCheckedChange = { checked -> + viewModel.setControlCommandBoolean( + AACPManager.Companion.ControlCommandIdentifiers.CONVERSATION_DETECT_CONFIG, + checked + ) + }, + loudSoundReductionChecked = loudSoundReduction, + onLoudSoundReductionCheckedChange = { + viewModel.setATTCharacteristicValue( + ATTHandles.LOUD_SOUND_REDUCTION, + byteArrayOf(if (it) 0x01.toByte() else 0x00.toByte()) + ) + }, + isXposed = isXposed, + isPremium = state.isPremium + ) + } item(key = "spacer_connection") { Spacer(modifier = Modifier.height(16.dp)) } - item(key = "connection") { ConnectionSettings() } + item(key = "connection") { + ConnectionSettings( + automaticEarDetectionEnabled = state.automaticEarDetectionEnabled, + onAutomaticEarDetectionChanged = { + viewModel.setAutomaticEarDetectionEnabled(it) + }, + automaticConnectionEnabled = state.automaticConnectionEnabled, + onAutomaticConnectionChanged = { viewModel.setAutomaticConnectionEnabled(it) } + ) + } item(key = "spacer_microphone") { Spacer(modifier = Modifier.height(16.dp)) } - item(key = "microphone") { MicrophoneSettings(hazeState) } + item(key = "microphone") { + val id = AACPManager.Companion.ControlCommandIdentifiers.MIC_MODE + MicrophoneSettings( + hazeState = hazeState, + micModeValue = state.controlStates[id]?.getOrNull(0) ?: 0x00.toByte(), + onMicModeValueChanged = { viewModel.setControlCommandByte(id, it) }) + } if (capabilities.contains(Capability.SLEEP_DETECTION)) { item(key = "spacer_sleep") { Spacer(modifier = Modifier.height(16.dp)) } item(key = "sleep_detection") { + val id = + AACPManager.Companion.ControlCommandIdentifiers.SLEEP_DETECTION_CONFIG StyledToggle( label = stringResource(R.string.sleep_detection), - controlCommandIdentifier = AACPManager.Companion.ControlCommandIdentifiers.SLEEP_DETECTION_CONFIG + checked = state.controlStates[id]?.getOrNull(0) == 0x01.toByte(), + onCheckedChange = { + viewModel.setControlCommandBoolean(id, it) + }, + enabled = state.isPremium ) } } if (capabilities.contains(Capability.HEAD_GESTURES)) { item(key = "spacer_head_tracking") { Spacer(modifier = Modifier.height(16.dp)) } - item(key = "head_tracking") { NavigationButton(to = "head_tracking", name = stringResource(R.string.head_gestures), navController = navController, currentState = if (sharedPreferences.getBoolean("head_gestures", false)) stringResource(R.string.on) else stringResource(R.string.off)) } + item(key = "head_tracking") { + NavigationButton( + to = "head_tracking", + name = stringResource(R.string.head_gestures), + navController = navController, + currentState = if (sharedPreferences.getBoolean( + "head_gestures", false + ) + ) stringResource(R.string.on) else stringResource(R.string.off) + ) + } } item(key = "spacer_accessibility") { Spacer(modifier = Modifier.height(16.dp)) } - item(key = "accessibility") { NavigationButton(to = "accessibility", name = stringResource(R.string.accessibility), navController = navController) } + item(key = "accessibility") { + NavigationButton( + to = "accessibility", + name = stringResource(R.string.accessibility), + navController = navController + ) + } - if (capabilities.contains(Capability.LOUD_SOUND_REDUCTION)){ + if (capabilities.contains(Capability.LOUD_SOUND_REDUCTION)) { item(key = "spacer_off_listening") { Spacer(modifier = Modifier.height(16.dp)) } item(key = "off_listening") { + val id = AACPManager.Companion.ControlCommandIdentifiers.ALLOW_OFF_OPTION StyledToggle( label = stringResource(R.string.off_listening_mode), - controlCommandIdentifier = AACPManager.Companion.ControlCommandIdentifiers.ALLOW_OFF_OPTION, - description = stringResource(R.string.off_listening_mode_description) + description = stringResource(R.string.off_listening_mode_description), + checked = state.controlStates[id]?.getOrNull(0) == 0x01.toByte(), + onCheckedChange = viewModel::setOffListeningMode ) } } item(key = "spacer_about") { Spacer(modifier = Modifier.height(32.dp)) } - item(key = "about") { AboutCard(navController = navController) } + item(key = "about") { + AboutCard( + navController = navController, + modelName = state.modelName, + actualModel = state.actualModel, + serialNumbers = state.serialNumbers, + version = state.version3, + ) + } - item(key = "spacer_debug") { Spacer(modifier = Modifier.height(16.dp)) } - item(key = "debug") { NavigationButton("debug", "Debug", navController) } +// item(key = "spacer_debug") { Spacer(modifier = Modifier.height(16.dp)) } +// item(key = "debug") { NavigationButton("debug", "Debug", navController) } item(key = "spacer_bottom") { Spacer(Modifier.height(24.dp)) } } - } - else { + } else { val backdrop = rememberLayerBackdrop() Column( modifier = Modifier @@ -348,23 +433,20 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService, shape = { RoundedCornerShape(0.dp) }, highlight = { Highlight.Ambient.copy(alpha = 0f) - } - ) + }, + effects = {}) .hazeSource(hazeState) .padding(horizontal = 8.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text( - text = stringResource(R.string.airpods_not_connected), - style = TextStyle( + text = stringResource(R.string.airpods_not_connected), style = TextStyle( fontSize = 24.sp, fontWeight = FontWeight.Medium, color = if (isSystemInDarkTheme()) Color.White else Color.Black, fontFamily = FontFamily(Font(R.font.sf_pro)) - ), - textAlign = TextAlign.Center, - modifier = Modifier.fillMaxWidth() + ), textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth() ) Spacer(Modifier.height(24.dp)) Text( @@ -379,34 +461,30 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService, modifier = Modifier.fillMaxWidth() ) Spacer(Modifier.height(32.dp)) - StyledButton( - onClick = { navController.navigate("troubleshooting") }, - backdrop = backdrop, - modifier = Modifier - .fillMaxWidth(0.9f) - ) { - Text( - text = stringResource(R.string.troubleshooting), - style = TextStyle( - fontSize = 16.sp, - fontWeight = FontWeight.Medium, - fontFamily = FontFamily(Font(R.font.sf_pro)), - color = if (isSystemInDarkTheme()) Color.White else Color.Black - ) - ) - } - Spacer(Modifier.height(16.dp)) +// StyledButton( +// onClick = { navController.navigate("troubleshooting") }, +// backdrop = backdrop, +// modifier = Modifier +// .fillMaxWidth(0.9f) +// ) { +// Text( +// text = stringResource(R.string.troubleshooting), +// style = TextStyle( +// fontSize = 16.sp, +// fontWeight = FontWeight.Medium, +// fontFamily = FontFamily(Font(R.font.sf_pro)), +// color = if (isSystemInDarkTheme()) Color.White else Color.Black +// ) +// ) +// } +// Spacer(Modifier.height(16.dp)) StyledButton( onClick = { - service.reconnectFromSavedMac() - }, - backdrop = backdrop, - modifier = Modifier - .fillMaxWidth(0.9f) + viewModel.reconnectFromSavedMac() + }, backdrop = backdrop, modifier = Modifier.fillMaxWidth(0.9f) ) { Text( - text = stringResource(R.string.reconnect_to_last_device), - style = TextStyle( + text = stringResource(R.string.reconnect_to_last_device), style = TextStyle( fontSize = 16.sp, fontWeight = FontWeight.Medium, fontFamily = FontFamily(Font(R.font.sf_pro)), @@ -417,37 +495,18 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService, } } } - ConfirmationDialog( - showDialog = showDialog, - title = stringResource(R.string.support_librepods), - message = stringResource(R.string.support_dialog_description), - confirmText = stringResource(R.string.support_me) + " \uDBC0\uDEB5", - dismissText = stringResource(R.string.never_show_again), - onConfirm = { - val browserIntent = Intent( - Intent.ACTION_VIEW, - "https://github.com/sponsors/kavishdevar".toUri() - ) - context.startActivity(browserIntent) - sharedPreferences.edit { putBoolean("donationDialogShown", true) } - }, - onDismiss = { - sharedPreferences.edit { putBoolean("donationDialogShown", true) } - }, - hazeState = hazeStateS.value, - ) } @Preview @Composable fun AirPodsSettingsScreenPreview() { - Column ( + Column( modifier = Modifier.height(2000.dp) ) { - LibrePodsTheme ( + LibrePodsTheme( darkTheme = true ) { - AirPodsSettingsScreen(dev = null, service = AirPodsService(), navController = rememberNavController(), isConnected = true, isRemotelyConnected = false) +// AirPodsSettingsScreen(dev = null, service = AirPodsService(), navController = rememberNavController(), isConnected = true, isRemotelyConnected = false) } } } diff --git a/android/app/src/main/java/me/kavishdevar/librepods/screens/AppSettingsScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/AppSettingsScreen.kt index 1245deb..bb73a2a 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/screens/AppSettingsScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/AppSettingsScreen.kt @@ -18,36 +18,21 @@ package me.kavishdevar.librepods.screens -//import me.kavishdevar.librepods.utils.RadareOffsetFinder -import android.content.Context import android.widget.Toast -import androidx.activity.compose.BackHandler import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Refresh import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults @@ -55,11 +40,8 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.mutableFloatStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext @@ -72,121 +54,28 @@ import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.core.content.edit +import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import com.kyant.backdrop.backdrops.layerBackdrop import com.kyant.backdrop.backdrops.rememberLayerBackdrop import dev.chrisbanes.haze.hazeSource -import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi +import me.kavishdevar.librepods.BuildConfig import me.kavishdevar.librepods.R import me.kavishdevar.librepods.composables.NavigationButton +import me.kavishdevar.librepods.composables.StyledButton import me.kavishdevar.librepods.composables.StyledScaffold import me.kavishdevar.librepods.composables.StyledSlider import me.kavishdevar.librepods.composables.StyledToggle -import me.kavishdevar.librepods.utils.AACPManager -import kotlin.io.encoding.Base64 -import kotlin.io.encoding.ExperimentalEncodingApi -import kotlin.math.roundToInt +import me.kavishdevar.librepods.viewmodel.AppSettingsViewModel -@OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class, ExperimentalEncodingApi::class) @Composable -fun AppSettingsScreen(navController: NavController) { - val sharedPreferences = LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE) - - val isDarkTheme = isSystemInDarkTheme() +fun AppSettingsScreen( + navController: NavController, + viewModel: AppSettingsViewModel = viewModel() +) { val context = LocalContext.current - val coroutineScope = rememberCoroutineScope() val scrollState = rememberScrollState() - - val showResetDialog = remember { mutableStateOf(false) } - val showIrkDialog = remember { mutableStateOf(false) } - val showEncKeyDialog = remember { mutableStateOf(false) } - val showCameraDialog = remember { mutableStateOf(false) } - val irkValue = remember { mutableStateOf("") } - val encKeyValue = remember { mutableStateOf("") } - val cameraPackageValue = remember { mutableStateOf("") } - val irkError = remember { mutableStateOf(null) } - val encKeyError = remember { mutableStateOf(null) } - val cameraPackageError = remember { mutableStateOf(null) } - - LaunchedEffect(Unit) { - val savedIrk = sharedPreferences.getString(AACPManager.Companion.ProximityKeyType.IRK.name, null) - val savedEncKey = sharedPreferences.getString(AACPManager.Companion.ProximityKeyType.ENC_KEY.name, null) - val savedCameraPackage = sharedPreferences.getString("custom_camera_package", null) - - if (savedIrk != null) { - try { - val decoded = Base64.decode(savedIrk) - irkValue.value = decoded.joinToString("") { "%02x".format(it) } - } catch (e: Exception) { - irkValue.value = "" - e.printStackTrace() - } - } - - if (savedEncKey != null) { - try { - val decoded = Base64.decode(savedEncKey) - encKeyValue.value = decoded.joinToString("") { "%02x".format(it) } - } catch (e: Exception) { - encKeyValue.value = "" - e.printStackTrace() - } - } - if (savedCameraPackage != null) { - cameraPackageValue.value = savedCameraPackage - } - } - - val showPhoneBatteryInWidget = remember { - mutableStateOf(sharedPreferences.getBoolean("show_phone_battery_in_widget", true)) - } - val conversationalAwarenessPauseMusicEnabled = remember { - mutableStateOf(sharedPreferences.getBoolean("conversational_awareness_pause_music", false)) - } - val relativeConversationalAwarenessVolumeEnabled = remember { - mutableStateOf(sharedPreferences.getBoolean("relative_conversational_awareness_volume", true)) - } - val openDialogForControlling = remember { - mutableStateOf(sharedPreferences.getString("qs_click_behavior", "dialog") == "dialog") - } - val disconnectWhenNotWearing = remember { - mutableStateOf(sharedPreferences.getBoolean("disconnect_when_not_wearing", false)) - } - - val takeoverWhenDisconnected = remember { - mutableStateOf(sharedPreferences.getBoolean("takeover_when_disconnected", true)) - } - val takeoverWhenIdle = remember { - mutableStateOf(sharedPreferences.getBoolean("takeover_when_idle", true)) - } - val takeoverWhenMusic = remember { - mutableStateOf(sharedPreferences.getBoolean("takeover_when_music", false)) - } - val takeoverWhenCall = remember { - mutableStateOf(sharedPreferences.getBoolean("takeover_when_call", true)) - } - - val takeoverWhenRingingCall = remember { - mutableStateOf(sharedPreferences.getBoolean("takeover_when_ringing_call", true)) - } - val takeoverWhenMediaStart = remember { - mutableStateOf(sharedPreferences.getBoolean("takeover_when_media_start", true)) - } - - val useAlternateHeadTrackingPackets = remember { - mutableStateOf(sharedPreferences.getBoolean("use_alternate_head_tracking_packets", false)) - } - - fun validateHexInput(input: String): Boolean { - val hexPattern = Regex("^[0-9a-fA-F]{32}$") - return hexPattern.matches(input) - } - - val isProcessingSdp = remember { mutableStateOf(false) } -// val actAsAppleDevice = remember { mutableStateOf(false) } - - BackHandler(enabled = isProcessingSdp.value) {} + val uiState by viewModel.uiState.collectAsState() val backdrop = rememberLayerBackdrop() @@ -207,86 +96,97 @@ fun AppSettingsScreen(navController: NavController) { val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF) val textColor = if (isDarkTheme) Color.White else Color.Black + if (!uiState.isPremium) { + StyledButton( + onClick = { + viewModel.purchase(context) + }, + backdrop = rememberLayerBackdrop(), + modifier = Modifier.fillMaxWidth(), + maxScale = 0.05f, + tint = Color(0xFF916100) + ) { + Text( + stringResource(R.string.unlock_all_features), + style = TextStyle( + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + fontFamily = FontFamily(Font(R.font.sf_pro)), + color = textColor + ), + ) + } + } + StyledToggle( title = stringResource(R.string.widget), label = stringResource(R.string.show_phone_battery_in_widget), description = stringResource(R.string.show_phone_battery_in_widget_description), - checkedState = showPhoneBatteryInWidget, - sharedPreferenceKey = "show_phone_battery_in_widget", - sharedPreferences = sharedPreferences, + checked = uiState.showPhoneBatteryInWidget, + onCheckedChange = viewModel::setShowPhoneBatteryInWidget, + enabled = uiState.isPremium ) Text( - text = stringResource(R.string.conversational_awareness), - style = TextStyle( + text = stringResource(R.string.conversational_awareness), style = TextStyle( fontSize = 14.sp, fontWeight = FontWeight.Bold, color = textColor.copy(alpha = 0.6f), fontFamily = FontFamily(Font(R.font.sf_pro)) - ), - modifier = Modifier.padding(16.dp, bottom = 2.dp, top = 24.dp) + ), modifier = Modifier.padding(16.dp, bottom = 2.dp, top = 24.dp) ) Spacer(modifier = Modifier.height(2.dp)) - Column ( + Column( modifier = Modifier .fillMaxWidth() .background( - backgroundColor, - RoundedCornerShape(28.dp) + backgroundColor, RoundedCornerShape(28.dp) ) .padding(vertical = 4.dp) ) { - fun updateConversationalAwarenessPauseMusic(enabled: Boolean) { - conversationalAwarenessPauseMusicEnabled.value = enabled - sharedPreferences.edit { putBoolean("conversational_awareness_pause_music", enabled)} - } - - fun updateRelativeConversationalAwarenessVolume(enabled: Boolean) { - relativeConversationalAwarenessVolumeEnabled.value = enabled - sharedPreferences.edit { putBoolean("relative_conversational_awareness_volume", enabled)} - } - StyledToggle( label = stringResource(R.string.conversational_awareness_pause_music), description = stringResource(R.string.conversational_awareness_pause_music_description), - checkedState = conversationalAwarenessPauseMusicEnabled, - onCheckedChange = { updateConversationalAwarenessPauseMusic(it) }, - independent = false + checked = uiState.conversationalAwarenessPauseMusicEnabled, + onCheckedChange = viewModel::setConversationalAwarenessPauseMusicEnabled, + independent = false, + enabled = uiState.isPremium ) HorizontalDivider( thickness = 1.dp, color = Color(0x40888888), - modifier = Modifier - .padding(horizontal = 12.dp) + modifier = Modifier.padding(horizontal = 12.dp) ) StyledToggle( label = stringResource(R.string.relative_conversational_awareness_volume), description = stringResource(R.string.relative_conversational_awareness_volume_description), - checkedState = relativeConversationalAwarenessVolumeEnabled, - onCheckedChange = { updateRelativeConversationalAwarenessVolume(it) }, - independent = false + checked = uiState.relativeConversationalAwarenessVolumeEnabled, + onCheckedChange = viewModel::setRelativeConversationalAwarenessVolumeEnabled, + independent = false, + enabled = uiState.isPremium ) } Spacer(modifier = Modifier.height(16.dp)) - val conversationalAwarenessVolume = remember { mutableFloatStateOf(sharedPreferences.getInt("conversational_awareness_volume", 43).toFloat()) } - LaunchedEffect(conversationalAwarenessVolume.floatValue) { - sharedPreferences.edit { putInt("conversational_awareness_volume", conversationalAwarenessVolume.floatValue.roundToInt()) } + val conversationalAwarenessVolume = uiState.conversationalAwarenessVolume + LaunchedEffect(conversationalAwarenessVolume) { + viewModel.setConversationalAwarenessVolume(conversationalAwarenessVolume) } StyledSlider( label = stringResource(R.string.conversational_awareness_volume), - mutableFloatState = conversationalAwarenessVolume, + value = conversationalAwarenessVolume, valueRange = 10f..85f, startLabel = "10%", endLabel = "85%", - onValueChange = { newValue -> conversationalAwarenessVolume.floatValue = newValue }, - independent = true + onValueChange = { newValue -> viewModel.setConversationalAwarenessVolume(newValue) }, + independent = true, + enabled = uiState.isPremium ) Spacer(modifier = Modifier.height(16.dp)) @@ -296,44 +196,32 @@ fun AppSettingsScreen(navController: NavController) { title = stringResource(R.string.camera_control), name = stringResource(R.string.set_custom_camera_package), navController = navController, - onClick = { showCameraDialog.value = true }, + onClick = { + if (uiState.isPremium) viewModel.setShowCameraDialog(true) + }, independent = true, description = stringResource(R.string.camera_control_app_description) ) -// Spacer(modifier = Modifier.height(16.dp)) -// -// StyledToggle( -// title = stringResource(R.string.quick_settings_tile), -// label = stringResource(R.string.open_dialog_for_controlling), -// description = stringResource(R.string.open_dialog_for_controlling_description), -// checkedState = openDialogForControlling, -// onCheckedChange = { -// openDialogForControlling.value = it -// sharedPreferences.edit { putString("qs_click_behavior", if (it) "dialog" else "cycle") } -// }, -// ) - Spacer(modifier = Modifier.height(16.dp)) - - StyledToggle( - title = stringResource(R.string.ear_detection), - label = stringResource(R.string.disconnect_when_not_wearing), - description = stringResource(R.string.disconnect_when_not_wearing_description), - checkedState = disconnectWhenNotWearing, - sharedPreferenceKey = "disconnect_when_not_wearing", - sharedPreferences = sharedPreferences, - ) + if (BuildConfig.FLAVOR == "xposed") { + StyledToggle( + title = stringResource(R.string.ear_detection), + label = stringResource(R.string.disconnect_when_not_wearing), + description = stringResource(R.string.disconnect_when_not_wearing_description), + checked = uiState.disconnectWhenNotWearing, + onCheckedChange = viewModel::setDisconnectWhenNotWearing, + enabled = uiState.isPremium + ) + } Text( - text = stringResource(R.string.takeover_airpods_state), - style = TextStyle( + text = stringResource(R.string.takeover_airpods_state), style = TextStyle( fontSize = 14.sp, fontWeight = FontWeight.Bold, color = textColor.copy(alpha = 0.6f), fontFamily = FontFamily(Font(R.font.sf_pro)) - ), - modifier = Modifier.padding(16.dp, bottom = 2.dp, top = 24.dp) + ), modifier = Modifier.padding(16.dp, bottom = 2.dp, top = 24.dp) ) Spacer(modifier = Modifier.height(4.dp)) @@ -342,301 +230,134 @@ fun AppSettingsScreen(navController: NavController) { modifier = Modifier .fillMaxWidth() .background( - backgroundColor, - RoundedCornerShape(28.dp) + backgroundColor, RoundedCornerShape(28.dp) ) .padding(vertical = 4.dp) ) { StyledToggle( label = stringResource(R.string.takeover_disconnected), description = stringResource(R.string.takeover_disconnected_desc), - checkedState = takeoverWhenDisconnected, - onCheckedChange = { - takeoverWhenDisconnected.value = it - sharedPreferences.edit { putBoolean("takeover_when_disconnected", it)} - }, - independent = false + checked = uiState.takeoverWhenDisconnected, + onCheckedChange = viewModel::setTakeoverWhenDisconnected, + independent = false, + enabled = uiState.isPremium ) HorizontalDivider( thickness = 1.dp, color = Color(0x40888888), - modifier = Modifier - .padding(horizontal = 12.dp) + modifier = Modifier.padding(horizontal = 12.dp) ) StyledToggle( label = stringResource(R.string.takeover_idle), description = stringResource(R.string.takeover_idle_desc), - checkedState = takeoverWhenIdle, - onCheckedChange = { - takeoverWhenIdle.value = it - sharedPreferences.edit { putBoolean("takeover_when_idle", it)} - }, - independent = false + checked = uiState.takeoverWhenIdle, + onCheckedChange = viewModel::setTakeoverWhenIdle, + independent = false, + enabled = uiState.isPremium ) HorizontalDivider( thickness = 1.dp, color = Color(0x40888888), - modifier = Modifier - .padding(horizontal = 12.dp) + modifier = Modifier.padding(horizontal = 12.dp) ) StyledToggle( label = stringResource(R.string.takeover_music), description = stringResource(R.string.takeover_music_desc), - checkedState = takeoverWhenMusic, - onCheckedChange = { - takeoverWhenMusic.value = it - sharedPreferences.edit { putBoolean("takeover_when_music", it)} - }, - independent = false + checked = uiState.takeoverWhenMusic, + onCheckedChange = viewModel::setTakeoverWhenMusic, + independent = false, + enabled = uiState.isPremium ) HorizontalDivider( thickness = 1.dp, color = Color(0x40888888), - modifier = Modifier - .padding(horizontal = 12.dp) + modifier = Modifier.padding(horizontal = 12.dp) ) StyledToggle( label = stringResource(R.string.takeover_call), description = stringResource(R.string.takeover_call_desc), - checkedState = takeoverWhenCall, - onCheckedChange = { - takeoverWhenCall.value = it - sharedPreferences.edit { putBoolean("takeover_when_call", it)} - }, - independent = false + checked = uiState.takeoverWhenCall, + onCheckedChange = viewModel::setTakeoverWhenCall, + independent = false, + enabled = uiState.isPremium ) } Spacer(modifier = Modifier.height(16.dp)) Text( - text = stringResource(R.string.takeover_phone_state), - style = TextStyle( + text = stringResource(R.string.takeover_phone_state), style = TextStyle( fontSize = 14.sp, fontWeight = FontWeight.Bold, color = textColor.copy(alpha = 0.6f), fontFamily = FontFamily(Font(R.font.sf_pro)) - ), - modifier = Modifier.padding(horizontal = 16.dp) + ), modifier = Modifier.padding(horizontal = 16.dp) ) Spacer(modifier = Modifier.height(4.dp)) Column( modifier = Modifier .fillMaxWidth() .background( - backgroundColor, - RoundedCornerShape(28.dp) + backgroundColor, RoundedCornerShape(28.dp) ) .padding(vertical = 4.dp) - ){ + ) { StyledToggle( label = stringResource(R.string.takeover_ringing_call), description = stringResource(R.string.takeover_ringing_call_desc), - checkedState = takeoverWhenRingingCall, - onCheckedChange = { - takeoverWhenRingingCall.value = it - sharedPreferences.edit { putBoolean("takeover_when_ringing_call", it)} - }, - independent = false + checked = uiState.takeoverWhenRingingCall, + onCheckedChange = viewModel::setTakeoverWhenRingingCall, + independent = false, + enabled = uiState.isPremium ) HorizontalDivider( thickness = 1.dp, color = Color(0x40888888), - modifier = Modifier - .padding(horizontal = 12.dp) + modifier = Modifier.padding(horizontal = 12.dp) ) StyledToggle( label = stringResource(R.string.takeover_media_start), description = stringResource(R.string.takeover_media_start_desc), - checkedState = takeoverWhenMediaStart, - onCheckedChange = { - takeoverWhenMediaStart.value = it - sharedPreferences.edit { putBoolean("takeover_when_media_start", it)} - }, - independent = false + checked = uiState.takeoverWhenMediaStart, + onCheckedChange = viewModel::setTakeoverWhenMediaStart, + independent = false, + enabled = uiState.isPremium ) } Text( - text = stringResource(R.string.advanced_options), - style = TextStyle( + text = stringResource(R.string.advanced_options), style = TextStyle( fontSize = 14.sp, fontWeight = FontWeight.Bold, color = textColor.copy(alpha = 0.6f), fontFamily = FontFamily(Font(R.font.sf_pro)) - ), - modifier = Modifier.padding(16.dp, bottom = 2.dp, top = 24.dp) + ), modifier = Modifier.padding(16.dp, bottom = 2.dp, top = 24.dp) ) Spacer(modifier = Modifier.height(2.dp)) - Column( - modifier = Modifier - .fillMaxWidth() - .background( - backgroundColor, - RoundedCornerShape(28.dp) - ) - .padding(horizontal = 16.dp, vertical = 4.dp) - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .clickable ( - onClick = { showIrkDialog.value = true }, - indication = null, - interactionSource = remember { MutableInteractionSource() } - ), - verticalAlignment = Alignment.CenterVertically - ) { - Column( - modifier = Modifier - .weight(1f) - .padding(vertical = 8.dp) - .padding(end = 4.dp) - ) { - Text( - text = stringResource(R.string.set_identity_resolving_key), - fontSize = 16.sp, - color = textColor - ) - Spacer(modifier = Modifier.height(4.dp)) - Text( - text = stringResource(R.string.set_identity_resolving_key_description), - fontSize = 14.sp, - color = textColor.copy(0.6f), - lineHeight = 16.sp, - ) - } - } - - HorizontalDivider( - thickness = 1.dp, - color = Color(0x40888888), - ) - - Row( - modifier = Modifier - .fillMaxWidth() - .clickable ( - onClick = { showEncKeyDialog.value = true }, - indication = null, - interactionSource = remember { MutableInteractionSource() } - ), - verticalAlignment = Alignment.CenterVertically - ) { - Column( - modifier = Modifier - .weight(1f) - .padding(vertical = 8.dp) - .padding(end = 4.dp) - ) { - Text( - text = stringResource(R.string.set_encryption_key), - fontSize = 16.sp, - color = textColor - ) - Spacer(modifier = Modifier.height(4.dp)) - Text( - text = stringResource(R.string.set_encryption_key_description), - fontSize = 14.sp, - color = textColor.copy(0.6f), - lineHeight = 16.sp, - ) - } - } - } - - Spacer(modifier = Modifier.height(16.dp)) - StyledToggle( label = stringResource(R.string.use_alternate_head_tracking_packets), description = stringResource(R.string.use_alternate_head_tracking_packets_description), - checkedState = useAlternateHeadTrackingPackets, - onCheckedChange = { - useAlternateHeadTrackingPackets.value = it - sharedPreferences.edit { putBoolean("use_alternate_head_tracking_packets", it)} - }, - independent = true + checked = uiState.useAlternateHeadTrackingPackets, + onCheckedChange = viewModel::setUseAlternateHeadTrackingPackets, + independent = true, + enabled = uiState.isPremium ) Spacer(modifier = Modifier.height(16.dp)) - NavigationButton( - to = "troubleshooting", - name = stringResource(R.string.troubleshooting), - navController = navController, - independent = true, - description = stringResource(R.string.troubleshooting_description) - ) - -// LaunchedEffect(Unit) { -// actAsAppleDevice.value = RadareOffsetFinder.isSdpOffsetAvailable() -// } - val restartBluetoothText = stringResource(R.string.found_offset_restart_bluetooth) - - /*StyledToggle( - label = stringResource(R.string.act_as_an_apple_device), - description = stringResource(R.string.act_as_an_apple_device_description), - checkedState = actAsAppleDevice, - onCheckedChange = { - actAsAppleDevice.value = it - isProcessingSdp.value = true - coroutineScope.launch { - if (it) { - val radareOffsetFinder = RadareOffsetFinder(context) - val success = radareOffsetFinder.findSdpOffset() - if (success) { - Toast.makeText(context, restartBluetoothText, Toast.LENGTH_LONG).show() - } - } else { - RadareOffsetFinder.clearSdpOffset() - } - isProcessingSdp.value = false - } - }, - independent = true, - enabled = !isProcessingSdp.value - )*/ - - Spacer(modifier = Modifier.height(16.dp)) - -// Button( -// onClick = { showResetDialog.value = true }, -// modifier = Modifier -// .fillMaxWidth() -// .height(50.dp), -// colors = ButtonDefaults.buttonColors( -// containerColor = MaterialTheme.colorScheme.errorContainer -// ), -// shape = RoundedCornerShape(28.dp) -// ) { -// Row( -// verticalAlignment = Alignment.CenterVertically, -// horizontalArrangement = Arrangement.Center -// ) { -// Icon( -// imageVector = Icons.Default.Refresh, -// contentDescription = "Reset", -// tint = MaterialTheme.colorScheme.onErrorContainer, -// modifier = Modifier.size(18.dp) -// ) -// Spacer(modifier = Modifier.width(8.dp)) -// Text( -// text = stringResource(R.string.reset_hook_offset), -// color = MaterialTheme.colorScheme.onErrorContainer, -// style = TextStyle( -// fontSize = 16.sp, -// fontWeight = FontWeight.Medium, -// fontFamily = FontFamily(Font(R.font.sf_pro)) -// ) -// ) -// } -// } +// NavigationButton( +// to = "troubleshooting", +// name = stringResource(R.string.troubleshooting), +// navController = navController, +// independent = true, +// description = stringResource(R.string.troubleshooting_description) +// ) Spacer(modifier = Modifier.height(16.dp)) @@ -649,331 +370,72 @@ fun AppSettingsScreen(navController: NavController) { Spacer(modifier = Modifier.height(32.dp)) -// if (showResetDialog.value) { -// AlertDialog( -// onDismissRequest = { showResetDialog.value = false }, -// title = { -// Text( -// "Reset Hook Offset", -// fontFamily = FontFamily(Font(R.font.sf_pro)), -// fontWeight = FontWeight.Medium -// ) -// }, -// text = { -// Text( -// stringResource(R.string.reset_hook_offset_description), -// fontFamily = FontFamily(Font(R.font.sf_pro)) -// ) -// }, -// confirmButton = { -// val successText = stringResource(R.string.hook_offset_reset_success) -// val failureText = stringResource(R.string.hook_offset_reset_failure) -// TextButton( -// onClick = { -// if (RadareOffsetFinder.clearHookOffsets()) { -// Toast.makeText( -// context, -// successText, -// Toast.LENGTH_LONG -// ).show() -// -// navController.navigate("onboarding") { -// popUpTo("settings") { inclusive = true } -// } -// } else { -// Toast.makeText( -// context, -// failureText, -// Toast.LENGTH_SHORT -// ).show() -// } -// showResetDialog.value = false -// }, -// colors = ButtonDefaults.textButtonColors( -// contentColor = MaterialTheme.colorScheme.error -// ) -// ) { -// Text( -// stringResource(R.string.reset), -// fontFamily = FontFamily(Font(R.font.sf_pro)), -// fontWeight = FontWeight.Medium -// ) -// } -// }, -// dismissButton = { -// TextButton( -// onClick = { showResetDialog.value = false } -// ) { -// Text( -// "Cancel", -// fontFamily = FontFamily(Font(R.font.sf_pro)), -// fontWeight = FontWeight.Medium -// ) -// } -// } -// ) -// } - - if (showIrkDialog.value) { - AlertDialog( - onDismissRequest = { showIrkDialog.value = false }, - title = { + if (uiState.showCameraDialog) { + AlertDialog(onDismissRequest = { viewModel.setShowCameraDialog(false) }, title = { + Text( + stringResource(R.string.set_custom_camera_package), + fontFamily = FontFamily(Font(R.font.sf_pro)), + fontWeight = FontWeight.Medium + ) + }, text = { + Column { Text( - stringResource(R.string.set_identity_resolving_key), + stringResource(R.string.enter_custom_camera_package), + fontFamily = FontFamily(Font(R.font.sf_pro)), + modifier = Modifier.padding(bottom = 8.dp) + ) + + OutlinedTextField( + value = uiState.cameraPackageValue, + onValueChange = { + viewModel.setCameraPackageValue(it) + viewModel.setCameraPackageError(null) + }, + modifier = Modifier.fillMaxWidth(), + isError = uiState.cameraPackageError != null, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Ascii, + capitalization = KeyboardCapitalization.None + ), + colors = OutlinedTextFieldDefaults.colors( + focusedBorderColor = if (isDarkTheme) Color(0xFF007AFF) else Color( + 0xFF3C6DF5 + ), + unfocusedBorderColor = if (isDarkTheme) Color.Gray else Color.LightGray + ), + supportingText = { + if (uiState.cameraPackageError != null) { + Text( + uiState.cameraPackageError ?: "", + color = MaterialTheme.colorScheme.error + ) + } + }, + label = { Text(stringResource(R.string.custom_camera_package)) }) + } + }, confirmButton = { + val successText = stringResource(R.string.custom_camera_package_set_success) + TextButton( + onClick = { + viewModel.saveCameraPackage() + Toast.makeText(context, successText, Toast.LENGTH_SHORT).show() + }) { + Text( + "Save", fontFamily = FontFamily(Font(R.font.sf_pro)), fontWeight = FontWeight.Medium ) - }, - text = { - Column { - Text( - stringResource(R.string.enter_irk_hex), - fontFamily = FontFamily(Font(R.font.sf_pro)), - modifier = Modifier.padding(bottom = 8.dp) - ) - - OutlinedTextField( - value = irkValue.value, - onValueChange = { - irkValue.value = it.lowercase().filter { char -> char.isDigit() || char in 'a'..'f' } - irkError.value = null - }, - modifier = Modifier.fillMaxWidth(), - isError = irkError.value != null, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Ascii, - capitalization = KeyboardCapitalization.None - ), - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = if (isDarkTheme) Color(0xFF007AFF) else Color(0xFF3C6DF5), - unfocusedBorderColor = if (isDarkTheme) Color.Gray else Color.LightGray - ), - supportingText = { - if (irkError.value != null) { - Text(stringResource(R.string.must_be_32_hex_chars), color = MaterialTheme.colorScheme.error) - } - }, - label = { Text(stringResource(R.string.irk_hex_value)) } - ) - } - }, - confirmButton = { - val successText = stringResource(R.string.irk_set_success) - val errorText = stringResource(R.string.error_converting_hex) - TextButton( - onClick = { - if (!validateHexInput(irkValue.value)) { - irkError.value = "Must be exactly 32 hex characters" - return@TextButton - } - - try { - val hexBytes = ByteArray(16) - for (i in 0 until 16) { - val hexByte = irkValue.value.substring(i * 2, i * 2 + 2) - hexBytes[i] = hexByte.toInt(16).toByte() - } - - val base64Value = Base64.encode(hexBytes) - sharedPreferences.edit { putString(AACPManager.Companion.ProximityKeyType.IRK.name, base64Value)} - - Toast.makeText(context, successText, Toast.LENGTH_SHORT).show() - showIrkDialog.value = false - } catch (e: Exception) { - irkError.value = errorText + " " + (e.message ?: "Unknown error") - } - } - ) { - Text( - "Save", - fontFamily = FontFamily(Font(R.font.sf_pro)), - fontWeight = FontWeight.Medium - ) - } - }, - dismissButton = { - TextButton( - onClick = { showIrkDialog.value = false } - ) { - Text( - "Cancel", - fontFamily = FontFamily(Font(R.font.sf_pro)), - fontWeight = FontWeight.Medium - ) - } } - ) - } - - if (showEncKeyDialog.value) { - AlertDialog( - onDismissRequest = { showEncKeyDialog.value = false }, - title = { + }, dismissButton = { + TextButton( + onClick = { viewModel.setShowCameraDialog(false) }) { Text( - stringResource(R.string.set_encryption_key), + "Cancel", fontFamily = FontFamily(Font(R.font.sf_pro)), fontWeight = FontWeight.Medium ) - }, - text = { - Column { - Text( - stringResource(R.string.enter_enc_key_hex), - fontFamily = FontFamily(Font(R.font.sf_pro)), - modifier = Modifier.padding(bottom = 8.dp) - ) - - OutlinedTextField( - value = encKeyValue.value, - onValueChange = { - encKeyValue.value = it.lowercase().filter { char -> char.isDigit() || char in 'a'..'f' } - encKeyError.value = null - }, - modifier = Modifier.fillMaxWidth(), - isError = encKeyError.value != null, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Ascii, - capitalization = KeyboardCapitalization.None - ), - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = if (isDarkTheme) Color(0xFF007AFF) else Color(0xFF3C6DF5), - unfocusedBorderColor = if (isDarkTheme) Color.Gray else Color.LightGray - ), - supportingText = { - if (encKeyError.value != null) { - Text(stringResource(R.string.must_be_32_hex_chars), color = MaterialTheme.colorScheme.error) - } - }, - label = { Text(stringResource(R.string.enc_key_hex_value)) } - ) - } - }, - confirmButton = { - val successText = stringResource(R.string.encryption_key_set_success) - val errorText = stringResource(R.string.error_converting_hex) - TextButton( - onClick = { - if (!validateHexInput(encKeyValue.value)) { - encKeyError.value = "Must be exactly 32 hex characters" - return@TextButton - } - - try { - val hexBytes = ByteArray(16) - for (i in 0 until 16) { - val hexByte = encKeyValue.value.substring(i * 2, i * 2 + 2) - hexBytes[i] = hexByte.toInt(16).toByte() - } - - val base64Value = Base64.encode(hexBytes) - sharedPreferences.edit { putString(AACPManager.Companion.ProximityKeyType.ENC_KEY.name, base64Value)} - - Toast.makeText(context, successText, Toast.LENGTH_SHORT).show() - showEncKeyDialog.value = false - } catch (e: Exception) { - encKeyError.value = errorText + " " + (e.message ?: "Unknown error") - } - } - ) { - Text( - "Save", - fontFamily = FontFamily(Font(R.font.sf_pro)), - fontWeight = FontWeight.Medium - ) - } - }, - dismissButton = { - TextButton( - onClick = { showEncKeyDialog.value = false } - ) { - Text( - "Cancel", - fontFamily = FontFamily(Font(R.font.sf_pro)), - fontWeight = FontWeight.Medium - ) - } } - ) - } - - if (showCameraDialog.value) { - AlertDialog( - onDismissRequest = { showCameraDialog.value = false }, - title = { - Text( - stringResource(R.string.set_custom_camera_package), - fontFamily = FontFamily(Font(R.font.sf_pro)), - fontWeight = FontWeight.Medium - ) - }, - text = { - Column { - Text( - stringResource(R.string.enter_custom_camera_package), - fontFamily = FontFamily(Font(R.font.sf_pro)), - modifier = Modifier.padding(bottom = 8.dp) - ) - - OutlinedTextField( - value = cameraPackageValue.value, - onValueChange = { - cameraPackageValue.value = it - cameraPackageError.value = null - }, - modifier = Modifier.fillMaxWidth(), - isError = cameraPackageError.value != null, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Ascii, - capitalization = KeyboardCapitalization.None - ), - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = if (isDarkTheme) Color(0xFF007AFF) else Color(0xFF3C6DF5), - unfocusedBorderColor = if (isDarkTheme) Color.Gray else Color.LightGray - ), - supportingText = { - if (cameraPackageError.value != null) { - Text(cameraPackageError.value!!, color = MaterialTheme.colorScheme.error) - } - }, - label = { Text(stringResource(R.string.custom_camera_package)) } - ) - } - }, - confirmButton = { - val successText = stringResource(R.string.custom_camera_package_set_success) - TextButton( - onClick = { - if (cameraPackageValue.value.isBlank()) { - sharedPreferences.edit { remove("custom_camera_package") } - Toast.makeText(context, successText, Toast.LENGTH_SHORT).show() - showCameraDialog.value = false - return@TextButton - } - - sharedPreferences.edit { putString("custom_camera_package", cameraPackageValue.value) } - Toast.makeText(context, successText, Toast.LENGTH_SHORT).show() - showCameraDialog.value = false - } - ) { - Text( - "Save", - fontFamily = FontFamily(Font(R.font.sf_pro)), - fontWeight = FontWeight.Medium - ) - } - }, - dismissButton = { - TextButton( - onClick = { showCameraDialog.value = false } - ) { - Text( - "Cancel", - fontFamily = FontFamily(Font(R.font.sf_pro)), - fontWeight = FontWeight.Medium - ) - } - } - ) + }) } } } diff --git a/android/app/src/main/java/me/kavishdevar/librepods/screens/CameraControlScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/CameraControlScreen.kt index 7ad0f29..4c670f5 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/screens/CameraControlScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/CameraControlScreen.kt @@ -18,114 +18,79 @@ package me.kavishdevar.librepods.screens -import android.annotation.SuppressLint +import android.accessibilityservice.AccessibilityServiceInfo import android.content.ComponentName import android.content.Context import android.content.Intent import android.provider.Settings import android.view.accessibility.AccessibilityManager -import android.accessibilityservice.AccessibilityServiceInfo -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import androidx.core.content.edit import com.kyant.backdrop.backdrops.layerBackdrop import com.kyant.backdrop.backdrops.rememberLayerBackdrop -import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch import me.kavishdevar.librepods.R import me.kavishdevar.librepods.composables.SelectItem -import me.kavishdevar.librepods.composables.StyledIconButton import me.kavishdevar.librepods.composables.StyledScaffold import me.kavishdevar.librepods.composables.StyledSelectList -import me.kavishdevar.librepods.composables.StyledSlider -import me.kavishdevar.librepods.services.ServiceManager import me.kavishdevar.librepods.services.AppListenerService -import me.kavishdevar.librepods.utils.AACPManager import me.kavishdevar.librepods.utils.AACPManager.Companion.StemPressType -import kotlin.io.encoding.ExperimentalEncodingApi +import me.kavishdevar.librepods.viewmodel.AirPodsViewModel -private var debounceJob: Job? = null - -@SuppressLint("DefaultLocale") -@ExperimentalHazeMaterialsApi -@OptIn(ExperimentalMaterial3Api::class, ExperimentalEncodingApi::class) @Composable -fun CameraControlScreen(navController: NavController) { - val isDarkTheme = isSystemInDarkTheme() +fun CameraControlScreen(viewModel: AirPodsViewModel) { val context = LocalContext.current - val sharedPreferences = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - - val service = ServiceManager.getService()!! - var currentCameraAction by remember { - mutableStateOf( - sharedPreferences.getString("camera_action", null)?.let { StemPressType.valueOf(it) } - ) - } + val currentCameraAction by viewModel.cameraAction.collectAsState() fun isAppListenerServiceEnabled(context: Context): Boolean { val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager - val enabledServices = am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK) + val enabledServices = + am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK) val serviceComponent = ComponentName(context, AppListenerService::class.java) - return enabledServices.any { it.resolveInfo.serviceInfo.packageName == serviceComponent.packageName && it.resolveInfo.serviceInfo.name == serviceComponent.className } + return enabledServices.any { + it.resolveInfo.serviceInfo.packageName == serviceComponent.packageName && + it.resolveInfo.serviceInfo.name == serviceComponent.className + } } - val cameraOptions = listOf( - SelectItem( - name = stringResource(R.string.off), - selected = currentCameraAction == null, - onClick = { - sharedPreferences.edit { remove("camera_action") } - currentCameraAction = null - } - ), - SelectItem( - name = stringResource(R.string.press_once), - selected = currentCameraAction == StemPressType.SINGLE_PRESS, - onClick = { - if (!isAppListenerServiceEnabled(context)) { - context.startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)) - } else { - sharedPreferences.edit { putString("camera_action", StemPressType.SINGLE_PRESS.name) } - currentCameraAction = StemPressType.SINGLE_PRESS - } - } - ), - SelectItem( - name = stringResource(R.string.press_and_hold_airpods), - selected = currentCameraAction == StemPressType.LONG_PRESS, - onClick = { - if (!isAppListenerServiceEnabled(context)) { - context.startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)) - } else { - sharedPreferences.edit { putString("camera_action", StemPressType.LONG_PRESS.name) } - currentCameraAction = StemPressType.LONG_PRESS - } - } + fun handleSelection(action: StemPressType?) { + if (action != null && !isAppListenerServiceEnabled(context)) { + context.startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)) + } else { + viewModel.setCameraAction(action) + } + } + + val cameraOptions = remember(currentCameraAction) { + listOf( + SelectItem( + name = "Off", + selected = currentCameraAction == null, + onClick = { handleSelection(null) } + ), + SelectItem( + name = "Press once", + selected = currentCameraAction == StemPressType.SINGLE_PRESS, + onClick = { handleSelection(StemPressType.SINGLE_PRESS) } + ), + SelectItem( + name = "Press and hold AirPods", + selected = currentCameraAction == StemPressType.LONG_PRESS, + onClick = { handleSelection(StemPressType.LONG_PRESS) } + ) ) - ) + } val backdrop = rememberLayerBackdrop() diff --git a/android/app/src/main/java/me/kavishdevar/librepods/screens/DebugScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/DebugScreen.kt index 401fc91..a802847 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/screens/DebugScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/DebugScreen.kt @@ -335,7 +335,6 @@ fun DebugScreen(navController: NavController) { expandedItems.value = emptySet() }, icon = "􀈑", - darkMode = isDarkTheme, backdrop = scaffoldBackdrop ) } diff --git a/android/app/src/main/java/me/kavishdevar/librepods/screens/HeadTrackingScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/HeadTrackingScreen.kt index f3c8416..f485495 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/screens/HeadTrackingScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/HeadTrackingScreen.kt @@ -23,10 +23,7 @@ package me.kavishdevar.librepods.screens -import android.content.Context -import android.os.Build import android.util.Log -import androidx.annotation.RequiresApi import androidx.compose.animation.AnimatedContent import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.core.tween @@ -83,7 +80,6 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.rememberTextMeasurer 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 com.kyant.backdrop.backdrops.layerBackdrop @@ -100,6 +96,7 @@ import me.kavishdevar.librepods.composables.StyledScaffold import me.kavishdevar.librepods.composables.StyledToggle import me.kavishdevar.librepods.services.ServiceManager import me.kavishdevar.librepods.utils.HeadTracking +import me.kavishdevar.librepods.viewmodel.AirPodsViewModel import kotlin.io.encoding.ExperimentalEncodingApi import kotlin.math.abs import kotlin.math.cos @@ -107,14 +104,14 @@ import kotlin.math.sin import kotlin.random.Random @ExperimentalHazeMaterialsApi -@RequiresApi(Build.VERSION_CODES.Q) @OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class) @Composable -fun HeadTrackingScreen() { +fun HeadTrackingScreen(viewModel: AirPodsViewModel) { + val state by viewModel.uiState.collectAsState() DisposableEffect(Unit) { - ServiceManager.getService()?.startHeadTracking() + viewModel.startHeadTracking() onDispose { - ServiceManager.getService()?.stopHeadTracking() + viewModel.stopHeadTracking() } } val isDarkTheme = isSystemInDarkTheme() @@ -127,25 +124,22 @@ fun HeadTrackingScreen() { title = stringResource(R.string.head_tracking), actionButtons = listOf( { scaffoldBackdrop -> - var isActive by remember { mutableStateOf(ServiceManager.getService()?.isHeadTrackingActive == true) } StyledIconButton( onClick = { - if (ServiceManager.getService()?.isHeadTrackingActive == false) { - ServiceManager.getService()?.startHeadTracking() + if (!state.headTrackingActive) { + viewModel.startHeadTracking() Log.d("HeadTrackingScreen", "Head tracking started") } else { - ServiceManager.getService()?.stopHeadTracking() + viewModel.stopHeadTracking() Log.d("HeadTrackingScreen", "Head tracking stopped") } }, - icon = if (isActive) "􀊅" else "􀊃", - darkMode = isDarkTheme, + icon = if (state.headTrackingActive) "􀊅" else "􀊃", backdrop = scaffoldBackdrop ) } ), ) { spacerHeight, hazeState -> - val sharedPreferences = LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE) var gestureText by remember { mutableStateOf("") } val coroutineScope = rememberCoroutineScope() @@ -167,10 +161,37 @@ fun HeadTrackingScreen() { .verticalScroll(scrollState) ) { Spacer(modifier = Modifier.height(spacerHeight)) + + val context = LocalContext.current + + if (!state.isPremium) { + StyledButton( + onClick = { + viewModel.purchase(context) + }, + backdrop = rememberLayerBackdrop(), + modifier = Modifier.fillMaxWidth(), + maxScale = 0.05f, + tint = Color(0xFF916100) + ) { + Text( + stringResource(R.string.unlock_all_features), + style = TextStyle( + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + fontFamily = FontFamily(Font(R.font.sf_pro)), + color = textColor + ), + ) + } + Spacer(modifier = Modifier.height(8.dp)) + } + StyledToggle( label = "Head Gestures", - sharedPreferences = sharedPreferences, - sharedPreferenceKey = "head_gestures", + checked = state.headGesturesEnabled, + onCheckedChange = { viewModel.setHeadGesturesEnabled(it) }, + enabled = state.isPremium ) Spacer(modifier = Modifier.height(2.dp)) @@ -739,11 +760,3 @@ private fun AccelerationPlot() { } } } - -@ExperimentalHazeMaterialsApi -@RequiresApi(Build.VERSION_CODES.Q) -@Preview -@Composable -fun HeadTrackingScreenPreview() { - HeadTrackingScreen() -} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/screens/HearingAidAdjustmentsScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/HearingAidAdjustmentsScreen.kt index 6925fbb..8fe06b7 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/screens/HearingAidAdjustmentsScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/HearingAidAdjustmentsScreen.kt @@ -31,9 +31,10 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf @@ -41,7 +42,6 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.navigation.NavController import com.kyant.backdrop.backdrops.layerBackdrop import com.kyant.backdrop.backdrops.rememberLayerBackdrop import dev.chrisbanes.haze.HazeState @@ -59,6 +59,7 @@ import me.kavishdevar.librepods.utils.ATTHandles import me.kavishdevar.librepods.utils.HearingAidSettings import me.kavishdevar.librepods.utils.parseHearingAidSettingsResponse import me.kavishdevar.librepods.utils.sendHearingAidSettings +import me.kavishdevar.librepods.viewmodel.AirPodsViewModel import java.io.IOException import kotlin.io.encoding.ExperimentalEncodingApi @@ -69,13 +70,14 @@ private const val TAG = "HearingAidAdjustments" @ExperimentalHazeMaterialsApi @OptIn(ExperimentalMaterial3Api::class, ExperimentalEncodingApi::class) @Composable -fun HearingAidAdjustmentsScreen(@Suppress("unused") navController: NavController) { +fun HearingAidAdjustmentsScreen(viewModel: AirPodsViewModel) { isSystemInDarkTheme() val verticalScrollState = rememberScrollState() val hazeState = remember { HazeState() } val attManager = ServiceManager.getService()?.attManager ?: throw IllegalStateException("ATTManager not available") - val aacpManager = remember { ServiceManager.getService()?.aacpManager } + val state by viewModel.uiState.collectAsState() + val backdrop = rememberLayerBackdrop() StyledScaffold( title = stringResource(R.string.adjustments) @@ -125,25 +127,6 @@ fun HearingAidAdjustmentsScreen(@Suppress("unused") navController: NavController ) } - val hearingAidEnabled = remember { - val aidStatus = aacpManager?.controlCommandStatusList?.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID } - val assistStatus = aacpManager?.controlCommandStatusList?.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG } - mutableStateOf((aidStatus?.value?.getOrNull(1) == 0x01.toByte()) && (assistStatus?.value?.getOrNull(0) == 0x01.toByte())) - } - - val hearingAidListener = remember { - object : AACPManager.ControlCommandListener { - override fun onControlCommandReceived(controlCommand: AACPManager.ControlCommand) { - if (controlCommand.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID.value || - controlCommand.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG.value) { - val aidStatus = aacpManager?.controlCommandStatusList?.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID } - val assistStatus = aacpManager?.controlCommandStatusList?.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG } - hearingAidEnabled.value = (aidStatus?.value?.getOrNull(1) == 0x01.toByte()) && (assistStatus?.value?.getOrNull(0) == 0x01.toByte()) - } - } - } - } - val hearingAidATTListener = remember { object : (ByteArray) -> Unit { override fun invoke(value: ByteArray) { @@ -165,19 +148,6 @@ fun HearingAidAdjustmentsScreen(@Suppress("unused") navController: NavController } } - LaunchedEffect(Unit) { - aacpManager?.registerControlCommandListener(AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID, hearingAidListener) - aacpManager?.registerControlCommandListener(AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG, hearingAidListener) - } - - DisposableEffect(Unit) { - onDispose { - aacpManager?.unregisterControlCommandListener(AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID, hearingAidListener) - aacpManager?.unregisterControlCommandListener(AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG, hearingAidListener) - attManager.unregisterListener(ATTHandles.HEARING_AID, hearingAidATTListener) - } - } - LaunchedEffect(amplificationSliderValue.floatValue, balanceSliderValue.floatValue, toneSliderValue.floatValue, conversationBoostEnabled.value, ambientNoiseReductionSliderValue.floatValue, ownVoiceAmplification.floatValue, initialLoadComplete.value, initialReadSucceeded.value) { if (!initialLoadComplete.value) { Log.d(TAG, "Initial device load not complete - skipping send") @@ -256,7 +226,7 @@ fun HearingAidAdjustmentsScreen(@Suppress("unused") navController: NavController StyledSlider( label = stringResource(R.string.amplification), valueRange = -1f..1f, - mutableFloatState = amplificationSliderValue, + value = amplificationSliderValue.floatValue, onValueChange = { amplificationSliderValue.floatValue = it }, @@ -268,14 +238,15 @@ fun HearingAidAdjustmentsScreen(@Suppress("unused") navController: NavController StyledToggle( label = stringResource(R.string.swipe_to_control_amplification), - controlCommandIdentifier = AACPManager.Companion.ControlCommandIdentifiers.HPS_GAIN_SWIPE, + checked = state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.HPS_GAIN_SWIPE]?.getOrNull(0) == 0x01.toByte(), + onCheckedChange = { viewModel.setControlCommandBoolean(AACPManager.Companion.ControlCommandIdentifiers.HPS_GAIN_SWIPE, it) }, description = stringResource(R.string.swipe_amplification_description) ) StyledSlider( label = stringResource(R.string.balance), valueRange = -1f..1f, - mutableFloatState = balanceSliderValue, + value = balanceSliderValue.floatValue, onValueChange = { balanceSliderValue.floatValue = it }, @@ -288,7 +259,7 @@ fun HearingAidAdjustmentsScreen(@Suppress("unused") navController: NavController StyledSlider( label = stringResource(R.string.tone), valueRange = -1f..1f, - mutableFloatState = toneSliderValue, + value = toneSliderValue.floatValue, onValueChange = { toneSliderValue.floatValue = it }, @@ -300,7 +271,7 @@ fun HearingAidAdjustmentsScreen(@Suppress("unused") navController: NavController StyledSlider( label = stringResource(R.string.ambient_noise_reduction), valueRange = 0f..1f, - mutableFloatState = ambientNoiseReductionSliderValue, + value = ambientNoiseReductionSliderValue.floatValue, onValueChange = { ambientNoiseReductionSliderValue.floatValue = it }, @@ -311,7 +282,8 @@ fun HearingAidAdjustmentsScreen(@Suppress("unused") navController: NavController StyledToggle( label = stringResource(R.string.conversation_boost), - checkedState = conversationBoostEnabled, + checked = conversationBoostEnabled.value, + onCheckedChange = { conversationBoostEnabled.value = it }, independent = true, description = stringResource(R.string.conversation_boost_description) ) diff --git a/android/app/src/main/java/me/kavishdevar/librepods/screens/HearingAidScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/HearingAidScreen.kt index b956d96..78c9918 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/screens/HearingAidScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/HearingAidScreen.kt @@ -37,8 +37,9 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier @@ -65,11 +66,11 @@ import me.kavishdevar.librepods.composables.ConfirmationDialog import me.kavishdevar.librepods.composables.NavigationButton import me.kavishdevar.librepods.composables.StyledScaffold import me.kavishdevar.librepods.composables.StyledToggle -import me.kavishdevar.librepods.services.ServiceManager import me.kavishdevar.librepods.utils.AACPManager import me.kavishdevar.librepods.utils.ATTHandles import me.kavishdevar.librepods.utils.parseTransparencySettingsResponse import me.kavishdevar.librepods.utils.sendTransparencySettings +import me.kavishdevar.librepods.viewmodel.AirPodsViewModel import kotlin.io.encoding.ExperimentalEncodingApi private const val TAG = "AccessibilitySettings" @@ -78,23 +79,22 @@ private const val TAG = "AccessibilitySettings" @ExperimentalHazeMaterialsApi @OptIn(ExperimentalMaterial3Api::class, ExperimentalEncodingApi::class) @Composable -fun HearingAidScreen(navController: NavController) { +fun HearingAidScreen(viewModel: AirPodsViewModel, navController: NavController) { val isDarkTheme = isSystemInDarkTheme() val textColor = if (isDarkTheme) Color.White else Color.Black val verticalScrollState = rememberScrollState() val snackbarHostState = remember { SnackbarHostState() } - val attManager = ServiceManager.getService()?.attManager ?: return - - val aacpManager = remember { ServiceManager.getService()?.aacpManager } val showDialog = remember { mutableStateOf(false) } val backdrop = rememberLayerBackdrop() val initialLoad = remember { mutableStateOf(true) } + val state by viewModel.uiState.collectAsState() + val hearingAidEnabled = remember { - val aidStatus = aacpManager?.controlCommandStatusList?.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID } - val assistStatus = aacpManager?.controlCommandStatusList?.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG } - mutableStateOf((aidStatus?.value?.getOrNull(1) == 0x01.toByte()) && (assistStatus?.value?.getOrNull(0) == 0x01.toByte())) + val aidStatus = state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID] + val assistStatus = state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG] + mutableStateOf((aidStatus?.getOrNull(1) == 0x01.toByte()) && (assistStatus?.getOrNull(0) == 0x01.toByte())) } val hazeStateS = remember { mutableStateOf(HazeState()) } // dont question this. i could possibly use something other than initializing it with an empty state and then replacing it with the the one provided by the scaffold @@ -115,41 +115,16 @@ fun HearingAidScreen(navController: NavController) { hazeStateS.value = hazeState Spacer(modifier = Modifier.height(spacerHeight)) - val hearingAidListener = remember { - object : AACPManager.ControlCommandListener { - override fun onControlCommandReceived(controlCommand: AACPManager.ControlCommand) { - if (controlCommand.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID.value || - controlCommand.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG.value) { - val aidStatus = aacpManager?.controlCommandStatusList?.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID } - val assistStatus = aacpManager?.controlCommandStatusList?.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG } - hearingAidEnabled.value = (aidStatus?.value?.getOrNull(1) == 0x01.toByte()) && (assistStatus?.value?.getOrNull(0) == 0x01.toByte()) - } - } - } - } - // val mediaAssistEnabled = remember { mutableStateOf(false) } // val adjustMediaEnabled = remember { mutableStateOf(false) } // val adjustPhoneEnabled = remember { mutableStateOf(false) } - LaunchedEffect(Unit) { - aacpManager?.registerControlCommandListener(AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID, hearingAidListener) - aacpManager?.registerControlCommandListener(AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG, hearingAidListener) - } - - DisposableEffect(Unit) { - onDispose { - aacpManager?.unregisterControlCommandListener(AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID, hearingAidListener) - aacpManager?.unregisterControlCommandListener(AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG, hearingAidListener) - } - } - LaunchedEffect(hearingAidEnabled.value) { if (hearingAidEnabled.value && !initialLoad.value) { showDialog.value = true } else if (!hearingAidEnabled.value && !initialLoad.value) { - aacpManager?.sendControlCommand(AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID.value, byteArrayOf(0x01, 0x02)) - aacpManager?.sendControlCommand(AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG.value, 0x02.toByte()) + viewModel.setControlCommandValue(AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID, byteArrayOf(0x01, 0x02)) + viewModel.setControlCommandByte(AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG, 0x02.toByte()) hearingAidEnabled.value = false } initialLoad.value = false @@ -186,7 +161,8 @@ fun HearingAidScreen(navController: NavController) { ) { StyledToggle( label = stringResource(R.string.hearing_aid), - checkedState = hearingAidEnabled, + checked = hearingAidEnabled.value, + onCheckedChange = { hearingAidEnabled.value = it }, independent = false ) HorizontalDivider( @@ -269,20 +245,24 @@ fun HearingAidScreen(navController: NavController) { dismissText = "Cancel", onConfirm = { showDialog.value = false - val enrolled = aacpManager?.controlCommandStatusList?.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID }?.value?.getOrNull(0) == 0x01.toByte() + val enrolled = state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID]?.getOrNull(0) == 0x01.toByte() if (!enrolled) { - aacpManager?.sendControlCommand(AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID.value, byteArrayOf(0x01, 0x01)) + viewModel.setControlCommandValue(AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID, byteArrayOf(0x01, 0x01)) } else { - aacpManager.sendControlCommand(AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID.value, byteArrayOf(0x01, 0x01)) + viewModel.setControlCommandValue(AACPManager.Companion.ControlCommandIdentifiers.HEARING_AID, byteArrayOf(0x01, 0x01)) } - aacpManager?.sendControlCommand(AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG.value, 0x01.toByte()) + viewModel.setControlCommandByte(AACPManager.Companion.ControlCommandIdentifiers.HEARING_ASSIST_CONFIG, 0x01.toByte()) hearingAidEnabled.value = true CoroutineScope(Dispatchers.IO).launch { try { - val data = attManager.read(ATTHandles.TRANSPARENCY) + val data = viewModel.getATTCharacteristicValue(ATTHandles.TRANSPARENCY) ?: byteArrayOf() + if (data.isEmpty()) { + Log.w(TAG, "read failed") + return@launch + } val parsed = parseTransparencySettingsResponse(data) val disabledSettings = parsed.copy(enabled = false) - sendTransparencySettings(attManager, disabledSettings) + sendTransparencySettings(viewModel::setATTCharacteristicValue, disabledSettings) } catch (e: Exception) { Log.e(TAG, "Error disabling transparency: ${e.message}") } diff --git a/android/app/src/main/java/me/kavishdevar/librepods/screens/HearingProtectionScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/HearingProtectionScreen.kt index bffac6d..9e6cf28 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/screens/HearingProtectionScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/HearingProtectionScreen.kt @@ -18,48 +18,31 @@ package me.kavishdevar.librepods.screens -import android.annotation.SuppressLint -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.navigation.NavController import com.kyant.backdrop.backdrops.layerBackdrop import com.kyant.backdrop.backdrops.rememberLayerBackdrop -import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi -import kotlinx.coroutines.Job +import me.kavishdevar.librepods.BuildConfig import me.kavishdevar.librepods.R import me.kavishdevar.librepods.composables.StyledScaffold import me.kavishdevar.librepods.composables.StyledToggle -import me.kavishdevar.librepods.services.ServiceManager import me.kavishdevar.librepods.utils.AACPManager import me.kavishdevar.librepods.utils.ATTHandles -import kotlin.io.encoding.ExperimentalEncodingApi +import me.kavishdevar.librepods.viewmodel.AirPodsViewModel -private var debounceJob: Job? = null - -@SuppressLint("DefaultLocale") -@ExperimentalHazeMaterialsApi -@OptIn(ExperimentalMaterial3Api::class, ExperimentalEncodingApi::class) @Composable -fun HearingProtectionScreen(navController: NavController) { - val isDarkTheme = isSystemInDarkTheme() - val service = ServiceManager.getService() - if (service == null) return - - val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF) - val textColor = if (isDarkTheme) Color.White else Color.Black - +fun HearingProtectionScreen(viewModel: AirPodsViewModel) { val backdrop = rememberLayerBackdrop() - + val state by viewModel.uiState.collectAsState() StyledScaffold( title = stringResource(R.string.hearing_protection), ) { spacerHeight -> @@ -71,20 +54,36 @@ fun HearingProtectionScreen(navController: NavController) { ) { Spacer(modifier = Modifier.height(spacerHeight)) - StyledToggle( - title = stringResource(R.string.environmental_noise), - label = stringResource(R.string.loud_sound_reduction), - description = stringResource(R.string.loud_sound_reduction_description), - attHandle = ATTHandles.LOUD_SOUND_REDUCTION - ) + if (BuildConfig.FLAVOR == "xposed") { + StyledToggle( + title = stringResource(R.string.environmental_noise), + label = stringResource(R.string.loud_sound_reduction), + description = stringResource(R.string.loud_sound_reduction_description), + checked = viewModel.getATTCharacteristicValue(ATTHandles.LOUD_SOUND_REDUCTION) + ?.get(0)?.toInt() == 1, + onCheckedChange = { + viewModel.setATTCharacteristicValue( + ATTHandles.LOUD_SOUND_REDUCTION, + byteArrayOf(if (it) 1.toByte() else 0.toByte()) + ) + } +// attHandle = ATTHandles.LOUD_SOUND_REDUCTION + ) - Spacer(modifier = Modifier.height(12.dp)) + Spacer(modifier = Modifier.height(12.dp)) + } StyledToggle( title = stringResource(R.string.workspace_use), label = stringResource(R.string.ppe), description = stringResource(R.string.workspace_use_description), - controlCommandIdentifier = AACPManager.Companion.ControlCommandIdentifiers.PPE_TOGGLE_CONFIG - ) + checked = state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.PPE_TOGGLE_CONFIG]?.getOrNull( + 0 + )?.toInt() == 1, + onCheckedChange = { + viewModel.setControlCommandBoolean( + AACPManager.Companion.ControlCommandIdentifiers.PPE_TOGGLE_CONFIG, it + ) + }) } } -} \ No newline at end of file +} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/screens/PressAndHoldSettingsScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/PressAndHoldSettingsScreen.kt index cc20647..096631c 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/screens/PressAndHoldSettingsScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/PressAndHoldSettingsScreen.kt @@ -22,37 +22,25 @@ package me.kavishdevar.librepods.screens import android.content.Context import android.util.Log -import androidx.compose.animation.animateColorAsState -import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.animation.core.tween -import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.wrapContentWidth -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.Font @@ -61,59 +49,38 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.core.content.edit -import androidx.navigation.NavController import com.kyant.backdrop.backdrops.layerBackdrop import com.kyant.backdrop.backdrops.rememberLayerBackdrop import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi import me.kavishdevar.librepods.R import me.kavishdevar.librepods.composables.SelectItem -import me.kavishdevar.librepods.composables.StyledIconButton +import me.kavishdevar.librepods.composables.StyledButton import me.kavishdevar.librepods.composables.StyledScaffold import me.kavishdevar.librepods.composables.StyledSelectList import me.kavishdevar.librepods.constants.StemAction import me.kavishdevar.librepods.services.ServiceManager import me.kavishdevar.librepods.utils.AACPManager +import me.kavishdevar.librepods.viewmodel.AirPodsViewModel import kotlin.experimental.and import kotlin.io.encoding.ExperimentalEncodingApi -@Composable -fun RightDivider() { - HorizontalDivider( - thickness = 1.dp, - color = Color(0x40888888), - modifier = Modifier - .padding(start = 72.dp, end = 20.dp) - ) -} - -@Composable -fun RightDividerNoIcon() { - HorizontalDivider( - thickness = 1.dp, - color = Color(0x40888888), - modifier = Modifier - .padding(start = 20.dp, end = 20.dp) - ) -} - @ExperimentalHazeMaterialsApi @OptIn(ExperimentalMaterial3Api::class) @Composable -fun LongPress(navController: NavController, name: String) { +fun LongPress(viewModel: AirPodsViewModel, name: String) { val isDarkTheme = isSystemInDarkTheme() val textColor = if (isDarkTheme) Color.White else Color.Black - val modesByte = ServiceManager.getService()!!.aacpManager.controlCommandStatusList.find { - it.identifier == AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE_CONFIGS - }?.value?.takeIf { it.isNotEmpty() }?.get(0) + val state by viewModel.uiState.collectAsState() + + val modesByte = state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE_CONFIGS]?.get(0) ?: 0 + + Log.d("PressAndHoldSettingsScreen", "Current modes state: ${modesByte.toString(2)}") + Log.d("PressAndHoldSettingsScreen", "Off mode: ${(modesByte and 0x01) != 0.toByte()}") + Log.d("PressAndHoldSettingsScreen", "Transparency mode: ${(modesByte and 0x04) != 0.toByte()}") + Log.d("PressAndHoldSettingsScreen", "Noise Cancellation mode: ${(modesByte and 0x02) != 0.toByte()}") + Log.d("PressAndHoldSettingsScreen", "Adaptive mode: ${(modesByte and 0x08) != 0.toByte()}") - if (modesByte != null) { - Log.d("PressAndHoldSettingsScreen", "Current modes state: ${modesByte.toString(2)}") - Log.d("PressAndHoldSettingsScreen", "Off mode: ${(modesByte and 0x01) != 0.toByte()}") - Log.d("PressAndHoldSettingsScreen", "Transparency mode: ${(modesByte and 0x04) != 0.toByte()}") - Log.d("PressAndHoldSettingsScreen", "Noise Cancellation mode: ${(modesByte and 0x02) != 0.toByte()}") - Log.d("PressAndHoldSettingsScreen", "Adaptive mode: ${(modesByte and 0x08) != 0.toByte()}") - } val context = LocalContext.current val sharedPreferences = context.getSharedPreferences("settings", Context.MODE_PRIVATE) val prefKey = if (name.lowercase() == "left") "left_long_press_action" else "right_long_press_action" @@ -124,9 +91,8 @@ fun LongPress(navController: NavController, name: String) { StyledScaffold( title = name ) { spacerHeight -> - val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF) Column ( - modifier = Modifier + modifier = Modifier .layerBackdrop(backdrop) .fillMaxSize() .padding(top = 8.dp) @@ -148,11 +114,36 @@ fun LongPress(navController: NavController, name: String) { onClick = { longPressAction = StemAction.DIGITAL_ASSISTANT sharedPreferences.edit { putString(prefKey, StemAction.DIGITAL_ASSISTANT.name) } - } + }, + enabled = state.isPremium ) ) StyledSelectList(items = actionItems) + if (!state.isPremium) { + Spacer(modifier = Modifier.height(24.dp)) + StyledButton( + onClick = { + viewModel.purchase(context) + }, + backdrop = rememberLayerBackdrop(), + modifier = Modifier.fillMaxWidth(), + maxScale = 0.05f, + tint = Color(0xFF916100) + ) { + Text( + stringResource(R.string.unlock_all_features), + style = TextStyle( + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + fontFamily = FontFamily(Font(R.font.sf_pro)), + color = textColor + ), + ) + } + Spacer(modifier = Modifier.height(8.dp)) + } + if (longPressAction == StemAction.CYCLE_NOISE_CONTROL_MODES) { Spacer(modifier = Modifier.height(32.dp)) Text( @@ -176,10 +167,11 @@ fun LongPress(navController: NavController, name: String) { val allowOff = offListeningModeValue == 1.toByte() Log.d("PressAndHoldSettingsScreen", "Allow Off option: $allowOff") - val initialByte = ServiceManager.getService()!!.aacpManager.controlCommandStatusList.find { - it.identifier == AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE_CONFIGS - }?.value?.takeIf { it.isNotEmpty() }?.get(0)?.toInt() ?: sharedPreferences.getInt("long_press_byte", 0b0101) - var currentByte by remember { mutableStateOf(initialByte) } + val initialByte = state.controlStates[AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE_CONFIGS] + ?.get(0)?.toInt() + ?: sharedPreferences.getInt("long_press_byte", 0b0101) + + var currentByte by remember { mutableIntStateOf(initialByte) } val listeningModeItems = mutableListOf() if (allowOff) { @@ -197,8 +189,8 @@ fun LongPress(navController: NavController, name: String) { } else { currentByte or bit } - ServiceManager.getService()!!.aacpManager.sendControlCommand( - AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE_CONFIGS.value, + viewModel.setControlCommandByte( + AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE_CONFIGS, newValue.toByte() ) sharedPreferences.edit { @@ -223,8 +215,8 @@ fun LongPress(navController: NavController, name: String) { } else { currentByte or bit } - ServiceManager.getService()!!.aacpManager.sendControlCommand( - AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE_CONFIGS.value, + viewModel.setControlCommandByte( + AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE_CONFIGS, newValue.toByte() ) sharedPreferences.edit { @@ -246,8 +238,8 @@ fun LongPress(navController: NavController, name: String) { } else { currentByte or bit } - ServiceManager.getService()!!.aacpManager.sendControlCommand( - AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE_CONFIGS.value, + viewModel.setControlCommandByte( + AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE_CONFIGS, newValue.toByte() ) sharedPreferences.edit { @@ -269,8 +261,8 @@ fun LongPress(navController: NavController, name: String) { } else { currentByte or bit } - ServiceManager.getService()!!.aacpManager.sendControlCommand( - AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE_CONFIGS.value, + viewModel.setControlCommandByte( + AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE_CONFIGS, newValue.toByte() ) sharedPreferences.edit { @@ -296,9 +288,7 @@ fun LongPress(navController: NavController, name: String) { } } } - Log.d("PressAndHoldSettingsScreen", "Current byte: ${ServiceManager.getService()!!.aacpManager.controlCommandStatusList.find { - it.identifier == AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE_CONFIGS - }?.value?.takeIf { it.isNotEmpty() }?.get(0)?.toString(2)}") + Log.d("PressAndHoldSettingsScreen", "Current byte: ${modesByte.toString(2)}") } fun countEnabledModes(byteValue: Int): Int { diff --git a/android/app/src/main/java/me/kavishdevar/librepods/screens/RenameScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/RenameScreen.kt index 95d412e..829bdd6 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/screens/RenameScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/RenameScreen.kt @@ -53,26 +53,22 @@ 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.input.TextFieldValue -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.core.content.edit -import androidx.navigation.NavController import com.kyant.backdrop.backdrops.layerBackdrop import com.kyant.backdrop.backdrops.rememberLayerBackdrop import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi import me.kavishdevar.librepods.R -import me.kavishdevar.librepods.composables.StyledIconButton import me.kavishdevar.librepods.composables.StyledScaffold -import me.kavishdevar.librepods.services.ServiceManager +import me.kavishdevar.librepods.viewmodel.AirPodsViewModel import kotlin.io.encoding.ExperimentalEncodingApi @OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class) @Composable -fun RenameScreen(navController: NavController) { +fun RenameScreen(viewModel: AirPodsViewModel) { val sharedPreferences = LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE) - val isDarkTheme = isSystemInDarkTheme() val name = remember { mutableStateOf(TextFieldValue(sharedPreferences.getString("name", "") ?: "")) } val focusRequester = remember { FocusRequester() } val keyboardController = LocalSoftwareKeyboardController.current @@ -115,7 +111,7 @@ fun RenameScreen(navController: NavController) { onValueChange = { name.value = it sharedPreferences.edit {putString("name", it.text)} - ServiceManager.getService()?.setName(it.text) + viewModel.setName(it.text) }, textStyle = TextStyle( fontSize = 16.sp, @@ -159,9 +155,3 @@ fun RenameScreen(navController: NavController) { } } } - -@Preview -@Composable -fun RenameScreenPreview() { - RenameScreen(navController = NavController(LocalContext.current)) -} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/screens/TransparencySettingsScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/TransparencySettingsScreen.kt index 8c28ba6..06f2614 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/screens/TransparencySettingsScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/TransparencySettingsScreen.kt @@ -18,6 +18,7 @@ package me.kavishdevar.librepods.screens +// import me.kavishdevar.librepods.utils.RadareOffsetFinder import android.annotation.SuppressLint import android.util.Log import androidx.compose.foundation.background @@ -43,6 +44,8 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf @@ -58,23 +61,22 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.navigation.NavController import com.kyant.backdrop.backdrops.layerBackdrop import com.kyant.backdrop.backdrops.rememberLayerBackdrop import dev.chrisbanes.haze.hazeSource import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi import kotlinx.coroutines.delay +import me.kavishdevar.librepods.BuildConfig import me.kavishdevar.librepods.R -import me.kavishdevar.librepods.composables.StyledIconButton import me.kavishdevar.librepods.composables.StyledScaffold import me.kavishdevar.librepods.composables.StyledSlider import me.kavishdevar.librepods.composables.StyledToggle import me.kavishdevar.librepods.services.ServiceManager import me.kavishdevar.librepods.utils.ATTHandles -// import me.kavishdevar.librepods.utils.RadareOffsetFinder import me.kavishdevar.librepods.utils.TransparencySettings import me.kavishdevar.librepods.utils.parseTransparencySettingsResponse import me.kavishdevar.librepods.utils.sendTransparencySettings +import me.kavishdevar.librepods.viewmodel.AirPodsViewModel import java.io.IOException import kotlin.io.encoding.ExperimentalEncodingApi @@ -84,14 +86,12 @@ private const val TAG = "TransparencySettings" @ExperimentalHazeMaterialsApi @OptIn(ExperimentalMaterial3Api::class, ExperimentalEncodingApi::class) @Composable -fun TransparencySettingsScreen(navController: NavController) { +fun TransparencySettingsScreen(viewModel: AirPodsViewModel) { val isDarkTheme = isSystemInDarkTheme() val textColor = if (isDarkTheme) Color.White else Color.Black val verticalScrollState = rememberScrollState() + val attManager = ServiceManager.getService()?.attManager ?: return - val aacpManager = remember { ServiceManager.getService()?.aacpManager } - val isSdpOffsetAvailable = remember { mutableStateOf(false) } // always available rn, for testing without radare -// remember { mutableStateOf(RadareOffsetFinder.isSdpOffsetAvailable()) } val trackColor = if (isDarkTheme) Color(0xFFB3B3B3) else Color(0xFF929491) val activeTrackColor = if (isDarkTheme) Color(0xFF007AFF) else Color(0xFF3C6DF5) @@ -99,6 +99,9 @@ fun TransparencySettingsScreen(navController: NavController) { val backdrop = rememberLayerBackdrop() + + val state by viewModel.uiState.collectAsState() + StyledScaffold( title = stringResource(R.string.customize_transparency_mode) ){ spacerHeight, hazeState -> @@ -205,7 +208,7 @@ fun TransparencySettingsScreen(navController: NavController) { balance = balanceSliderValue.floatValue ) Log.d("TransparencySettings", "Updated settings: ${transparencySettings.value}") - sendTransparencySettings(attManager, transparencySettings.value) + sendTransparencySettings(viewModel::setATTCharacteristicValue, transparencySettings.value) } DisposableEffect(Unit) { @@ -222,18 +225,14 @@ fun TransparencySettingsScreen(navController: NavController) { // If we have an AACP manager, prefer its EQ data to populate EQ controls first try { - if (aacpManager != null) { - Log.d(TAG, "Found AACPManager, reading cached EQ data") - val aacpEQ = aacpManager.eqData - if (aacpEQ.isNotEmpty()) { - eq.value = aacpEQ.copyOf() - phoneMediaEQ.value = aacpEQ.copyOf() - Log.d(TAG, "Populated EQ from AACPManager: ${aacpEQ.toList()}") - } else { - Log.d(TAG, "AACPManager EQ data empty") - } + Log.d(TAG, "Found AACPManager, reading cached EQ data") + val aacpEQ = state.eqData + if (aacpEQ.isNotEmpty()) { + eq.value = aacpEQ.copyOf() + phoneMediaEQ.value = aacpEQ.copyOf() + Log.d(TAG, "Populated EQ from AACPManager: ${aacpEQ.toList()}") } else { - Log.d(TAG, "No AACPManager available") + Log.d(TAG, "AACPManager EQ data empty") } } catch (e: Exception) { Log.w(TAG, "Error reading EQ from AACPManager: ${e.message}") @@ -277,18 +276,19 @@ fun TransparencySettingsScreen(navController: NavController) { } // Only show transparency mode section if SDP offset is available - if (isSdpOffsetAvailable.value) { + if (BuildConfig.FLAVOR == "xposed") { StyledToggle( label = stringResource(R.string.transparency_mode), - checkedState = enabled, + checked = enabled.value, independent = true, - description = stringResource(R.string.customize_transparency_mode_description) + description = stringResource(R.string.customize_transparency_mode_description), + onCheckedChange = { enabled.value = it } ) Spacer(modifier = Modifier.height(4.dp)) StyledSlider( label = stringResource(R.string.amplification), valueRange = -1f..1f, - mutableFloatState = amplificationSliderValue, + value = amplificationSliderValue.floatValue, onValueChange = { amplificationSliderValue.floatValue = it }, @@ -300,7 +300,7 @@ fun TransparencySettingsScreen(navController: NavController) { StyledSlider( label = stringResource(R.string.balance), valueRange = -1f..1f, - mutableFloatState = balanceSliderValue, + value = balanceSliderValue.floatValue, onValueChange = { balanceSliderValue.floatValue = it }, @@ -313,7 +313,7 @@ fun TransparencySettingsScreen(navController: NavController) { StyledSlider( label = stringResource(R.string.tone), valueRange = -1f..1f, - mutableFloatState = toneSliderValue, + value = toneSliderValue.floatValue, onValueChange = { toneSliderValue.floatValue = it }, @@ -325,7 +325,7 @@ fun TransparencySettingsScreen(navController: NavController) { StyledSlider( label = stringResource(R.string.ambient_noise_reduction), valueRange = 0f..1f, - mutableFloatState = ambientNoiseReductionSliderValue, + value = ambientNoiseReductionSliderValue.floatValue, onValueChange = { ambientNoiseReductionSliderValue.floatValue = it }, @@ -336,14 +336,12 @@ fun TransparencySettingsScreen(navController: NavController) { StyledToggle( label = stringResource(R.string.conversation_boost), - checkedState = conversationBoostEnabled, + checked = conversationBoostEnabled.value, independent = true, - description = stringResource(R.string.conversation_boost_description) + description = stringResource(R.string.conversation_boost_description), + onCheckedChange = { conversationBoostEnabled.value = it } ) - } - // Only show transparency mode EQ section if SDP offset is available - if (isSdpOffsetAvailable.value) { Text( text = stringResource(R.string.equalizer), style = TextStyle( diff --git a/android/app/src/main/java/me/kavishdevar/librepods/screens/UpdateHearingTestScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/UpdateHearingTestScreen.kt index 89e0791..00bcbdb 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/screens/UpdateHearingTestScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/UpdateHearingTestScreen.kt @@ -55,7 +55,6 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.navigation.NavController import com.kyant.backdrop.backdrops.layerBackdrop import com.kyant.backdrop.backdrops.rememberLayerBackdrop import dev.chrisbanes.haze.hazeSource @@ -75,11 +74,8 @@ import kotlin.io.encoding.ExperimentalEncodingApi private var debounceJob: MutableState = mutableStateOf(null) private const val TAG = "HearingAidAdjustments" -@SuppressLint("DefaultLocale") -@ExperimentalHazeMaterialsApi -@OptIn(ExperimentalMaterial3Api::class, ExperimentalEncodingApi::class) @Composable -fun UpdateHearingTestScreen(@Suppress("unused") navController: NavController) { +fun UpdateHearingTestScreen() { val verticalScrollState = rememberScrollState() val attManager = ServiceManager.getService()?.attManager if (attManager == null) { @@ -138,17 +134,17 @@ fun UpdateHearingTestScreen(@Suppress("unused") navController: NavController) { HearingAidSettings( leftEQ = leftEQ.value, rightEQ = rightEQ.value, - leftAmplification = leftAmplification.value, - rightAmplification = rightAmplification.value, - leftTone = tone.value, - rightTone = tone.value, + leftAmplification = leftAmplification.floatValue, + rightAmplification = rightAmplification.floatValue, + leftTone = tone.floatValue, + rightTone = tone.floatValue, leftConversationBoost = conversationBoostEnabled.value, rightConversationBoost = conversationBoostEnabled.value, - leftAmbientNoiseReduction = ambientNoiseReduction.value, - rightAmbientNoiseReduction = ambientNoiseReduction.value, - netAmplification = leftAmplification.value + rightAmplification.value / 2, - balance = 0.5f + (rightAmplification.value - leftAmplification.value) / 2, - ownVoiceAmplification = ownVoiceAmplification.value + leftAmbientNoiseReduction = ambientNoiseReduction.floatValue, + rightAmbientNoiseReduction = ambientNoiseReduction.floatValue, + netAmplification = leftAmplification.floatValue + rightAmplification.floatValue / 2, + balance = 0.5f + (rightAmplification.floatValue - leftAmplification.floatValue) / 2, + ownVoiceAmplification = ownVoiceAmplification.floatValue ) ) } @@ -161,11 +157,11 @@ fun UpdateHearingTestScreen(@Suppress("unused") navController: NavController) { leftEQ.value = parsed.leftEQ.copyOf() rightEQ.value = parsed.rightEQ.copyOf() conversationBoostEnabled.value = parsed.leftConversationBoost - tone.value = parsed.leftTone - ambientNoiseReduction.value = parsed.leftAmbientNoiseReduction - ownVoiceAmplification.value = parsed.ownVoiceAmplification - leftAmplification.value = parsed.leftAmplification - rightAmplification.value = parsed.rightAmplification + tone.floatValue = parsed.leftTone + ambientNoiseReduction.floatValue = parsed.leftAmbientNoiseReduction + ownVoiceAmplification.floatValue = parsed.ownVoiceAmplification + leftAmplification.floatValue = parsed.leftAmplification + rightAmplification.floatValue = parsed.rightAmplification Log.d(TAG, "Updated hearing aid settings from notification") } else { Log.w(TAG, "Failed to parse hearing aid settings from notification") @@ -181,31 +177,45 @@ fun UpdateHearingTestScreen(@Suppress("unused") navController: NavController) { } } - LaunchedEffect(leftEQ.value, rightEQ.value, conversationBoostEnabled.value, initialLoadComplete.value, initialReadSucceeded.value, leftAmplification.value, rightAmplification.value, tone.value, ambientNoiseReduction.value, ownVoiceAmplification.value) { + LaunchedEffect( + leftEQ.value, + rightEQ.value, + conversationBoostEnabled.value, + initialLoadComplete.value, + initialReadSucceeded.value, + leftAmplification.floatValue, + rightAmplification.floatValue, + tone.floatValue, + ambientNoiseReduction.floatValue, + ownVoiceAmplification.floatValue + ) { if (!initialLoadComplete.value) { Log.d(TAG, "Initial device load not complete - skipping send") return@LaunchedEffect } if (!initialReadSucceeded.value) { - Log.d(TAG, "Initial device read not successful yet - skipping send until read succeeds") + Log.d( + TAG, + "Initial device read not successful yet - skipping send until read succeeds" + ) return@LaunchedEffect } hearingAidSettings.value = HearingAidSettings( leftEQ = leftEQ.value, rightEQ = rightEQ.value, - leftAmplification = leftAmplification.value, - rightAmplification = rightAmplification.value, - leftTone = tone.value, - rightTone = tone.value, + leftAmplification = leftAmplification.floatValue, + rightAmplification = rightAmplification.floatValue, + leftTone = tone.floatValue, + rightTone = tone.floatValue, leftConversationBoost = conversationBoostEnabled.value, rightConversationBoost = conversationBoostEnabled.value, - leftAmbientNoiseReduction = ambientNoiseReduction.value, - rightAmbientNoiseReduction = ambientNoiseReduction.value, - netAmplification = leftAmplification.value + rightAmplification.value / 2, - balance = 0.5f + (rightAmplification.value - leftAmplification.value) / 2, - ownVoiceAmplification = ownVoiceAmplification.value + leftAmbientNoiseReduction = ambientNoiseReduction.floatValue, + rightAmbientNoiseReduction = ambientNoiseReduction.floatValue, + netAmplification = leftAmplification.floatValue + rightAmplification.floatValue / 2, + balance = 0.5f + (rightAmplification.floatValue - leftAmplification.floatValue) / 2, + ownVoiceAmplification = ownVoiceAmplification.floatValue ) Log.d(TAG, "Updated settings: ${hearingAidSettings.value}") sendHearingAidSettings(attManager, hearingAidSettings.value, debounceJob) @@ -240,14 +250,17 @@ fun UpdateHearingTestScreen(@Suppress("unused") navController: NavController) { leftEQ.value = parsedSettings.leftEQ.copyOf() rightEQ.value = parsedSettings.rightEQ.copyOf() conversationBoostEnabled.value = parsedSettings.leftConversationBoost - tone.value = parsedSettings.leftTone - ambientNoiseReduction.value = parsedSettings.leftAmbientNoiseReduction - ownVoiceAmplification.value = parsedSettings.ownVoiceAmplification - leftAmplification.value = parsedSettings.leftAmplification - rightAmplification.value = parsedSettings.rightAmplification + tone.floatValue = parsedSettings.leftTone + ambientNoiseReduction.floatValue = parsedSettings.leftAmbientNoiseReduction + ownVoiceAmplification.floatValue = parsedSettings.ownVoiceAmplification + leftAmplification.floatValue = parsedSettings.leftAmplification + rightAmplification.floatValue = parsedSettings.rightAmplification initialReadSucceeded.value = true } else { - Log.d(TAG, "Failed to read/parse initial hearing aid settings after ${initialReadAttempts.intValue} attempts") + Log.d( + TAG, + "Failed to read/parse initial hearing aid settings after ${initialReadAttempts.intValue} attempts" + ) } } catch (e: IOException) { e.printStackTrace() @@ -256,7 +269,8 @@ fun UpdateHearingTestScreen(@Suppress("unused") navController: NavController) { } } - val frequencies = listOf("250Hz", "500Hz", "1kHz", "2kHz", "3kHz", "4kHz", "6kHz", "8kHz") + val frequencies = + listOf("250Hz", "500Hz", "1kHz", "2kHz", "3kHz", "4kHz", "6kHz", "8kHz") Row( modifier = Modifier.fillMaxWidth(), diff --git a/android/app/src/main/java/me/kavishdevar/librepods/screens/VersionInfoScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/VersionInfoScreen.kt index a0ea75e..feadafd 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/screens/VersionInfoScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/VersionInfoScreen.kt @@ -19,22 +19,22 @@ package me.kavishdevar.librepods.screens import androidx.compose.foundation.background -import android.annotation.SuppressLint import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color @@ -45,36 +45,23 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.navigation.NavController import com.kyant.backdrop.backdrops.layerBackdrop import com.kyant.backdrop.backdrops.rememberLayerBackdrop -import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi -import kotlinx.coroutines.Job import me.kavishdevar.librepods.R import me.kavishdevar.librepods.composables.StyledScaffold -import me.kavishdevar.librepods.services.ServiceManager -import kotlin.io.encoding.ExperimentalEncodingApi +import me.kavishdevar.librepods.viewmodel.AirPodsViewModel -private var debounceJob: Job? = null - -@SuppressLint("DefaultLocale") -@ExperimentalHazeMaterialsApi -@OptIn(ExperimentalMaterial3Api::class, ExperimentalEncodingApi::class) @Composable -fun VersionScreen(navController: NavController) { +fun VersionScreen(viewModel: AirPodsViewModel) { + val state by viewModel.uiState.collectAsState() val isDarkTheme = isSystemInDarkTheme() - val service = ServiceManager.getService() - if (service == null) return - val airpodsInstance = service.airpodsInstance - if (airpodsInstance == null) return - val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF) val textColor = if (isDarkTheme) Color.White else Color.Black val backdrop = rememberLayerBackdrop() StyledScaffold( - title = stringResource(R.string.customize_adaptive_audio) + title = stringResource(R.string.version) ) { spacerHeight -> Column( modifier = Modifier @@ -120,7 +107,7 @@ fun VersionScreen(navController: NavController) { ) ) Text( - text = airpodsInstance.version1 ?: "N/A", + text = state.version1, style = TextStyle( fontSize = 16.sp, color = textColor.copy(0.8f), @@ -149,7 +136,7 @@ fun VersionScreen(navController: NavController) { ) ) Text( - text = airpodsInstance.version2 ?: "N/A", + text = state.version2, style = TextStyle( fontSize = 16.sp, color = textColor.copy(0.8f), @@ -178,7 +165,7 @@ fun VersionScreen(navController: NavController) { ) ) Text( - text = airpodsInstance.version3 ?: "N/A", + text = state.version3, style = TextStyle( fontSize = 16.sp, color = textColor.copy(0.8f), @@ -189,4 +176,4 @@ fun VersionScreen(navController: NavController) { } } } -} \ No newline at end of file +} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsQSService.kt b/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsQSService.kt index 1fc6fce..4d20339 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsQSService.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsQSService.kt @@ -244,8 +244,10 @@ class AirPodsQSService : TileService() { private fun getNextAncMode(): Int { val availableModes = getAvailableModes() + Log.d("AirPodsQSService", "availableModes: $availableModes, currentAncMode: $currentAncMode") val currentIndex = availableModes.indexOf(currentAncMode) val nextIndex = (currentIndex + 1) % availableModes.size + Log.d("AirPodsQSService", "nextIndex: $nextIndex") return availableModes[nextIndex] } diff --git a/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt b/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt index 2aff354..b7b9b12 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt @@ -16,11 +16,12 @@ along with this program. If not, see . */ -@file:OptIn(ExperimentalEncodingApi::class) -@file:Suppress("DEPRECATION") +@file:OptIn(ExperimentalEncodingApi::class) @file:Suppress("DEPRECATION") package me.kavishdevar.librepods.services +//import me.kavishdevar.librepods.utils.CrossDevice +//import me.kavishdevar.librepods.utils.CrossDevicePackets import android.Manifest import android.annotation.SuppressLint import android.app.Notification @@ -79,6 +80,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withTimeout +import me.kavishdevar.librepods.BuildConfig import me.kavishdevar.librepods.MainActivity import me.kavishdevar.librepods.R import me.kavishdevar.librepods.constants.AirPodsNotifications @@ -94,8 +96,6 @@ import me.kavishdevar.librepods.utils.AirPodsInstance import me.kavishdevar.librepods.utils.AirPodsModels import me.kavishdevar.librepods.utils.BLEManager import me.kavishdevar.librepods.utils.BluetoothConnectionManager -//import me.kavishdevar.librepods.utils.CrossDevice -//import me.kavishdevar.librepods.utils.CrossDevicePackets import me.kavishdevar.librepods.utils.GestureDetector import me.kavishdevar.librepods.utils.HeadTracking import me.kavishdevar.librepods.utils.IslandType @@ -127,21 +127,17 @@ import java.nio.ByteBuffer import java.nio.ByteOrder import kotlin.io.encoding.Base64 import kotlin.io.encoding.ExperimentalEncodingApi -import kotlin.jvm.java private const val TAG = "AirPodsService" object ServiceManager { - @ExperimentalEncodingApi private var service: AirPodsService? = null - @ExperimentalEncodingApi @Synchronized fun getService(): AirPodsService? { return service } - @ExperimentalEncodingApi @Synchronized fun setService(service: AirPodsService?) { this.service = service @@ -149,7 +145,6 @@ object ServiceManager { } // @Suppress("unused") -@ExperimentalEncodingApi class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeListener { var macAddress = "" var localMac = "" @@ -159,6 +154,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList var cameraActive = false private var disconnectedBecauseReversed = false private var otherDeviceTookOver = false + data class ServiceConfig( var deviceName: String = "AirPods", var earDetectionEnabled: Boolean = true, @@ -237,38 +233,31 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList companion object { init { - System.loadLibrary("socket_private_constructor") + System.loadLibrary("bluetooth_socket") } } private val bleStatusListener = object : BLEManager.AirPodsStatusListener { @SuppressLint("NewApi") override fun onDeviceStatusChanged( - device: BLEManager.AirPodsStatus, - previousStatus: BLEManager.AirPodsStatus? + device: BLEManager.AirPodsStatus, previousStatus: BLEManager.AirPodsStatus? ) { - // Store MAC address for BLE-only mode if not already stored - if (config.bleOnlyMode && macAddress.isEmpty()) { - macAddress = device.address - sharedPreferences.edit { - putString("mac_address", macAddress) - } - Log.d(TAG, "BLE-only mode: stored MAC address ${device.address}") - } - - if (device.connectionState == "Disconnected" && !config.bleOnlyMode) { + if (device.connectionState == "Disconnected" && !isConnected()) { // should never happen unless android messes up and sends us a stale broadcast Log.d(TAG, "Seems no device has taken over, we will.") val bluetoothManager = getSystemService(BluetoothManager::class.java) val bluetoothAdapter = bluetoothManager.adapter - val bluetoothDevice = bluetoothAdapter.getRemoteDevice(sharedPreferences.getString( - "mac_address", "") ?: "") + val bluetoothDevice = bluetoothAdapter.getRemoteDevice( + sharedPreferences.getString( + "mac_address", "" + ) ?: "" + ) connectToSocket(bluetoothAdapter, bluetoothDevice) } Log.d(TAG, "Device status changed") - if (isConnectedLocally) return - val leftLevel = bleManager.getMostRecentStatus()?.leftBattery?: 0 - val rightLevel = bleManager.getMostRecentStatus()?.rightBattery?: 0 - val caseLevel = bleManager.getMostRecentStatus()?.caseBattery?: 0 + if (socket.isConnected) return + val leftLevel = bleManager.getMostRecentStatus()?.leftBattery ?: 0 + val rightLevel = bleManager.getMostRecentStatus()?.rightBattery ?: 0 + val caseLevel = bleManager.getMostRecentStatus()?.caseBattery ?: 0 val leftCharging = bleManager.getMostRecentStatus()?.isLeftCharging val rightCharging = bleManager.getMostRecentStatus()?.isRightCharging val caseCharging = bleManager.getMostRecentStatus()?.isCaseCharging @@ -295,12 +284,13 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList Log.d(TAG, "Lid opened") showPopup( this@AirPodsService, - getSharedPreferences("settings", MODE_PRIVATE).getString("name", "AirPods Pro") ?: "AirPods" + getSharedPreferences("settings", MODE_PRIVATE).getString("name", "AirPods Pro") + ?: "AirPods" ) - if (isConnectedLocally) return - val leftLevel = bleManager.getMostRecentStatus()?.leftBattery?: 0 - val rightLevel = bleManager.getMostRecentStatus()?.rightBattery?: 0 - val caseLevel = bleManager.getMostRecentStatus()?.caseBattery?: 0 + if (socket.isConnected) return + val leftLevel = bleManager.getMostRecentStatus()?.leftBattery ?: 0 + val rightLevel = bleManager.getMostRecentStatus()?.rightBattery ?: 0 + val caseLevel = bleManager.getMostRecentStatus()?.caseBattery ?: 0 val leftCharging = bleManager.getMostRecentStatus()?.isLeftCharging val rightCharging = bleManager.getMostRecentStatus()?.isRightCharging val caseCharging = bleManager.getMostRecentStatus()?.isCaseCharging @@ -320,9 +310,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } override fun onEarStateChanged( - device: BLEManager.AirPodsStatus, - leftInEar: Boolean, - rightInEar: Boolean + device: BLEManager.AirPodsStatus, leftInEar: Boolean, rightInEar: Boolean ) { Log.d(TAG, "Ear state changed - Left: $leftInEar, Right: $rightInEar") @@ -333,10 +321,10 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } override fun onBatteryChanged(device: BLEManager.AirPodsStatus) { - if (isConnectedLocally) return - val leftLevel = bleManager.getMostRecentStatus()?.leftBattery?: 0 - val rightLevel = bleManager.getMostRecentStatus()?.rightBattery?: 0 - val caseLevel = bleManager.getMostRecentStatus()?.caseBattery?: 0 + if (socket.isConnected) return + val leftLevel = bleManager.getMostRecentStatus()?.leftBattery ?: 0 + val rightLevel = bleManager.getMostRecentStatus()?.rightBattery ?: 0 + val caseLevel = bleManager.getMostRecentStatus()?.caseBattery ?: 0 val leftCharging = bleManager.getMostRecentStatus()?.isLeftCharging val rightCharging = bleManager.getMostRecentStatus()?.isRightCharging val caseCharging = bleManager.getMostRecentStatus()?.isCaseCharging @@ -379,7 +367,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList sharedPreferencesLogs = getSharedPreferences("packet_logs", MODE_PRIVATE) - inMemoryLogs.addAll(sharedPreferencesLogs.getStringSet(packetLogKey, emptySet()) ?: emptySet()) + inMemoryLogs.addAll( + sharedPreferencesLogs.getStringSet(packetLogKey, emptySet()) ?: emptySet() + ) _packetLogsFlow.value = inMemoryLogs.toSet() sharedPreferences = getSharedPreferences("settings", MODE_PRIVATE) @@ -392,21 +382,26 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList localMac = config.selfMacAddress if (localMac.isEmpty()) { - localMac = try { - val process = Runtime.getRuntime().exec( - arrayOf("su", "-c", "settings get secure bluetooth_address") - ) + if (BuildConfig.FLAVOR == "xposed") { + localMac = try { + val process = Runtime.getRuntime().exec( + arrayOf("su", "-c", "settings get secure bluetooth_address") + ) - val exitCode = process.waitFor() + val exitCode = process.waitFor() - if (exitCode == 0) { - process.inputStream.bufferedReader().use { it.readLine()?.trim().orEmpty() } - } else { + if (exitCode == 0) { + process.inputStream.bufferedReader().use { it.readLine()?.trim().orEmpty() } + } else { + "" + } + } catch (e: Exception) { + Log.e( + TAG, + "Error retrieving local MAC address: ${e.message}. We probably aren't rooted." + ) "" } - } catch (e: Exception) { - Log.e(TAG, "Error retrieving local MAC address: ${e.message}. We probably aren't rooted.") - "" } config.selfMacAddress = localMac sharedPreferences.edit { @@ -433,31 +428,25 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList with(sharedPreferences) { edit { if (!contains("conversational_awareness_pause_music")) putBoolean( - "conversational_awareness_pause_music", - false + "conversational_awareness_pause_music", false ) if (!contains("personalized_volume")) putBoolean("personalized_volume", false) if (!contains("automatic_ear_detection")) putBoolean( - "automatic_ear_detection", - true + "automatic_ear_detection", true ) if (!contains("long_press_nc")) putBoolean("long_press_nc", true) if (!contains("show_phone_battery_in_widget")) putBoolean( - "show_phone_battery_in_widget", - true + "show_phone_battery_in_widget", true ) if (!contains("single_anc")) putBoolean("single_anc", true) if (!contains("long_press_transparency")) putBoolean( - "long_press_transparency", - true + "long_press_transparency", true ) if (!contains("conversational_awareness")) putBoolean( - "conversational_awareness", - true + "conversational_awareness", true ) if (!contains("relative_conversational_awareness_volume")) putBoolean( - "relative_conversational_awareness_volume", - true + "relative_conversational_awareness_volume", true ) if (!contains("long_press_adaptive")) putBoolean("long_press_adaptive", true) if (!contains("loud_sound_reduction")) putBoolean("loud_sound_reduction", true) @@ -465,34 +454,29 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList if (!contains("volume_control")) putBoolean("volume_control", true) if (!contains("head_gestures")) putBoolean("head_gestures", true) if (!contains("disconnect_when_not_wearing")) putBoolean( - "disconnect_when_not_wearing", - false + "disconnect_when_not_wearing", false ) // AirPods state-based takeover if (!contains("takeover_when_disconnected")) putBoolean( - "takeover_when_disconnected", - true + "takeover_when_disconnected", false ) - if (!contains("takeover_when_idle")) putBoolean("takeover_when_idle", true) + if (!contains("takeover_when_idle")) putBoolean("takeover_when_idle", false) if (!contains("takeover_when_music")) putBoolean("takeover_when_music", false) - if (!contains("takeover_when_call")) putBoolean("takeover_when_call", true) + if (!contains("takeover_when_call")) putBoolean("takeover_when_call", false) // Phone state-based takeover if (!contains("takeover_when_ringing_call")) putBoolean( - "takeover_when_ringing_call", - true + "takeover_when_ringing_call", false ) if (!contains("takeover_when_media_start")) putBoolean( - "takeover_when_media_start", - true + "takeover_when_media_start", false ) if (!contains("adaptive_strength")) putInt("adaptive_strength", 51) if (!contains("tone_volume")) putInt("tone_volume", 75) if (!contains("conversational_awareness_volume")) putInt( - "conversational_awareness_volume", - 43 + "conversational_awareness_volume", 43 ) if (!contains("qs_click_behavior")) putString("qs_click_behavior", "cycle") @@ -550,8 +534,10 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } } else { val currentMode = ancNotification.status - val allowOffModeValue = aacpManager.controlCommandStatusList.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.ALLOW_OFF_OPTION } - val allowOffMode = allowOffModeValue?.value?.takeIf { it.isNotEmpty() }?.get(0) == 0x01.toByte() + val allowOffModeValue = + aacpManager.controlCommandStatusList.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.ALLOW_OFF_OPTION } + val allowOffMode = allowOffModeValue?.value?.takeIf { it.isNotEmpty() } + ?.get(0) == 0x01.toByte() val nextMode = if (allowOffMode) { when (currentMode) { @@ -575,7 +561,10 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE.value, nextMode ) - Log.d(TAG, "Cycling ANC mode from $currentMode to $nextMode (offListeningMode: $allowOffMode)") + Log.d( + TAG, + "Cycling ANC mode from $currentMode to $nextMode (offListeningMode: $allowOffMode)" + ) } } } @@ -584,16 +573,14 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { registerReceiver(ancModeReceiver, ancModeFilter, RECEIVER_EXPORTED) } else { - @Suppress("UnspecifiedRegisterReceiverFlag") - registerReceiver(ancModeReceiver, ancModeFilter) + @Suppress("UnspecifiedRegisterReceiverFlag") registerReceiver( + ancModeReceiver, ancModeFilter + ) } - val audioManager = - this@AirPodsService.getSystemService(AUDIO_SERVICE) as AudioManager + val audioManager = this@AirPodsService.getSystemService(AUDIO_SERVICE) as AudioManager MediaController.initialize( - audioManager, - this@AirPodsService.getSharedPreferences( - "settings", - MODE_PRIVATE + audioManager, this@AirPodsService.getSharedPreferences( + "settings", MODE_PRIVATE ) ) // Log.d(TAG, "Initializing CrossDevice") @@ -607,12 +594,13 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList telephonyManager = getSystemService(TELEPHONY_SERVICE) as TelephonyManager phoneStateListener = object : PhoneStateListener() { - @SuppressLint("SwitchIntDef", "NewApi") + @Deprecated("Deprecated in Java") override fun onCallStateChanged(state: Int, phoneNumber: String?) { super.onCallStateChanged(state, phoneNumber) when (state) { TelephonyManager.CALL_STATE_RINGING -> { - val leAvailableForAudio = bleManager.getMostRecentStatus()?.isLeftInEar == true || bleManager.getMostRecentStatus()?.isRightInEar == true + val leAvailableForAudio = + bleManager.getMostRecentStatus()?.isLeftInEar == true || bleManager.getMostRecentStatus()?.isRightInEar == true // if ((CrossDevice.isAvailable && !isConnectedLocally && earDetectionNotification.status.contains(0x00)) || leAvailableForAudio) CoroutineScope(Dispatchers.IO).launch { if (leAvailableForAudio) runBlocking { takeOver("call") @@ -622,15 +610,19 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList handleIncomingCall() } } + TelephonyManager.CALL_STATE_OFFHOOK -> { - val leAvailableForAudio = bleManager.getMostRecentStatus()?.isLeftInEar == true || bleManager.getMostRecentStatus()?.isRightInEar == true + val leAvailableForAudio = + bleManager.getMostRecentStatus()?.isLeftInEar == true || bleManager.getMostRecentStatus()?.isRightInEar == true // if ((CrossDevice.isAvailable && !isConnectedLocally && earDetectionNotification.status.contains(0x00)) || leAvailableForAudio) CoroutineScope( if (leAvailableForAudio) CoroutineScope( - Dispatchers.IO).launch { + Dispatchers.IO + ).launch { takeOver("call") } isInCall = true } + TelephonyManager.CALL_STATE_IDLE -> { isInCall = false callNumber = null @@ -647,13 +639,12 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList batteryChangedIntentFilter.addAction(AirPodsNotifications.DISCONNECT_RECEIVERS) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { registerReceiver( - BatteryChangedIntentReceiver, - batteryChangedIntentFilter, - RECEIVER_EXPORTED + BatteryChangedIntentReceiver, batteryChangedIntentFilter, RECEIVER_EXPORTED ) } else { - @Suppress("UnspecifiedRegisterReceiverFlag") - registerReceiver(BatteryChangedIntentReceiver, batteryChangedIntentFilter) + @Suppress("UnspecifiedRegisterReceiverFlag") registerReceiver( + BatteryChangedIntentReceiver, batteryChangedIntentFilter + ) } } val serviceIntentFilter = IntentFilter().apply { @@ -692,7 +683,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } Log.d(TAG, "Setting metadata") setMetadatas(device!!) - isConnectedLocally = true +// isConnectedLocally = true macAddress = device!!.address sharedPreferences.edit { putString("mac_address", macAddress) @@ -701,7 +692,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } else if (intent?.action == AirPodsNotifications.AIRPODS_DISCONNECTED) { device = null - isConnectedLocally = false +// isConnectedLocally = false popupShown = false updateNotificationContent(false) attManager?.disconnect() @@ -709,10 +700,17 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } } } - val showIslandReceiver = object: BroadcastReceiver() { + val showIslandReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent?.action == "me.kavishdevar.librepods.cross_device_island") { - showIsland(this@AirPodsService, batteryNotification.getBattery().find { it.component == BatteryComponent.LEFT}?.level!!.coerceAtMost(batteryNotification.getBattery().find { it.component == BatteryComponent.RIGHT}?.level!!)) + showIsland( + this@AirPodsService, + batteryNotification.getBattery() + .find { it.component == BatteryComponent.LEFT }?.level!!.coerceAtMost( + batteryNotification.getBattery() + .find { it.component == BatteryComponent.RIGHT }?.level!! + ) + ) } else if (intent?.action == AirPodsNotifications.DISCONNECT_RECEIVERS) { try { context?.unregisterReceiver(this) @@ -731,8 +729,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { registerReceiver(showIslandReceiver, showIslandIntentFilter, RECEIVER_EXPORTED) } else { - @Suppress("UnspecifiedRegisterReceiverFlag") - registerReceiver(showIslandReceiver, showIslandIntentFilter) + @Suppress("UnspecifiedRegisterReceiverFlag") registerReceiver( + showIslandReceiver, showIslandIntentFilter + ) } val deviceIntentFilter = IntentFilter().apply { @@ -744,8 +743,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList registerReceiver(connectionReceiver, deviceIntentFilter, RECEIVER_EXPORTED) registerReceiver(bluetoothReceiver, serviceIntentFilter, RECEIVER_EXPORTED) } else { - @Suppress("UnspecifiedRegisterReceiverFlag") - registerReceiver(connectionReceiver, deviceIntentFilter) + @Suppress("UnspecifiedRegisterReceiverFlag") registerReceiver( + connectionReceiver, deviceIntentFilter + ) registerReceiver(bluetoothReceiver, serviceIntentFilter) } @@ -756,8 +756,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList if (device.uuids != null) { if (device.uuids.contains(ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a"))) { bluetoothAdapter.getProfileProxy( - this, - object : BluetoothProfile.ServiceListener { + this, object : BluetoothProfile.ServiceListener { @SuppressLint("NewApi") override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { if (profile == BluetoothProfile.A2DP) { @@ -773,17 +772,17 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList putString("mac_address", macAddress) } // } - this@AirPodsService.sendBroadcast( - Intent(AirPodsNotifications.AIRPODS_CONNECTED) - ) + sendBroadcast( + Intent(AirPodsNotifications.AIRPODS_CONNECTED).apply { + setPackage(packageName) + }) } } bluetoothAdapter.closeProfileProxy(profile, proxy) } override fun onServiceDisconnected(profile: Int) {} - }, - BluetoothProfile.A2DP + }, BluetoothProfile.A2DP ) } } @@ -800,7 +799,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList @Suppress("unused") fun cameraOpened() { - Log.d(TAG, "Camera opened, gonna handle stem presses and take action if enabled") + Log.d(TAG, "Camera opened, gonna handle stem presses and take action if visible") cameraActive = true setupStemActions() } @@ -812,8 +811,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } fun isCustomAction( - action: StemAction?, - default: StemAction? + action: StemAction?, default: StemAction? ): Boolean { return action != default } @@ -822,23 +820,29 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList val singlePressDefault = StemAction.defaultActions[StemPressType.SINGLE_PRESS] val doublePressDefault = StemAction.defaultActions[StemPressType.DOUBLE_PRESS] val triplePressDefault = StemAction.defaultActions[StemPressType.TRIPLE_PRESS] - val longPressDefault = StemAction.defaultActions[StemPressType.LONG_PRESS] + val longPressDefault = StemAction.defaultActions[StemPressType.LONG_PRESS] - val singlePressCustomized = isCustomAction(config.leftSinglePressAction, singlePressDefault) || - isCustomAction(config.rightSinglePressAction, singlePressDefault) || - (cameraActive && config.cameraAction == StemPressType.SINGLE_PRESS) - val doublePressCustomized = isCustomAction(config.leftDoublePressAction, doublePressDefault) || - isCustomAction(config.rightDoublePressAction, doublePressDefault) - val triplePressCustomized = isCustomAction(config.leftTriplePressAction, triplePressDefault) || - isCustomAction(config.rightTriplePressAction, triplePressDefault) - val longPressCustomized = isCustomAction(config.leftLongPressAction, longPressDefault) || - isCustomAction(config.rightLongPressAction, longPressDefault) || - (cameraActive && config.cameraAction == StemPressType.LONG_PRESS) - Log.d(TAG, "Setting up stem actions: " + - "Single Press Customized: $singlePressCustomized, " + - "Double Press Customized: $doublePressCustomized, " + - "Triple Press Customized: $triplePressCustomized, " + - "Long Press Customized: $longPressCustomized") + val singlePressCustomized = + isCustomAction(config.leftSinglePressAction, singlePressDefault) || isCustomAction( + config.rightSinglePressAction, singlePressDefault + ) || (cameraActive && config.cameraAction == StemPressType.SINGLE_PRESS) + val doublePressCustomized = + isCustomAction(config.leftDoublePressAction, doublePressDefault) || isCustomAction( + config.rightDoublePressAction, doublePressDefault + ) + val triplePressCustomized = + isCustomAction(config.leftTriplePressAction, triplePressDefault) || isCustomAction( + config.rightTriplePressAction, triplePressDefault + ) + val longPressCustomized = isCustomAction( + config.leftLongPressAction, longPressDefault + ) || isCustomAction( + config.rightLongPressAction, longPressDefault + ) || (cameraActive && config.cameraAction == StemPressType.LONG_PRESS) + Log.d( + TAG, + "Setting up stem actions: " + "Single Press Customized: $singlePressCustomized, " + "Double Press Customized: $doublePressCustomized, " + "Triple Press Customized: $triplePressCustomized, " + "Long Press Customized: $longPressCustomized" + ) aacpManager.sendStemConfigPacket( singlePressCustomized, doublePressCustomized, @@ -855,6 +859,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList batteryNotification.setBattery(batteryInfo) sendBroadcast(Intent(AirPodsNotifications.BATTERY_DATA).apply { putParcelableArrayListExtra("data", ArrayList(batteryNotification.getBattery())) + setPackage(packageName) }) updateBattery() updateNotificationContent( @@ -887,6 +892,8 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList bytes[0] = list[0] bytes[1] = list[1] putExtra("data", bytes) + }.apply { + setPackage(packageName) }) Log.d( "AirPodsParser", @@ -899,6 +906,8 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList conversationAwarenessNotification.setData(conversationAwareness) sendBroadcast(Intent(AirPodsNotifications.CA_DATA).apply { putExtra("data", conversationAwarenessNotification.status) + }.apply { + setPackage(packageName) }) if (conversationAwarenessNotification.status == 1.toByte() || conversationAwarenessNotification.status == 2.toByte()) { @@ -916,7 +925,8 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList override fun onControlCommandReceived(controlCommand: ByteArray) { val command = AACPManager.ControlCommand.fromByteArray(controlCommand) if (command.identifier == AACPManager.Companion.ControlCommandIdentifiers.LISTENING_MODE.value) { - ancNotification.setStatus(byteArrayOf(command.value.takeIf { it.isNotEmpty() }?.get(0) ?: 0x00.toByte())) + ancNotification.setStatus(byteArrayOf(command.value.takeIf { it.isNotEmpty() } + ?.get(0) ?: 0x00.toByte())) sendANCBroadcast() updateNoiseControlWidget() } @@ -933,8 +943,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList MediaController.pausedForOtherDevice = true otherDeviceTookOver = true disconnectAudio( - this@AirPodsService, - device + this@AirPodsService, device ) } } @@ -943,16 +952,19 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList // TODO: Show a reverse button, but that's a lot of effort -- i'd have to change the UI too, which i hate doing, and handle other device's reverses too, and disconnect audio etc... so for now, just pause the audio and show the island without asking to reverse. // handling reverse is a problem because we'd have to disconnect the audio, but there's no option connect audio again natively, so notification would have to be changed. I wish there was a way to just "change the audio output device". // (20 minutes later) i've done it nonetheless :] - val senderName = aacpManager.connectedDevices.find { it.mac == sender }?.type ?: "Other device" - Log.d(TAG, "other device has hijacked the connection, reasonReverseTapped: $reasonReverseTapped") + val senderName = + aacpManager.connectedDevices.find { it.mac == sender }?.type ?: "Other device" + Log.d( + TAG, + "other device has hijacked the connection, reasonReverseTapped: $reasonReverseTapped" + ) aacpManager.sendControlCommand( AACPManager.Companion.ControlCommandIdentifiers.OWNS_CONNECTION.value, byteArrayOf(0x00) ) otherDeviceTookOver = true disconnectAudio( - this@AirPodsService, - device + this@AirPodsService, device ) if (reasonReverseTapped) { Log.d(TAG, "reverse tapped, disconnecting audio") @@ -960,7 +972,12 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList disconnectAudio(this@AirPodsService, device) showIsland( this@AirPodsService, - (batteryNotification.getBattery().find { it.component == BatteryComponent.LEFT}?.level?: 0).coerceAtMost(batteryNotification.getBattery().find { it.component == BatteryComponent.RIGHT}?.level?: 0), + (batteryNotification.getBattery() + .find { it.component == BatteryComponent.LEFT }?.level + ?: 0).coerceAtMost( + batteryNotification.getBattery() + .find { it.component == BatteryComponent.RIGHT }?.level ?: 0 + ), IslandType.MOVED_TO_OTHER_DEVICE, reversed = true, otherDeviceName = senderName @@ -969,7 +986,12 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList if (!aacpManager.owns) { showIsland( this@AirPodsService, - (batteryNotification.getBattery().find { it.component == BatteryComponent.LEFT}?.level?: 0).coerceAtMost(batteryNotification.getBattery().find { it.component == BatteryComponent.RIGHT}?.level?: 0), + (batteryNotification.getBattery() + .find { it.component == BatteryComponent.LEFT }?.level + ?: 0).coerceAtMost( + batteryNotification.getBattery() + .find { it.component == BatteryComponent.RIGHT }?.level ?: 0 + ), IslandType.MOVED_TO_OTHER_DEVICE, reversed = reasonReverseTapped, otherDeviceName = senderName @@ -979,10 +1001,15 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } override fun onShowNearbyUI(sender: String) { - val senderName = aacpManager.connectedDevices.find { it.mac == sender }?.type ?: "Other device" + val senderName = + aacpManager.connectedDevices.find { it.mac == sender }?.type ?: "Other device" showIsland( this@AirPodsService, - (batteryNotification.getBattery().find { it.component == BatteryComponent.LEFT}?.level?: 0).coerceAtMost(batteryNotification.getBattery().find { it.component == BatteryComponent.RIGHT}?.level?: 0), + (batteryNotification.getBattery() + .find { it.component == BatteryComponent.LEFT }?.level ?: 0).coerceAtMost( + batteryNotification.getBattery() + .find { it.component == BatteryComponent.RIGHT }?.level ?: 0 + ), IslandType.MOVED_TO_OTHER_DEVICE, reversed = false, otherDeviceName = senderName @@ -1037,6 +1064,11 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList attManager = attManager ) } + sendBroadcast( + Intent(AirPodsNotifications.AIRPODS_INFORMATION_UPDATED).setPackage( + packageName + ) + ) } @SuppressLint("NewApi") @@ -1059,21 +1091,34 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } override fun onStemPressReceived(stemPress: ByteArray) { + val (stemPressType, bud) = aacpManager.parseStemPressResponse(stemPress) - Log.d("AirPodsParser", "Stem press received: $stemPressType on $bud, cameraActive: $cameraActive, cameraAction: ${config.cameraAction}") + Log.d( + "AirPodsParser", + "Stem press received: $stemPressType on $bud, cameraActive: $cameraActive, cameraAction: ${config.cameraAction}" + ) if (cameraActive && config.cameraAction != null && stemPressType == config.cameraAction) { - Runtime.getRuntime().exec(arrayOf("su", "-c", "input keyevent 27")) + if (BuildConfig.FLAVOR == "xposed") { + Runtime.getRuntime().exec(arrayOf("su", "-c", "input keyevent 27")) + } } else { val action = getActionFor(bud, stemPressType) Log.d("AirPodsParser", "$bud $stemPressType action: $action") action?.let { executeStemAction(it) } } } + override fun onAudioSourceReceived(audioSource: ByteArray) { - Log.d("AirPodsParser", "Audio source changed mac: ${aacpManager.audioSource?.mac}, type: ${aacpManager.audioSource?.type?.name}") + Log.d( + "AirPodsParser", + "Audio source changed mac: ${aacpManager.audioSource?.mac}, type: ${aacpManager.audioSource?.type?.name}" + ) if (aacpManager.audioSource?.type != AACPManager.Companion.AudioSourceType.NONE && aacpManager.audioSource?.mac != localMac) { - Log.d("AirPodsParser", "Audio source is another device, better to give up aacp control") + Log.d( + "AirPodsParser", + "Audio source is another device, better to give up aacp control" + ) aacpManager.sendControlCommand( AACPManager.Companion.ControlCommandIdentifiers.OWNS_CONNECTION.value, byteArrayOf(0x00) @@ -1087,28 +1132,55 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList override fun onConnectedDevicesReceived(connectedDevices: List) { for (device in connectedDevices) { - Log.d("AirPodsParser", "Connected device: ${device.mac}, info1: ${device.info1}, info2: ${device.info2})") + Log.d( + "AirPodsParser", + "Connected device: ${device.mac}, info1: ${device.info1}, info2: ${device.info2})" + ) } val newDevices = connectedDevices.filter { newDevice -> - val notInOld = aacpManager.oldConnectedDevices.none { oldDevice -> oldDevice.mac == newDevice.mac } + val notInOld = + aacpManager.oldConnectedDevices.none { oldDevice -> oldDevice.mac == newDevice.mac } val notLocal = newDevice.mac != localMac notInOld && notLocal } for (device in newDevices) { - Log.d("AirPodsParser", "New connected device: ${device.mac}, info1: ${device.info1}, info2: ${device.info2})") - Log.d(TAG, "Sending new Tipi packet for device ${device.mac}, and sending media info to the device") - aacpManager.sendMediaInformationNewDevice(selfMacAddress = localMac, targetMacAddress = device.mac) - aacpManager.sendAddTiPiDevice(selfMacAddress = localMac, targetMacAddress = device.mac) + Log.d( + "AirPodsParser", + "New connected device: ${device.mac}, info1: ${device.info1}, info2: ${device.info2})" + ) + Log.d( + TAG, + "Sending new Tipi packet for device ${device.mac}, and sending media info to the device" + ) + aacpManager.sendMediaInformationNewDevice( + selfMacAddress = localMac, targetMacAddress = device.mac + ) + aacpManager.sendAddTiPiDevice( + selfMacAddress = localMac, targetMacAddress = device.mac + ) } } + + override fun onEQPacketReceived(eqData: FloatArray) { + sendBroadcast( + Intent(AirPodsNotifications.EQ_DATA).putExtra("eqData", eqData).apply { + setPackage(packageName) + }) + } + override fun onUnknownPacketReceived(packet: ByteArray) { - Log.d("AACPManager", "Unknown packet received: ${packet.joinToString(" ") { "%02X".format(it) }}") + Log.d( + "AACPManager", + "Unknown packet received: ${packet.joinToString(" ") { "%02X".format(it) }}" + ) } }) } - private fun getActionFor(bud: AACPManager.Companion.StemPressBudType, type: StemPressType): StemAction? { + private fun getActionFor( + bud: AACPManager.Companion.StemPressBudType, type: StemPressType + ): StemAction? { return when (type) { StemPressType.SINGLE_PRESS -> if (bud == AACPManager.Companion.StemPressBudType.LEFT) config.leftSinglePressAction else config.rightSinglePressAction StemPressType.DOUBLE_PRESS -> if (bud == AACPManager.Companion.StemPressBudType.LEFT) config.leftDoublePressAction else config.rightDoublePressAction @@ -1120,8 +1192,11 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList private fun executeStemAction(action: StemAction) { when (action) { StemAction.defaultActions[StemPressType.SINGLE_PRESS] -> { - Log.d("AirPodsParser", "Default single press action: Play/Pause, not taking action.") + Log.d( + "AirPodsParser", "Default single press action: Play/Pause, not taking action." + ) } + StemAction.PLAY_PAUSE -> MediaController.sendPlayPause() StemAction.PREVIOUS_TRACK -> MediaController.sendPreviousTrack() StemAction.NEXT_TRACK -> MediaController.sendNextTrack() @@ -1132,19 +1207,28 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } startActivity(intent) } else { - Log.w("AirPodsParser", "Digital Assistant action is not supported on this Android version.") + Log.w( + "AirPodsParser", + "Digital Assistant action is not supported on this Android version." + ) } } + StemAction.CYCLE_NOISE_CONTROL_MODES -> { Log.d("AirPodsParser", "Cycling noise control modes") - sendBroadcast(Intent("me.kavishdevar.librepods.SET_ANC_MODE")) + sendBroadcast(Intent("me.kavishdevar.librepods.SET_ANC_MODE").apply { + setPackage(packageName) + }) } } } private fun processEarDetectionChange(earDetection: ByteArray) { var inEar: Boolean - val inEarData = listOf(earDetectionNotification.status[0] == 0x00.toByte(), earDetectionNotification.status[1] == 0x00.toByte()) + val inEarData = listOf( + earDetectionNotification.status[0] == 0x00.toByte(), + earDetectionNotification.status[1] == 0x00.toByte() + ) var justEnabledA2dp = false earDetectionNotification.setStatus(earDetection) if (config.earDetectionEnabled) { @@ -1152,14 +1236,21 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList inEar = data[0] == 0x00.toByte() && data[1] == 0x00.toByte() val newInEarData = listOf( - data[0] == 0x00.toByte(), - data[1] == 0x00.toByte() + data[0] == 0x00.toByte(), data[1] == 0x00.toByte() ) - if (inEarData.sorted() == listOf(false, false) && newInEarData.sorted() != listOf(false, false) && islandWindow?.isVisible != true) { + if (inEarData.sorted() == listOf(false, false) && newInEarData.sorted() != listOf( + false, false + ) && islandWindow?.isVisible != true + ) { showIsland( this@AirPodsService, - (batteryNotification.getBattery().find { it.component == BatteryComponent.LEFT}?.level?: 0).coerceAtMost(batteryNotification.getBattery().find { it.component == BatteryComponent.RIGHT}?.level?: 0)) + (batteryNotification.getBattery() + .find { it.component == BatteryComponent.LEFT }?.level ?: 0).coerceAtMost( + batteryNotification.getBattery() + .find { it.component == BatteryComponent.RIGHT }?.level ?: 0 + ) + ) } if (newInEarData == listOf(false, false) && islandWindow?.isVisible == true) { @@ -1190,7 +1281,10 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList MediaController.userPlayedTheMedia = false } - Log.d("AirPodsParser", "inEarData: ${inEarData.sorted()}, newInEarData: ${newInEarData.sorted()}") + Log.d( + "AirPodsParser", + "inEarData: ${inEarData.sorted()}, newInEarData: ${newInEarData.sorted()}" + ) if (newInEarData.sorted() != inEarData.sorted()) { if (inEar) { @@ -1209,15 +1303,21 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList val a2dpConnectionStateReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.action == "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED") { - val state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED) - val previousState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED) - val device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) + val state = intent.getIntExtra( + BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED + ) + val previousState = intent.getIntExtra( + BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED + ) + val device = + intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) - Log.d("MediaController", "A2DP state changed: $previousState -> $state for device: ${device?.address}") + Log.d( + "MediaController", + "A2DP state changed: $previousState -> $state for device: ${device?.address}" + ) - if (state == BluetoothProfile.STATE_CONNECTED && - previousState != BluetoothProfile.STATE_CONNECTED && - device?.address == this@AirPodsService.device?.address) { + if (state == BluetoothProfile.STATE_CONNECTED && previousState != BluetoothProfile.STATE_CONNECTED && device?.address == this@AirPodsService.device?.address) { Log.d("MediaController", "A2DP connected, sending play command") MediaController.sendPlay() @@ -1229,7 +1329,8 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } } - val a2dpIntentFilter = IntentFilter("android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED") + val a2dpIntentFilter = + IntentFilter("android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { registerReceiver(a2dpConnectionStateReceiver, a2dpIntentFilter, RECEIVER_EXPORTED) } else { @@ -1241,51 +1342,105 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList config = ServiceConfig( deviceName = sharedPreferences.getString("name", "AirPods") ?: "AirPods", earDetectionEnabled = sharedPreferences.getBoolean("automatic_ear_detection", true), - conversationalAwarenessPauseMusic = sharedPreferences.getBoolean("conversational_awareness_pause_music", false), - showPhoneBatteryInWidget = sharedPreferences.getBoolean("show_phone_battery_in_widget", true), - relativeConversationalAwarenessVolume = sharedPreferences.getBoolean("relative_conversational_awareness_volume", true), + conversationalAwarenessPauseMusic = sharedPreferences.getBoolean( + "conversational_awareness_pause_music", false + ), + showPhoneBatteryInWidget = sharedPreferences.getBoolean( + "show_phone_battery_in_widget", true + ), + relativeConversationalAwarenessVolume = sharedPreferences.getBoolean( + "relative_conversational_awareness_volume", true + ), headGestures = sharedPreferences.getBoolean("head_gestures", true), - disconnectWhenNotWearing = sharedPreferences.getBoolean("disconnect_when_not_wearing", false), - conversationalAwarenessVolume = sharedPreferences.getInt("conversational_awareness_volume", 43), + disconnectWhenNotWearing = sharedPreferences.getBoolean( + "disconnect_when_not_wearing", false + ), + conversationalAwarenessVolume = sharedPreferences.getInt( + "conversational_awareness_volume", 43 + ), qsClickBehavior = sharedPreferences.getString("qs_click_behavior", "cycle") ?: "cycle", // AirPods state-based takeover - takeoverWhenDisconnected = sharedPreferences.getBoolean("takeover_when_disconnected", true), - takeoverWhenIdle = sharedPreferences.getBoolean("takeover_when_idle", true), + takeoverWhenDisconnected = sharedPreferences.getBoolean( + "takeover_when_disconnected", false + ), + takeoverWhenIdle = sharedPreferences.getBoolean("takeover_when_idle", false), takeoverWhenMusic = sharedPreferences.getBoolean("takeover_when_music", false), - takeoverWhenCall = sharedPreferences.getBoolean("takeover_when_call", true), + takeoverWhenCall = sharedPreferences.getBoolean("takeover_when_call", false), // Phone state-based takeover - takeoverWhenRingingCall = sharedPreferences.getBoolean("takeover_when_ringing_call", true), - takeoverWhenMediaStart = sharedPreferences.getBoolean("takeover_when_media_start", true), + takeoverWhenRingingCall = sharedPreferences.getBoolean( + "takeover_when_ringing_call", false + ), + takeoverWhenMediaStart = sharedPreferences.getBoolean( + "takeover_when_media_start", false + ), // Stem actions - leftSinglePressAction = StemAction.fromString(sharedPreferences.getString("left_single_press_action", "PLAY_PAUSE") ?: "PLAY_PAUSE")!!, - rightSinglePressAction = StemAction.fromString(sharedPreferences.getString("right_single_press_action", "PLAY_PAUSE") ?: "PLAY_PAUSE")!!, + leftSinglePressAction = StemAction.fromString( + sharedPreferences.getString( + "left_single_press_action", "PLAY_PAUSE" + ) ?: "PLAY_PAUSE" + )!!, + rightSinglePressAction = StemAction.fromString( + sharedPreferences.getString( + "right_single_press_action", "PLAY_PAUSE" + ) ?: "PLAY_PAUSE" + )!!, - leftDoublePressAction = StemAction.fromString(sharedPreferences.getString("left_double_press_action", "PREVIOUS_TRACK") ?: "NEXT_TRACK")!!, - rightDoublePressAction = StemAction.fromString(sharedPreferences.getString("right_double_press_action", "NEXT_TRACK") ?: "NEXT_TRACK")!!, + leftDoublePressAction = StemAction.fromString( + sharedPreferences.getString( + "left_double_press_action", "PREVIOUS_TRACK" + ) ?: "NEXT_TRACK" + )!!, + rightDoublePressAction = StemAction.fromString( + sharedPreferences.getString( + "right_double_press_action", "NEXT_TRACK" + ) ?: "NEXT_TRACK" + )!!, - leftTriplePressAction = StemAction.fromString(sharedPreferences.getString("left_triple_press_action", "PREVIOUS_TRACK") ?: "PREVIOUS_TRACK")!!, - rightTriplePressAction = StemAction.fromString(sharedPreferences.getString("right_triple_press_action", "PREVIOUS_TRACK") ?: "PREVIOUS_TRACK")!!, + leftTriplePressAction = StemAction.fromString( + sharedPreferences.getString( + "left_triple_press_action", "PREVIOUS_TRACK" + ) ?: "PREVIOUS_TRACK" + )!!, + rightTriplePressAction = StemAction.fromString( + sharedPreferences.getString( + "right_triple_press_action", "PREVIOUS_TRACK" + ) ?: "PREVIOUS_TRACK" + )!!, - leftLongPressAction = StemAction.fromString(sharedPreferences.getString("left_long_press_action", "CYCLE_NOISE_CONTROL_MODES") ?: "CYCLE_NOISE_CONTROL_MODES")!!, - rightLongPressAction = StemAction.fromString(sharedPreferences.getString("right_long_press_action", "DIGITAL_ASSISTANT") ?: "DIGITAL_ASSISTANT")!!, + leftLongPressAction = StemAction.fromString( + sharedPreferences.getString( + "left_long_press_action", "CYCLE_NOISE_CONTROL_MODES" + ) ?: "CYCLE_NOISE_CONTROL_MODES" + )!!, + rightLongPressAction = StemAction.fromString( + sharedPreferences.getString( + "right_long_press_action", "DIGITAL_ASSISTANT" + ) ?: "DIGITAL_ASSISTANT" + )!!, - cameraAction = sharedPreferences.getString("camera_action", null)?.let { StemPressType.valueOf(it) }, + cameraAction = sharedPreferences.getString("camera_action", null) + ?.let { StemPressType.valueOf(it) }, // AirPods device information airpodsName = sharedPreferences.getString("airpods_name", "") ?: "", airpodsModelNumber = sharedPreferences.getString("airpods_model_number", "") ?: "", airpodsManufacturer = sharedPreferences.getString("airpods_manufacturer", "") ?: "", airpodsSerialNumber = sharedPreferences.getString("airpods_serial_number", "") ?: "", - airpodsLeftSerialNumber = sharedPreferences.getString("airpods_left_serial_number", "") ?: "", - airpodsRightSerialNumber = sharedPreferences.getString("airpods_right_serial_number", "") ?: "", + airpodsLeftSerialNumber = sharedPreferences.getString("airpods_left_serial_number", "") + ?: "", + airpodsRightSerialNumber = sharedPreferences.getString( + "airpods_right_serial_number", "" + ) ?: "", airpodsVersion1 = sharedPreferences.getString("airpods_version1", "") ?: "", airpodsVersion2 = sharedPreferences.getString("airpods_version2", "") ?: "", airpodsVersion3 = sharedPreferences.getString("airpods_version3", "") ?: "", - airpodsHardwareRevision = sharedPreferences.getString("airpods_hardware_revision", "") ?: "", - airpodsUpdaterIdentifier = sharedPreferences.getString("airpods_updater_identifier", "") ?: "", + airpodsHardwareRevision = sharedPreferences.getString("airpods_hardware_revision", "") + ?: "", + airpodsUpdaterIdentifier = sharedPreferences.getString("airpods_updater_identifier", "") + ?: "", selfMacAddress = sharedPreferences.getString("self_mac_address", "") ?: "" ) @@ -1294,31 +1449,48 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList override fun onSharedPreferenceChanged(preferences: SharedPreferences?, key: String?) { if (preferences == null || key == null) return - when(key) { + when (key) { "name" -> config.deviceName = preferences.getString(key, "AirPods") ?: "AirPods" "mac_address" -> macAddress = preferences.getString(key, "") ?: "" - "automatic_ear_detection" -> config.earDetectionEnabled = preferences.getBoolean(key, true) - "conversational_awareness_pause_music" -> config.conversationalAwarenessPauseMusic = preferences.getBoolean(key, false) + "automatic_ear_detection" -> config.earDetectionEnabled = + preferences.getBoolean(key, true) + + "conversational_awareness_pause_music" -> config.conversationalAwarenessPauseMusic = + preferences.getBoolean(key, false) + "show_phone_battery_in_widget" -> { config.showPhoneBatteryInWidget = preferences.getBoolean(key, true) widgetMobileBatteryEnabled = config.showPhoneBatteryInWidget updateBattery() } - "relative_conversational_awareness_volume" -> config.relativeConversationalAwarenessVolume = preferences.getBoolean(key, true) + + "relative_conversational_awareness_volume" -> config.relativeConversationalAwarenessVolume = + preferences.getBoolean(key, true) + "head_gestures" -> config.headGestures = preferences.getBoolean(key, true) - "disconnect_when_not_wearing" -> config.disconnectWhenNotWearing = preferences.getBoolean(key, false) - "conversational_awareness_volume" -> config.conversationalAwarenessVolume = preferences.getInt(key, 43) - "qs_click_behavior" -> config.qsClickBehavior = preferences.getString(key, "cycle") ?: "cycle" + "disconnect_when_not_wearing" -> config.disconnectWhenNotWearing = + preferences.getBoolean(key, false) + + "conversational_awareness_volume" -> config.conversationalAwarenessVolume = + preferences.getInt(key, 43) + + "qs_click_behavior" -> config.qsClickBehavior = + preferences.getString(key, "cycle") ?: "cycle" // AirPods state-based takeover - "takeover_when_disconnected" -> config.takeoverWhenDisconnected = preferences.getBoolean(key, true) + "takeover_when_disconnected" -> config.takeoverWhenDisconnected = + preferences.getBoolean(key, true) + "takeover_when_idle" -> config.takeoverWhenIdle = preferences.getBoolean(key, true) "takeover_when_music" -> config.takeoverWhenMusic = preferences.getBoolean(key, false) "takeover_when_call" -> config.takeoverWhenCall = preferences.getBoolean(key, true) // Phone state-based takeover - "takeover_when_ringing_call" -> config.takeoverWhenRingingCall = preferences.getBoolean(key, true) - "takeover_when_media_start" -> config.takeoverWhenMediaStart = preferences.getBoolean(key, true) + "takeover_when_ringing_call" -> config.takeoverWhenRingingCall = + preferences.getBoolean(key, true) + + "takeover_when_media_start" -> config.takeoverWhenMediaStart = + preferences.getBoolean(key, true) "left_single_press_action" -> { config.leftSinglePressAction = StemAction.fromString( @@ -1326,62 +1498,85 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList )!! setupStemActions() } + "right_single_press_action" -> { config.rightSinglePressAction = StemAction.fromString( preferences.getString(key, "PLAY_PAUSE") ?: "PLAY_PAUSE" )!! setupStemActions() } + "left_double_press_action" -> { config.leftDoublePressAction = StemAction.fromString( preferences.getString(key, "PREVIOUS_TRACK") ?: "PREVIOUS_TRACK" )!! setupStemActions() } + "right_double_press_action" -> { config.rightDoublePressAction = StemAction.fromString( preferences.getString(key, "NEXT_TRACK") ?: "NEXT_TRACK" )!! setupStemActions() } + "left_triple_press_action" -> { config.leftTriplePressAction = StemAction.fromString( preferences.getString(key, "PREVIOUS_TRACK") ?: "PREVIOUS_TRACK" )!! setupStemActions() } + "right_triple_press_action" -> { config.rightTriplePressAction = StemAction.fromString( preferences.getString(key, "PREVIOUS_TRACK") ?: "PREVIOUS_TRACK" )!! setupStemActions() } + "left_long_press_action" -> { config.leftLongPressAction = StemAction.fromString( - preferences.getString(key, "CYCLE_NOISE_CONTROL_MODES") ?: "CYCLE_NOISE_CONTROL_MODES" + preferences.getString(key, "CYCLE_NOISE_CONTROL_MODES") + ?: "CYCLE_NOISE_CONTROL_MODES" )!! setupStemActions() } + "right_long_press_action" -> { config.rightLongPressAction = StemAction.fromString( preferences.getString(key, "DIGITAL_ASSISTANT") ?: "DIGITAL_ASSISTANT" )!! setupStemActions() } - "camera_action" -> config.cameraAction = preferences.getString(key, null)?.let { StemPressType.valueOf(it) } + + "camera_action" -> config.cameraAction = + preferences.getString(key, null)?.let { StemPressType.valueOf(it) } // AirPods device information "airpods_name" -> config.airpodsName = preferences.getString(key, "") ?: "" - "airpods_model_number" -> config.airpodsModelNumber = preferences.getString(key, "") ?: "" - "airpods_manufacturer" -> config.airpodsManufacturer = preferences.getString(key, "") ?: "" - "airpods_serial_number" -> config.airpodsSerialNumber = preferences.getString(key, "") ?: "" - "airpods_left_serial_number" -> config.airpodsLeftSerialNumber = preferences.getString(key, "") ?: "" - "airpods_right_serial_number" -> config.airpodsRightSerialNumber = preferences.getString(key, "") ?: "" + "airpods_model_number" -> config.airpodsModelNumber = + preferences.getString(key, "") ?: "" + + "airpods_manufacturer" -> config.airpodsManufacturer = + preferences.getString(key, "") ?: "" + + "airpods_serial_number" -> config.airpodsSerialNumber = + preferences.getString(key, "") ?: "" + + "airpods_left_serial_number" -> config.airpodsLeftSerialNumber = + preferences.getString(key, "") ?: "" + + "airpods_right_serial_number" -> config.airpodsRightSerialNumber = + preferences.getString(key, "") ?: "" + "airpods_version1" -> config.airpodsVersion1 = preferences.getString(key, "") ?: "" "airpods_version2" -> config.airpodsVersion2 = preferences.getString(key, "") ?: "" "airpods_version3" -> config.airpodsVersion3 = preferences.getString(key, "") ?: "" - "airpods_hardware_revision" -> config.airpodsHardwareRevision = preferences.getString(key, "") ?: "" - "airpods_updater_identifier" -> config.airpodsUpdaterIdentifier = preferences.getString(key, "") ?: "" + "airpods_hardware_revision" -> config.airpodsHardwareRevision = + preferences.getString(key, "") ?: "" + + "airpods_updater_identifier" -> config.airpodsUpdaterIdentifier = + preferences.getString(key, "") ?: "" "self_mac_address" -> config.selfMacAddress = preferences.getString(key, "") ?: "" } @@ -1403,8 +1598,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } CoroutineScope(Dispatchers.IO).launch { - val logs = sharedPreferencesLogs.getStringSet(packetLogKey, mutableSetOf())?.toMutableSet() - ?: mutableSetOf() + val logs = + sharedPreferencesLogs.getStringSet(packetLogKey, mutableSetOf())?.toMutableSet() + ?: mutableSetOf() logs.add(logEntry) if (logs.size > maxLogEntries) { @@ -1460,8 +1656,15 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList var islandOpen = false var islandWindow: IslandWindow? = null + @SuppressLint("MissingPermission") - fun showIsland(service: Service, batteryPercentage: Int, type: IslandType = IslandType.CONNECTED, reversed: Boolean = false, otherDeviceName: String? = null) { + fun showIsland( + service: Service, + batteryPercentage: Int, + type: IslandType = IslandType.CONNECTED, + reversed: Boolean = false, + otherDeviceName: String? = null + ) { Log.d(TAG, "Showing island window") if (!Settings.canDrawOverlays(service)) { Log.d(TAG, "No permission for SYSTEM_ALERT_WINDOW") @@ -1469,7 +1672,14 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } CoroutineScope(Dispatchers.Main).launch { islandWindow = IslandWindow(service.applicationContext) - islandWindow!!.show(sharedPreferences.getString("name", "AirPods Pro").toString(), batteryPercentage, this@AirPodsService, type, reversed, otherDeviceName) + islandWindow!!.show( + sharedPreferences.getString("name", "AirPods Pro").toString(), + batteryPercentage, + this@AirPodsService, + type, + reversed, + otherDeviceName + ) } } @@ -1480,7 +1690,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList startActivity(intent) } - var isConnectedLocally = false + // var isConnectedLocally = false var device: BluetoothDevice? = null private lateinit var earReceiver: BroadcastReceiver @@ -1530,10 +1740,11 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList notificationManager.createNotificationChannel(connectedNotificationChannel) notificationManager.createNotificationChannel(socketFailureChannel) - val notificationSettingsIntent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS).apply { - putExtra(Settings.EXTRA_APP_PACKAGE, packageName) - putExtra(Settings.EXTRA_CHANNEL_ID, "background_service_status") - } + val notificationSettingsIntent = + Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS).apply { + putExtra(Settings.EXTRA_APP_PACKAGE, packageName) + putExtra(Settings.EXTRA_CHANNEL_ID, "background_service_status") + } val pendingIntentNotifDisable = PendingIntent.getActivity( this, 0, @@ -1542,14 +1753,10 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList ) val notification = NotificationCompat.Builder(this, "background_service_status") - .setSmallIcon(R.drawable.airpods) - .setContentTitle("Background Service Running") + .setSmallIcon(R.drawable.airpods).setContentTitle("Background Service Running") .setContentText("Useless notification, disable it by clicking on it.") - .setContentIntent(pendingIntentNotifDisable) - .setCategory(Notification.CATEGORY_SERVICE) - .setPriority(NotificationCompat.PRIORITY_LOW) - .setOngoing(true) - .build() + .setContentIntent(pendingIntentNotifDisable).setCategory(Notification.CATEGORY_SERVICE) + .setPriority(NotificationCompat.PRIORITY_LOW).setOngoing(true).build() try { startForeground(1, notification) @@ -1560,6 +1767,13 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList @OptIn(ExperimentalMaterial3Api::class) private fun showSocketConnectionFailureNotification(errorMessage: String) { + if (BuildConfig.FLAVOR != "xposed") { + Log.w( + TAG, + "Not showing socket error notification to user, the service shouldn't be running if it isn't supported." + ) + return + } val notificationManager = getSystemService(NotificationManager::class.java) val notificationIntent = Intent(this, MainActivity::class.java) @@ -1571,17 +1785,13 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList ) val notification = NotificationCompat.Builder(this, "socket_connection_failure") - .setSmallIcon(R.drawable.airpods) - .setContentTitle("AirPods Connection Issue") - .setContentText("Unable to connect to AirPods over L2CAP") - .setStyle(NotificationCompat.BigTextStyle() - .bigText("Your AirPods are connected via Bluetooth, but LibrePods couldn't connect to AirPods using L2CAP. " + - "Error: $errorMessage")) - .setContentIntent(pendingIntent) - .setCategory(Notification.CATEGORY_ERROR) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setAutoCancel(true) - .build() + .setSmallIcon(R.drawable.airpods).setContentTitle("AirPods Connection Issue") + .setContentText("Unable to connect to AirPods over L2CAP").setStyle( + NotificationCompat.BigTextStyle().bigText( + "Your AirPods are connected via Bluetooth, but LibrePods couldn't connect to AirPods using L2CAP. " + "Error: $errorMessage" + ) + ).setContentIntent(pendingIntent).setCategory(Notification.CATEGORY_ERROR) + .setPriority(NotificationCompat.PRIORITY_HIGH).setAutoCancel(true).build() notificationManager.notify(3, notification) } @@ -1589,12 +1799,14 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList fun sendANCBroadcast() { sendBroadcast(Intent(AirPodsNotifications.ANC_DATA).apply { putExtra("data", ancNotification.status) + setPackage(packageName) }) } fun sendBatteryBroadcast() { sendBroadcast(Intent(AirPodsNotifications.BATTERY_DATA).apply { putParcelableArrayListExtra("data", ArrayList(batteryNotification.getBattery())) + setPackage(packageName) }) } @@ -1608,36 +1820,46 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } fun setBatteryMetadata() { + if (BuildConfig.FLAVOR != "xposed") return device?.let { it -> SystemApisUtils.setMetadata( it, it.METADATA_UNTETHERED_CASE_BATTERY, - batteryNotification.getBattery().find { it.component == BatteryComponent.CASE }?.level.toString().toByteArray() + batteryNotification.getBattery() + .find { it.component == BatteryComponent.CASE }?.level.toString().toByteArray() ) SystemApisUtils.setMetadata( it, it.METADATA_UNTETHERED_CASE_CHARGING, - (if (batteryNotification.getBattery().find { it.component == BatteryComponent.CASE}?.status == BatteryStatus.CHARGING) "1".toByteArray() else "0".toByteArray()) + (if (batteryNotification.getBattery() + .find { it.component == BatteryComponent.CASE }?.status == BatteryStatus.CHARGING + ) "1".toByteArray() else "0".toByteArray()) ) SystemApisUtils.setMetadata( it, it.METADATA_UNTETHERED_LEFT_BATTERY, - batteryNotification.getBattery().find { it.component == BatteryComponent.LEFT }?.level.toString().toByteArray() + batteryNotification.getBattery() + .find { it.component == BatteryComponent.LEFT }?.level.toString().toByteArray() ) SystemApisUtils.setMetadata( it, it.METADATA_UNTETHERED_LEFT_CHARGING, - (if (batteryNotification.getBattery().find { it.component == BatteryComponent.LEFT}?.status == BatteryStatus.CHARGING) "1".toByteArray() else "0".toByteArray()) + (if (batteryNotification.getBattery() + .find { it.component == BatteryComponent.LEFT }?.status == BatteryStatus.CHARGING + ) "1".toByteArray() else "0".toByteArray()) ) SystemApisUtils.setMetadata( it, it.METADATA_UNTETHERED_RIGHT_BATTERY, - batteryNotification.getBattery().find { it.component == BatteryComponent.RIGHT }?.level.toString().toByteArray() + batteryNotification.getBattery() + .find { it.component == BatteryComponent.RIGHT }?.level.toString().toByteArray() ) SystemApisUtils.setMetadata( it, it.METADATA_UNTETHERED_RIGHT_CHARGING, - (if (batteryNotification.getBattery().find { it.component == BatteryComponent.RIGHT}?.status == BatteryStatus.CHARGING) "1".toByteArray() else "0".toByteArray()) + (if (batteryNotification.getBattery() + .find { it.component == BatteryComponent.RIGHT }?.status == BatteryStatus.CHARGING + ) "1".toByteArray() else "0".toByteArray()) ) } } @@ -1649,7 +1871,12 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList val widgetIds = appWidgetManager.getAppWidgetIds(componentName) val remoteViews = RemoteViews(packageName, R.layout.battery_widget).also { it -> - val openActivityIntent = PendingIntent.getActivity(this, 0, Intent(this, MainActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) + val openActivityIntent = PendingIntent.getActivity( + this, + 0, + Intent(this, MainActivity::class.java), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) it.setOnClickPendingIntent(R.id.battery_widget, openActivityIntent) val leftBattery = @@ -1659,51 +1886,33 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList val caseBattery = batteryNotification.getBattery().find { it.component == BatteryComponent.CASE } - it.setTextViewText( - R.id.left_battery_widget, - leftBattery?.let { - "${it.level}%" - } ?: "" - ) + it.setTextViewText(R.id.left_battery_widget, leftBattery?.let { + "${it.level}%" + } ?: "") it.setProgressBar( - R.id.left_battery_progress, - 100, - leftBattery?.level ?: 0, - false + R.id.left_battery_progress, 100, leftBattery?.level ?: 0, false ) it.setViewVisibility( R.id.left_charging_icon, if (leftBattery?.status == BatteryStatus.CHARGING) View.VISIBLE else View.GONE ) - it.setTextViewText( - R.id.right_battery_widget, - rightBattery?.let { - "${it.level}%" - } ?: "" - ) + it.setTextViewText(R.id.right_battery_widget, rightBattery?.let { + "${it.level}%" + } ?: "") it.setProgressBar( - R.id.right_battery_progress, - 100, - rightBattery?.level ?: 0, - false + R.id.right_battery_progress, 100, rightBattery?.level ?: 0, false ) it.setViewVisibility( R.id.right_charging_icon, if (rightBattery?.status == BatteryStatus.CHARGING) View.VISIBLE else View.GONE ) - it.setTextViewText( - R.id.case_battery_widget, - caseBattery?.let { - "${it.level}%" - } ?: "" - ) + it.setTextViewText(R.id.case_battery_widget, caseBattery?.let { + "${it.level}%" + } ?: "") it.setProgressBar( - R.id.case_battery_progress, - 100, - caseBattery?.level ?: 0, - false + R.id.case_battery_progress, 100, caseBattery?.level ?: 0, false ) it.setViewVisibility( R.id.case_charging_icon, @@ -1721,18 +1930,13 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList val charging = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS) == BatteryManager.BATTERY_STATUS_CHARGING it.setTextViewText( - R.id.phone_battery_widget, - "$batteryLevel%" + R.id.phone_battery_widget, "$batteryLevel%" ) it.setViewVisibility( - R.id.phone_charging_icon, - if (charging) View.VISIBLE else View.GONE + R.id.phone_charging_icon, if (charging) View.VISIBLE else View.GONE ) it.setProgressBar( - R.id.phone_battery_progress, - 100, - batteryLevel, - false + R.id.phone_battery_progress, 100, batteryLevel, false ) } } @@ -1754,8 +1958,10 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList val widgetIds = appWidgetManager.getAppWidgetIds(componentName) val remoteViews = RemoteViews(packageName, R.layout.noise_control_widget).also { it -> val ancStatus = ancNotification.status - val allowOffModeValue = aacpManager.controlCommandStatusList.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.ALLOW_OFF_OPTION } - val allowOffMode = allowOffModeValue?.value?.takeIf { it.isNotEmpty() }?.get(0) == 0x01.toByte() + val allowOffModeValue = + aacpManager.controlCommandStatusList.find { it.identifier == AACPManager.Companion.ControlCommandIdentifiers.ALLOW_OFF_OPTION } + val allowOffMode = + allowOffModeValue?.value?.takeIf { it.isNotEmpty() }?.get(0) == 0x01.toByte() it.setInt( R.id.widget_off_button, "setBackgroundResource", @@ -1777,8 +1983,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList if (ancStatus == 2) R.drawable.widget_button_checked_shape_end else R.drawable.widget_button_shape_end ) it.setViewVisibility( - R.id.widget_off_button, - if (allowOffMode) View.VISIBLE else View.GONE + R.id.widget_off_button, if (allowOffMode) View.VISIBLE else View.GONE ) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { it.setViewLayoutMargin( @@ -1803,9 +2008,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList @OptIn(ExperimentalMaterial3Api::class) fun updateNotificationContent( - connected: Boolean, - airpodsName: String? = null, - batteryList: List? = null + connected: Boolean, airpodsName: String? = null, batteryList: List? = null ) { val notificationManager = getSystemService(NotificationManager::class.java) var updatedNotification: Notification? @@ -1822,11 +2025,11 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList return } if (connected && (config.bleOnlyMode || socket.isConnected)) { - val updatedNotificationBuilder = NotificationCompat.Builder(this, "airpods_connection_status") - .setSmallIcon(R.drawable.airpods) - .setContentTitle(airpodsName ?: config.deviceName) - .setContentText( - """${ + val updatedNotificationBuilder = + NotificationCompat.Builder(this, "airpods_connection_status") + .setSmallIcon(R.drawable.airpods) + .setContentTitle(airpodsName ?: config.deviceName).setContentText( + """${ batteryList?.find { it.component == BatteryComponent.LEFT }?.let { if (it.status != BatteryStatus.DISCONNECTED) { "L: ${if (it.status == BatteryStatus.CHARGING) "⚡" else ""} ${it.level}%" @@ -1850,23 +2053,15 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList "" } } ?: "" - }""") - .setContentIntent(pendingIntent) - .setCategory(Notification.CATEGORY_STATUS) - .setPriority(NotificationCompat.PRIORITY_LOW) - .setOngoing(true) + }""").setContentIntent(pendingIntent).setCategory(Notification.CATEGORY_STATUS) + .setPriority(NotificationCompat.PRIORITY_LOW).setOngoing(true) if (disconnectedBecauseReversed) { updatedNotificationBuilder.addAction( - R.drawable.ic_bluetooth, - "Reconnect", - PendingIntent.getService( - this, - 0, - Intent(this, AirPodsService::class.java).apply { + R.drawable.ic_bluetooth, "Reconnect", PendingIntent.getService( + this, 0, Intent(this, AirPodsService::class.java).apply { action = "me.kavishdevar.librepods.RECONNECT_AFTER_REVERSE" - }, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + }, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) ) } @@ -1877,19 +2072,15 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList notificationManager.cancel(1) } else if (!connected) { updatedNotification = NotificationCompat.Builder(this, "background_service_status") - .setSmallIcon(R.drawable.airpods) - .setContentTitle("AirPods not connected") - .setContentText("Tap to open app") - .setContentIntent(pendingIntent) + .setSmallIcon(R.drawable.airpods).setContentTitle("AirPods not connected") + .setContentText("Tap to open app").setContentIntent(pendingIntent) .setCategory(Notification.CATEGORY_SERVICE) - .setPriority(NotificationCompat.PRIORITY_LOW) - .setOngoing(true) - .build() + .setPriority(NotificationCompat.PRIORITY_LOW).setOngoing(true).build() notificationManager.notify(1, updatedNotification) notificationManager.cancel(2) - } else if (!config.bleOnlyMode && !socket.isConnected && isConnectedLocally) { - showSocketConnectionFailureNotification("Socket created, but not connected. Is the Bluetooth process hooked?") + } else if (!config.bleOnlyMode && !socket.isConnected) { + showSocketConnectionFailureNotification("Socket created, but not connected. Check logs") } } @@ -1923,6 +2114,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } } } + private fun answerCall() { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { @@ -1936,7 +2128,8 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList val method = telephonyClass.getDeclaredMethod("getITelephony") method.isAccessible = true val telephonyInterface = method.invoke(telephonyService) - val answerCallMethod = telephonyInterface.javaClass.getDeclaredMethod("answerRingingCall") + val answerCallMethod = + telephonyInterface.javaClass.getDeclaredMethod("answerRingingCall") answerCallMethod.invoke(telephonyInterface) } @@ -1948,6 +2141,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList islandWindow?.close() } } + private fun rejectCall() { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { @@ -1991,12 +2185,10 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList private fun resToUri(resId: Int): Uri? { return try { - Uri.Builder() - .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) + Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) .authority("me.kavishdevar.librepods") .appendPath(applicationContext.resources.getResourceTypeName(resId)) - .appendPath(applicationContext.resources.getResourceEntryName(resId)) - .build() + .appendPath(applicationContext.resources.getResourceEntryName(resId)).build() } catch (_: Resources.NotFoundException) { null } @@ -2004,16 +2196,23 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList @Suppress("PrivatePropertyName") private val VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV = "+IPHONEACCEV" + @Suppress("PrivatePropertyName") private val VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL = 1 + @Suppress("PrivatePropertyName") private val APPLE = 0x004C + @Suppress("PrivatePropertyName") - private val ACTION_BATTERY_LEVEL_CHANGED = "android.bluetooth.device.action.BATTERY_LEVEL_CHANGED" + private val ACTION_BATTERY_LEVEL_CHANGED = + "android.bluetooth.device.action.BATTERY_LEVEL_CHANGED" + @Suppress("PrivatePropertyName") private val EXTRA_BATTERY_LEVEL = "android.bluetooth.device.extra.BATTERY_LEVEL" + @Suppress("PrivatePropertyName") private val PACKAGE_ASI = "com.google.android.settings.intelligence" + @Suppress("PrivatePropertyName") private val ACTION_ASI_UPDATE_BLUETOOTH_DATA = "batterywidget.impl.action.update_bluetooth_data" @@ -2027,8 +2226,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList // Calculate unified battery level (minimum of left and right) val batteryUnified = minOf( - leftBattery?.level ?: 100, - rightBattery?.level ?: 100 + leftBattery?.level ?: 100, rightBattery?.level ?: 100 ) // Check charging status @@ -2045,8 +2243,14 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList // Broadcast vendor-specific event val intent = Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT).apply { - putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV) - putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, BluetoothHeadset.AT_CMD_TYPE_SET) + putExtra( + BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, + VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV + ) + putExtra( + BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, + BluetoothHeadset.AT_CMD_TYPE_SET + ) putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments) putExtra(BluetoothDevice.EXTRA_DEVICE, device) putExtra(BluetoothDevice.EXTRA_NAME, device?.name) @@ -2098,67 +2302,57 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } private fun setMetadatas(d: BluetoothDevice) { - d.let{ device -> + if (BuildConfig.FLAVOR != "xposed") return + d.let { device -> val instance = airpodsInstance if (instance != null) { val metadataSet = SystemApisUtils.setMetadata( device, device.METADATA_MAIN_ICON, resToUri(instance.model.budCaseRes).toString().toByteArray() - ) && - SystemApisUtils.setMetadata( - device, - device.METADATA_MODEL_NAME, - instance.model.name.toByteArray() - ) && - SystemApisUtils.setMetadata( - device, - device.METADATA_DEVICE_TYPE, - device.DEVICE_TYPE_UNTETHERED_HEADSET.toByteArray() - ) && - SystemApisUtils.setMetadata( - device, - device.METADATA_UNTETHERED_CASE_ICON, - resToUri(instance.model.caseRes).toString().toByteArray() - ) && - SystemApisUtils.setMetadata( - device, - device.METADATA_UNTETHERED_RIGHT_ICON, - resToUri(instance.model.rightBudsRes).toString().toByteArray() - ) && - SystemApisUtils.setMetadata( - device, - device.METADATA_UNTETHERED_LEFT_ICON, - resToUri(instance.model.leftBudsRes).toString().toByteArray() - ) && - SystemApisUtils.setMetadata( - device, - device.METADATA_MANUFACTURER_NAME, - instance.model.manufacturer.toByteArray() - ) && - SystemApisUtils.setMetadata( - device, - device.METADATA_COMPANION_APP, - "me.kavishdevar.librepods".toByteArray() - ) && - SystemApisUtils.setMetadata( - device, - device.METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD, - "20".toByteArray() - ) && - SystemApisUtils.setMetadata( - device, - device.METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD, - "20".toByteArray() - ) && - SystemApisUtils.setMetadata( - device, - device.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD, - "20".toByteArray() - ) + ) && SystemApisUtils.setMetadata( + device, device.METADATA_MODEL_NAME, instance.model.name.toByteArray() + ) && SystemApisUtils.setMetadata( + device, + device.METADATA_DEVICE_TYPE, + device.DEVICE_TYPE_UNTETHERED_HEADSET.toByteArray() + ) && SystemApisUtils.setMetadata( + device, + device.METADATA_UNTETHERED_CASE_ICON, + resToUri(instance.model.caseRes).toString().toByteArray() + ) && SystemApisUtils.setMetadata( + device, + device.METADATA_UNTETHERED_RIGHT_ICON, + resToUri(instance.model.rightBudsRes).toString().toByteArray() + ) && SystemApisUtils.setMetadata( + device, + device.METADATA_UNTETHERED_LEFT_ICON, + resToUri(instance.model.leftBudsRes).toString().toByteArray() + ) && SystemApisUtils.setMetadata( + device, + device.METADATA_MANUFACTURER_NAME, + instance.model.manufacturer.toByteArray() + ) && SystemApisUtils.setMetadata( + device, device.METADATA_COMPANION_APP, "me.kavishdevar.librepods".toByteArray() + ) && SystemApisUtils.setMetadata( + device, + device.METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD, + "20".toByteArray() + ) && SystemApisUtils.setMetadata( + device, + device.METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD, + "20".toByteArray() + ) && SystemApisUtils.setMetadata( + device, + device.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD, + "20".toByteArray() + ) Log.d(TAG, "Metadata set: $metadataSet") } else { - Log.w(TAG, "AirPods instance is not of type AirPodsInstance, skipping metadata setting") + Log.w( + TAG, + "AirPods instance is not of type AirPodsInstance, skipping metadata setting" + ) } } } @@ -2167,15 +2361,13 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList private object bluetoothReceiver : BroadcastReceiver() { @SuppressLint("MissingPermission") override fun onReceive(context: Context?, intent: Intent) { - val bluetoothDevice = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - intent.getParcelableExtra( - "android.bluetooth.device.extra.DEVICE", - BluetoothDevice::class.java - ) - } else { - intent.getParcelableExtra("android.bluetooth.device.extra.DEVICE") as BluetoothDevice? - } + val bluetoothDevice = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + intent.getParcelableExtra( + "android.bluetooth.device.extra.DEVICE", BluetoothDevice::class.java + ) + } else { + intent.getParcelableExtra("android.bluetooth.device.extra.DEVICE") as BluetoothDevice? + } val action = intent.action val context = context?.applicationContext val name = context?.getSharedPreferences("settings", MODE_PRIVATE) @@ -2187,8 +2379,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList bluetoothDevice.fetchUuidsWithSdp() if (bluetoothDevice.uuids != null) { if (bluetoothDevice.uuids.contains(uuid)) { - val intent = - Intent(AirPodsNotifications.AIRPODS_CONNECTION_DETECTED) + val intent = Intent(AirPodsNotifications.AIRPODS_CONNECTION_DETECTED) intent.putExtra("name", name) intent.putExtra("device", bluetoothDevice) context?.sendBroadcast(intent) @@ -2218,11 +2409,14 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList @RequiresApi(Build.VERSION_CODES.R) @SuppressLint("MissingPermission", "HardwareIds") - fun takeOver(takingOverFor: String, manualTakeOverAfterReversed: Boolean = false, startHeadTrackingAgain: Boolean = false) { + fun takeOver( + takingOverFor: String, + manualTakeOverAfterReversed: Boolean = false, + startHeadTrackingAgain: Boolean = false + ) { if (takingOverFor == "reverse") { aacpManager.sendControlCommand( - AACPManager.Companion.ControlCommandIdentifiers.OWNS_CONNECTION.value, - 1 + AACPManager.Companion.ControlCommandIdentifiers.OWNS_CONNECTION.value, 1 ) aacpManager.sendMediaInformataion( localMac @@ -2231,28 +2425,40 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList localMac ) connectAudio( - this@AirPodsService, - device + this@AirPodsService, device ) otherDeviceTookOver = false } - Log.d(TAG, "owns connection: ${aacpManager.getControlCommandStatus(AACPManager.Companion.ControlCommandIdentifiers.OWNS_CONNECTION)?.value?.get(0)?.toInt()}") - if (isConnectedLocally) { + Log.d( + TAG, "owns connection: ${ + aacpManager.getControlCommandStatus(AACPManager.Companion.ControlCommandIdentifiers.OWNS_CONNECTION)?.value?.get( + 0 + )?.toInt() + }" + ) + if (!::socket.isInitialized) return + if (socket.isConnected) { + if (BuildConfig.FLAVOR != "xposed") { + Log.d(TAG, "not taking over, vendorid is probably not set to apple") + return + } if (aacpManager.getControlCommandStatus(AACPManager.Companion.ControlCommandIdentifiers.OWNS_CONNECTION)?.value[0]?.toInt() != 1 || (aacpManager.audioSource?.mac != localMac && aacpManager.audioSource?.type != AACPManager.Companion.AudioSourceType.NONE)) { if (disconnectedBecauseReversed) { if (manualTakeOverAfterReversed) { Log.d(TAG, "forcefully taking over despite reverse as user requested") disconnectedBecauseReversed = false } else { - Log.d(TAG, "connected locally, but can not hijack as other device had reversed") + Log.d( + TAG, + "connected locally, but can not hijack as other device had reversed" + ) return } } Log.d(TAG, "already connected locally, hijacking connection by asking AirPods") aacpManager.sendControlCommand( - AACPManager.Companion.ControlCommandIdentifiers.OWNS_CONNECTION.value, - 1 + AACPManager.Companion.ControlCommandIdentifiers.OWNS_CONNECTION.value, 1 ) aacpManager.sendMediaInformataion( localMac @@ -2265,8 +2471,15 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList ) otherDeviceTookOver = false connectAudio(this, device) - showIsland(this, batteryNotification.getBattery().find { it.component == BatteryComponent.LEFT}?.level!!.coerceAtMost(batteryNotification.getBattery().find { it.component == BatteryComponent.RIGHT}?.level!!), - IslandType.CONNECTED) + showIsland( + this, + batteryNotification.getBattery() + .find { it.component == BatteryComponent.LEFT }?.level!!.coerceAtMost( + batteryNotification.getBattery() + .find { it.component == BatteryComponent.RIGHT }?.level!! + ), + IslandType.CONNECTED + ) CoroutineScope(Dispatchers.IO).launch { delay(500) // a2dp takes time, and so does taking control + AirPods pause it for no reason after connecting @@ -2286,7 +2499,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } } } else { - Log.d(TAG, "Already connected locally and already own connection, skipping takeover") + Log.d( + TAG, "Already connected locally and already own connection, skipping takeover" + ) } return } @@ -2357,25 +2572,32 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList // In BLE-only mode, just show connecting status without actual L2CAP connection Log.d(TAG, "BLE-only mode: showing connecting status without L2CAP connection") updateNotificationContent( - true, - config.deviceName, - batteryNotification.getBattery() + true, config.deviceName, batteryNotification.getBattery() ) // Set a temporary connecting state - isConnectedLocally = false // Keep as false since we're not actually connecting to L2CAP +// isConnectedLocally = false // Keep as false since we're not actually connecting to L2CAP } else { connectToSocket(bluetoothAdapter, device!!) connectAudio(this, device) - isConnectedLocally = true +// isConnectedLocally = true } } - showIsland(this, batteryNotification.getBattery().find { it.component == BatteryComponent.LEFT}?.level!!.coerceAtMost(batteryNotification.getBattery().find { it.component == BatteryComponent.RIGHT}?.level!!), - IslandType.TAKING_OVER) + showIsland( + this, + batteryNotification.getBattery() + .find { it.component == BatteryComponent.LEFT }?.level!!.coerceAtMost( + batteryNotification.getBattery() + .find { it.component == BatteryComponent.RIGHT }?.level!! + ), + IslandType.TAKING_OVER + ) // CrossDevice.isAvailable = false } - private fun createBluetoothSocket(adapter: BluetoothAdapter, device: BluetoothDevice, uuid: ParcelUuid): BluetoothSocket { + private fun createBluetoothSocket( + adapter: BluetoothAdapter, device: BluetoothDevice, uuid: ParcelUuid + ): BluetoothSocket { val type = 3 // L2CAP val constructorSpecs = listOf( arrayOf(adapter, device, type, true, true, 0x1001, uuid), // A16QPR3 @@ -2401,7 +2623,8 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList Log.d(TAG, "Trying constructor signature #${index + 1}") attemptedConstructors++ - val paramTypes = params.map { it::class.javaPrimitiveType ?: it::class.java }.toTypedArray() + val paramTypes = + params.map { it::class.javaPrimitiveType ?: it::class.java }.toTypedArray() val constructor = BluetoothSocket::class.java.getDeclaredConstructor(*paramTypes) constructor.isAccessible = true return constructor.newInstance(*params) as BluetoothSocket @@ -2412,175 +2635,187 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } } - val errorMessage = "Failed to create BluetoothSocket after trying $attemptedConstructors constructor signatures" + val errorMessage = + "Failed to create BluetoothSocket after trying $attemptedConstructors constructor signatures" Log.e(TAG, errorMessage) showSocketConnectionFailureNotification(errorMessage) throw lastException ?: IllegalStateException(errorMessage) } @SuppressLint("MissingPermission", "UnspecifiedRegisterReceiverFlag") - fun connectToSocket(adapter: BluetoothAdapter, device: BluetoothDevice, manual: Boolean = false) { + fun connectToSocket( + adapter: BluetoothAdapter, device: BluetoothDevice, manual: Boolean = false + ) { Log.d(TAG, " Connecting to socket") val uuid: ParcelUuid = ParcelUuid.fromString("74ec2172-0bad-4d01-8f77-997b2be0722a") - if (!isConnectedLocally) { - socket = try { - createBluetoothSocket(adapter, device, uuid) - } catch (e: Exception) { - Log.e(TAG, "Failed to create BluetoothSocket: ${e.message}") - showSocketConnectionFailureNotification("Failed to create Bluetooth socket: ${e.localizedMessage}") - return - } +// if (!isConnectedLocally) { + socket = try { + createBluetoothSocket(adapter, device, uuid) + } catch (e: Exception) { + Log.e(TAG, "Failed to create BluetoothSocket: ${e.message}") + showSocketConnectionFailureNotification("Failed to create Bluetooth socket: ${e.localizedMessage}") + return + } - try { - runBlocking { - withTimeout(5000L) { - try { - socket.connect() - isConnectedLocally = true - this@AirPodsService.device = device + try { + runBlocking { + withTimeout(5000L) { + try { + socket.connect() +// isConnectedLocally = true + this@AirPodsService.device = device - BluetoothConnectionManager.setCurrentConnection(socket, device) + BluetoothConnectionManager.setCurrentConnection(socket, device) + if (BuildConfig.FLAVOR == "xposed") { attManager = ATTManager(adapter, device) attManager!!.connect() + } - // Create AirPodsInstance from stored config if available - if (airpodsInstance == null && config.airpodsModelNumber.isNotEmpty()) { - val model = AirPodsModels.getModelByModelNumber(config.airpodsModelNumber) - if (model != null) { - airpodsInstance = AirPodsInstance( - name = config.airpodsName, - model = model, - actualModelNumber = config.airpodsModelNumber, - serialNumber = config.airpodsSerialNumber, - leftSerialNumber = config.airpodsLeftSerialNumber, - rightSerialNumber = config.airpodsRightSerialNumber, - version1 = config.airpodsVersion1, - version2 = config.airpodsVersion2, - version3 = config.airpodsVersion3, - aacpManager = aacpManager, - attManager = attManager - ) - } - } - - updateNotificationContent( - true, - config.deviceName, - batteryNotification.getBattery() - ) - Log.d(TAG, " Socket connected") - } catch (e: Exception) { - Log.d(TAG, " Socket not connected, ${e.message}") - if (manual) { - sendToast( - "Couldn't connect to socket: ${e.localizedMessage}" + // Create AirPodsInstance from stored config if available + if (airpodsInstance == null && config.airpodsModelNumber.isNotEmpty()) { + val model = + AirPodsModels.getModelByModelNumber(config.airpodsModelNumber) + if (model != null) { + airpodsInstance = AirPodsInstance( + name = config.airpodsName, + model = model, + actualModelNumber = config.airpodsModelNumber, + serialNumber = config.airpodsSerialNumber, + leftSerialNumber = config.airpodsLeftSerialNumber, + rightSerialNumber = config.airpodsRightSerialNumber, + version1 = config.airpodsVersion1, + version2 = config.airpodsVersion2, + version3 = config.airpodsVersion3, + aacpManager = aacpManager, + attManager = attManager ) - } else { - showSocketConnectionFailureNotification("Couldn't connect to socket: ${e.localizedMessage}") } - return@withTimeout + } + + updateNotificationContent( + true, config.deviceName, batteryNotification.getBattery() + ) + Log.d(TAG, " Socket connected") + } catch (e: Exception) { + Log.d( + TAG, " Socket not connected, ${e.message}" + ) + if (manual) { + sendToast( + "Couldn't connect to socket: ${e.localizedMessage}" + ) + } else { + showSocketConnectionFailureNotification("Couldn't connect to socket: ${e.localizedMessage}") + } + return@withTimeout // throw e // lol how did i not catch this before... gonna comment this line instead of removing to preserve history - } } } - if (!socket.isConnected) { - Log.d(TAG, " Socket not connected") - if (manual) { - sendToast( - "Couldn't connect to socket: timeout." - ) - } else { - showSocketConnectionFailureNotification("Couldn't connect to socket: Timeout") - } - return + } + if (!socket.isConnected) { + Log.d(TAG, " Socket not connected") + if (manual) { + sendToast( + "Couldn't connect to socket: timeout." + ) + } else { + showSocketConnectionFailureNotification("Couldn't connect to socket: Timeout") } - this@AirPodsService.device = device - socket.let { + return + } + this@AirPodsService.device = device + socket.let { + aacpManager.sendPacket(aacpManager.createHandshakePacket()) + aacpManager.sendSetFeatureFlagsPacket() + aacpManager.sendNotificationRequest() + Log.d(TAG, "Requesting proximity keys") + aacpManager.sendRequestProximityKeys((AACPManager.Companion.ProximityKeyType.IRK.value + AACPManager.Companion.ProximityKeyType.ENC_KEY.value).toByte()) + CoroutineScope(Dispatchers.IO).launch { aacpManager.sendPacket(aacpManager.createHandshakePacket()) + delay(200) aacpManager.sendSetFeatureFlagsPacket() + delay(200) aacpManager.sendNotificationRequest() - Log.d(TAG, "Requesting proximity keys") + delay(200) + aacpManager.sendSomePacketIDontKnowWhatItIs() + delay(200) aacpManager.sendRequestProximityKeys((AACPManager.Companion.ProximityKeyType.IRK.value + AACPManager.Companion.ProximityKeyType.ENC_KEY.value).toByte()) - CoroutineScope(Dispatchers.IO).launch { + if (!handleIncomingCallOnceConnected) startHeadTracking() else handleIncomingCall() + Handler(Looper.getMainLooper()).postDelayed({ aacpManager.sendPacket(aacpManager.createHandshakePacket()) - delay(200) aacpManager.sendSetFeatureFlagsPacket() - delay(200) aacpManager.sendNotificationRequest() - delay(200) - aacpManager.sendSomePacketIDontKnowWhatItIs() - delay(200) - aacpManager.sendRequestProximityKeys((AACPManager.Companion.ProximityKeyType.IRK.value+AACPManager.Companion.ProximityKeyType.ENC_KEY.value).toByte()) - if (!handleIncomingCallOnceConnected) startHeadTracking() else handleIncomingCall() - Handler(Looper.getMainLooper()).postDelayed({ - aacpManager.sendPacket(aacpManager.createHandshakePacket()) - aacpManager.sendSetFeatureFlagsPacket() - aacpManager.sendNotificationRequest() - aacpManager.sendRequestProximityKeys(AACPManager.Companion.ProximityKeyType.IRK.value) - if (!handleIncomingCallOnceConnected) stopHeadTracking() - }, 5000) + aacpManager.sendRequestProximityKeys(AACPManager.Companion.ProximityKeyType.IRK.value) + if (!handleIncomingCallOnceConnected) stopHeadTracking() + }, 5000) - sendBroadcast( - Intent(AirPodsNotifications.AIRPODS_CONNECTED) - .putExtra("device", device) - ) + sendBroadcast( + Intent(AirPodsNotifications.AIRPODS_CONNECTED).putExtra("device", device) + .apply { + setPackage(packageName) + }) - setupStemActions() + setupStemActions() - while (socket.isConnected) { - socket.let { it -> - val buffer = ByteArray(1024) - val bytesRead = it.inputStream.read(buffer) - var data: ByteArray - 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) } + while (socket.isConnected) { + socket.let { it -> + val buffer = ByteArray(1024) + val bytesRead = it.inputStream.read(buffer) + var data: ByteArray + if (bytesRead > 0) { + data = buffer.copyOfRange(0, bytesRead) + sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DATA).apply { + putExtra("data", buffer.copyOfRange(0, bytesRead)) + setPackage(packageName) + }) + val bytes = buffer.copyOfRange(0, bytesRead) + val formattedHex = bytes.joinToString(" ") { "%02X".format(it) } // CrossDevice.sendReceivedPacket(bytes) - updateNotificationContent( - true, - sharedPreferences.getString("name", device.name), - batteryNotification.getBattery() - ) + updateNotificationContent( + true, + sharedPreferences.getString("name", device.name), + batteryNotification.getBattery() + ) - aacpManager.receivePacket(data) + aacpManager.receivePacket(data) - if (!isHeadTrackingData(data)) { - Log.d("AirPodsData", "Data received: $formattedHex") - logPacket(data, "AirPods") - } - - } else if (bytesRead == -1) { - Log.d("AirPods Service", "Socket closed (bytesRead = -1)") - sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED)) - aacpManager.disconnected() - return@launch + if (!isHeadTrackingData(data)) { + Log.d("AirPodsData", "Data received: $formattedHex") + logPacket(data, "AirPods") } + + } else if (bytesRead == -1) { + Log.d("AirPods Service", "Socket closed (bytesRead = -1)") + sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED).apply { + setPackage(packageName) + }) + aacpManager.disconnected() + return@launch } } - Log.d("AirPods Service", "Socket closed") - isConnectedLocally = false - socket.close() - aacpManager.disconnected() - updateNotificationContent(false) - sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED)) } + Log.d("AirPods Service", "Socket closed") +// isConnectedLocally = false + socket.close() + aacpManager.disconnected() + updateNotificationContent(false) + sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED).apply { + setPackage(packageName) + }) } - } catch (e: Exception) { - e.printStackTrace() - Log.d(TAG, "Failed to connect to socket: ${e.message}") - showSocketConnectionFailureNotification("Failed to establish connection: ${e.localizedMessage}") - isConnectedLocally = false - this@AirPodsService.device = device - updateNotificationContent(false) } - } else { - Log.d(TAG, "Already connected locally, skipping socket connection (isConnectedLocally = $isConnectedLocally, socket.isConnected = ${this::socket.isInitialized && socket.isConnected})") + } catch (e: Exception) { + e.printStackTrace() + Log.d(TAG, "Failed to connect to socket: ${e.message}") + showSocketConnectionFailureNotification("Failed to establish connection: ${e.localizedMessage}") +// isConnectedLocally = false + this@AirPodsService.device = device + updateNotificationContent(false) } +// } else { +// Log.d(TAG, "Already connected locally, skipping socket connection (isConnectedLocally = $isConnectedLocally, socket.isConnected = ${this::socket.isInitialized && socket.isConnected})") +// } } fun disconnectForCD() { @@ -2588,8 +2823,15 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList socket.close() MediaController.pausedWhileTakingOver = false Log.d(TAG, "Disconnected from AirPods, showing island.") - showIsland(this, batteryNotification.getBattery().find { it.component == BatteryComponent.LEFT}?.level!!.coerceAtMost(batteryNotification.getBattery().find { it.component == BatteryComponent.RIGHT}?.level!!), - IslandType.MOVED_TO_REMOTE) + showIsland( + this, + batteryNotification.getBattery() + .find { it.component == BatteryComponent.LEFT }?.level!!.coerceAtMost( + batteryNotification.getBattery() + .find { it.component == BatteryComponent.RIGHT }?.level!! + ), + IslandType.MOVED_TO_REMOTE + ) val bluetoothAdapter = getSystemService(BluetoothManager::class.java).adapter bluetoothAdapter.getProfileProxy(this, object : BluetoothProfile.ServiceListener { override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { @@ -2604,18 +2846,20 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList override fun onServiceDisconnected(profile: Int) {} }, BluetoothProfile.A2DP) - isConnectedLocally = false +// isConnectedLocally = false // CrossDevice.isAvailable = true } fun disconnectAirPods() { if (!this::socket.isInitialized) return socket.close() - isConnectedLocally = false +// isConnectedLocally = false aacpManager.disconnected() attManager?.disconnect() updateNotificationContent(false) - sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED)) + sendBroadcast(Intent(AirPodsNotifications.AIRPODS_DISCONNECTED).apply { + setPackage(packageName) + }) val bluetoothAdapter = getSystemService(BluetoothManager::class.java).adapter bluetoothAdapter.getProfileProxy(this, object : BluetoothProfile.ServiceListener { @@ -2673,11 +2917,12 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList Log.d(TAG, "Already disconnected from A2DP") return } - val method = - proxy.javaClass.getMethod("setConnectionPolicy", BluetoothDevice::class.java, Int::class.java) + val method = proxy.javaClass.getMethod( + "setConnectionPolicy", BluetoothDevice::class.java, Int::class.java + ) method.invoke(proxy, device, 0) } catch (e: Exception) { - e.printStackTrace() + Log.w(TAG, "we probably do not have BLUETOOTH_PRIVILEGED") } finally { bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, proxy) } @@ -2686,24 +2931,25 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList override fun onServiceDisconnected(profile: Int) {} }, BluetoothProfile.A2DP) +// requires protected permission (MODIFY_PHONE_STATE) +// bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener { +// override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { +// if (profile == BluetoothProfile.HEADSET) { +// try { +// val method = +// proxy.javaClass.getMethod("setConnectionPolicy", BluetoothDevice::class.java, Int::class.java) +// method.invoke(proxy, device, 0) +// } catch (e: Exception) { +// e.printStackTrace() +// } finally { +// bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy) +// } +// } +// } +// +// override fun onServiceDisconnected(profile: Int) {} +// }, BluetoothProfile.HEADSET) - bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener { - override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { - if (profile == BluetoothProfile.HEADSET) { - try { - val method = - proxy.javaClass.getMethod("setConnectionPolicy", BluetoothDevice::class.java, Int::class.java) - method.invoke(proxy, device, 0) - } catch (e: Exception) { - e.printStackTrace() - } finally { - bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy) - } - } - } - - override fun onServiceDisconnected(profile: Int) {} - }, BluetoothProfile.HEADSET) } fun connectAudio(context: Context, device: BluetoothDevice?) { @@ -2713,13 +2959,17 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { if (profile == BluetoothProfile.A2DP) { try { - val policyMethod = proxy.javaClass.getMethod("setConnectionPolicy", BluetoothDevice::class.java, Int::class.java) + val policyMethod = proxy.javaClass.getMethod( + "setConnectionPolicy", BluetoothDevice::class.java, Int::class.java + ) policyMethod.invoke(proxy, device, 100) val connectMethod = proxy.javaClass.getMethod("connect", BluetoothDevice::class.java) - connectMethod.invoke(proxy, device) // reduces the slight delay between allowing and actually connecting + connectMethod.invoke( + proxy, device + ) // reduces the slight delay between allowing and actually connecting } catch (e: Exception) { - e.printStackTrace() + Log.w(TAG, "we probably do not have BLUETOOTH_PRIVILEGED") } finally { bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, proxy) if (MediaController.pausedWhileTakingOver) { @@ -2731,26 +2981,26 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList 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 policyMethod = proxy.javaClass.getMethod("setConnectionPolicy", BluetoothDevice::class.java, Int::class.java) - policyMethod.invoke(proxy, device, 100) - val connectMethod = - proxy.javaClass.getMethod("connect", BluetoothDevice::class.java) - connectMethod.invoke(proxy, device) - } catch (e: Exception) { - e.printStackTrace() - } finally { - bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy) - } - } - } - - override fun onServiceDisconnected(profile: Int) {} - }, BluetoothProfile.HEADSET) +// requires protected permission (MODIFY_PHONE_STATE) +// bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener { +// override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { +// if (profile == BluetoothProfile.HEADSET) { +// try { +// val policyMethod = proxy.javaClass.getMethod("setConnectionPolicy", BluetoothDevice::class.java, Int::class.java) +// policyMethod.invoke(proxy, device, 100) +// val connectMethod = +// proxy.javaClass.getMethod("connect", BluetoothDevice::class.java) +// connectMethod.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) { @@ -2798,7 +3048,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList e.printStackTrace() } telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE) - isConnectedLocally = false +// isConnectedLocally = false // CrossDevice.isAvailable = true super.onDestroy() } @@ -2807,8 +3057,12 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList fun startHeadTracking() { isHeadTrackingActive = true - val useAlternatePackets = sharedPreferences.getBoolean("use_alternate_head_tracking_packets", false) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && aacpManager.getControlCommandStatus(AACPManager.Companion.ControlCommandIdentifiers.OWNS_CONNECTION)?.value?.get(0)?.toInt() != 1) { + val useAlternatePackets = + sharedPreferences.getBoolean("use_alternate_head_tracking_packets", true) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && aacpManager.getControlCommandStatus( + AACPManager.Companion.ControlCommandIdentifiers.OWNS_CONNECTION + )?.value?.get(0)?.toInt() != 1 + ) { takeOver("call", startHeadTrackingAgain = true) Log.d(TAG, "Taking over for head tracking") } else { @@ -2823,7 +3077,8 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } fun stopHeadTracking() { - val useAlternatePackets = sharedPreferences.getBoolean("use_alternate_head_tracking_packets", false) + val useAlternatePackets = + sharedPreferences.getBoolean("use_alternate_head_tracking_packets", true) if (useAlternatePackets) { aacpManager.sendDataPacket(aacpManager.createAlternateStopHeadTrackingPacket()) } else { @@ -2833,7 +3088,7 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } @SuppressLint("MissingPermission") - fun reconnectFromSavedMac(){ + fun reconnectFromSavedMac() { val bluetoothAdapter = getSystemService(BluetoothManager::class.java).adapter device = bluetoothAdapter.bondedDevices.find { it.address == macAddress @@ -2846,6 +3101,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList } } + fun isConnected(): Boolean { + return if (::socket.isInitialized) socket.isConnected else false + } } private fun Int.dpToPx(): Int { diff --git a/android/app/src/main/java/me/kavishdevar/librepods/ui/theme/Theme.kt b/android/app/src/main/java/me/kavishdevar/librepods/ui/theme/Theme.kt index 5653123..cd96f1f 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/ui/theme/Theme.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/ui/theme/Theme.kt @@ -61,4 +61,4 @@ fun LibrePodsTheme( typography = Typography, content = content ) -} \ No newline at end of file +} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/utils/AACPManager.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/AACPManager.kt index 655b718..6149666 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/utils/AACPManager.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/utils/AACPManager.kt @@ -55,6 +55,7 @@ class AACPManager { const val TIPI_3: Byte = 0x0C // Don't know this one const val SMART_ROUTING_RESP: Byte = 0x11 const val SEND_CONNECTED_MAC: Byte = 0x14 + const val AUDIO_SOURCE_2: Byte = 0x0C // seems redundant? } private val HEADER_BYTES = byteArrayOf(0x04, 0x00, 0x04, 0x00) @@ -216,7 +217,7 @@ class AACPManager { var audioSource: AudioSource? = null private set - var eqData = FloatArray(8) { 0.0f } + var eqData = FloatArray(8) private set var eqOnPhone: Boolean = false @@ -265,6 +266,7 @@ class AACPManager { fun onConnectedDevicesReceived(connectedDevices: List) fun onOwnershipToFalseRequest(sender: String, reasonReverseTapped: Boolean) fun onShowNearbyUI(sender: String) + fun onEQPacketReceived(eqData: FloatArray) } fun parseStemPressResponse(data: ByteArray): Pair { @@ -458,21 +460,27 @@ class AACPManager { controlCommand.value.joinToString(" ") { "%02X".format(it) } }" ) - Log.d( - TAG, "Control command list is now: ${ - controlCommandStatusList.joinToString(", ") { it -> - "${it.identifier.name} (${it.identifier.value.toHexString()}) - ${ - it.value.joinToString( - " " - ) { "%02X".format(it) } - }" + + val controlCommandListText = try { + controlCommandStatusList.joinToString(", ") { it -> + "${it.identifier.name} (${it.identifier.value.toHexString()}) - ${ + it.value.joinToString( + " " + ) { "%02X".format(it) } + }" + } + } catch (e: Exception) { + e.message } - }") + + Log.d( + TAG, "Control command list is now: $controlCommandListText") val controlCommandIdentifier = ControlCommandIdentifiers.fromByte(controlCommand.identifier) if (controlCommandIdentifier != null) { controlCommandListeners[controlCommandIdentifier]?.forEach { listener -> + Log.d(TAG, "calling listener for ${controlCommandIdentifier.name}") listener.onControlCommandReceived(controlCommand) } } else { @@ -585,7 +593,7 @@ class AACPManager { eqOnMedia = (packet[10] == 0x01.toByte()) eqOnPhone = (packet[11] == 0x01.toByte()) - // there are 4 eqs. i am not sure what those are for, maybe all 4 listening modes, or maybe phone+media left+right, but then there shouldn't be another flag for phone/media enabled. just directly the EQ... weird. + // there are 4 eqs. i am not sure what those are for, maybe all 4 listening modes, or maybe phone+media left+right, but then there shouldn't be another flag for phone/media visible. just directly the EQ... weird. // the EQs are little endian floats val eq1 = ByteBuffer.wrap(packet, 12, 32).order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer() val eq2 = ByteBuffer.wrap(packet, 44, 32).order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer() @@ -594,11 +602,14 @@ class AACPManager { // for now, taking just the first EQ eqData = FloatArray(8) { i -> eq1.get(i) } + Log.d(TAG, "EQ Data set to: ${eqData.toList()}, eqOnPhone: $eqOnPhone, eqOnMedia: $eqOnMedia") + + callback?.onEQPacketReceived(eqData) } Opcodes.INFORMATION -> { - Log.e(TAG, "Parsing Information Packet") + Log.d(TAG, "Parsing Information Packet") val information = parseInformationPacket(packet) callback?.onDeviceInformationReceived(information) } diff --git a/android/app/src/main/java/me/kavishdevar/librepods/utils/CrossDevice.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/CrossDevice.kt deleted file mode 100644 index 026d0a3..0000000 --- a/android/app/src/main/java/me/kavishdevar/librepods/utils/CrossDevice.kt +++ /dev/null @@ -1,289 +0,0 @@ -/* - LibrePods - AirPods liberated from Apple’s ecosystem - Copyright (C) 2025 LibrePods contributors - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -@file:OptIn(ExperimentalEncodingApi::class) - -package me.kavishdevar.librepods.utils - -import android.annotation.SuppressLint -import android.bluetooth.BluetoothAdapter -import android.bluetooth.BluetoothManager -import android.bluetooth.BluetoothServerSocket -import android.bluetooth.BluetoothSocket -import android.bluetooth.le.AdvertiseCallback -import android.bluetooth.le.AdvertiseData -import android.bluetooth.le.AdvertiseSettings -import android.bluetooth.le.BluetoothLeAdvertiser -import android.content.Context -import android.content.Intent -import android.content.SharedPreferences -import android.os.ParcelUuid -import android.util.Log -import androidx.core.content.edit -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import me.kavishdevar.librepods.services.ServiceManager -import java.io.IOException -import java.util.UUID -import kotlin.io.encoding.ExperimentalEncodingApi - -enum class CrossDevicePackets(val packet: ByteArray) { - AIRPODS_CONNECTED(byteArrayOf(0x00, 0x01, 0x00, 0x01)), - AIRPODS_DISCONNECTED(byteArrayOf(0x00, 0x01, 0x00, 0x00)), - REQUEST_DISCONNECT(byteArrayOf(0x00, 0x02, 0x00, 0x00)), - REQUEST_BATTERY_BYTES(byteArrayOf(0x00, 0x02, 0x00, 0x01)), - REQUEST_ANC_BYTES(byteArrayOf(0x00, 0x02, 0x00, 0x02)), - REQUEST_CONNECTION_STATUS(byteArrayOf(0x00, 0x02, 0x00, 0x03)), - AIRPODS_DATA_HEADER(byteArrayOf(0x00, 0x04, 0x00, 0x01)), -} - - -object CrossDevice { - var initialized = false - private val uuid = UUID.fromString("1abbb9a4-10e4-4000-a75c-8953c5471342") - private var serverSocket: BluetoothServerSocket? = null - private var clientSocket: BluetoothSocket? = null - private lateinit var bluetoothAdapter: BluetoothAdapter - private lateinit var bluetoothLeAdvertiser: BluetoothLeAdvertiser - private const val MANUFACTURER_ID = 0x1234 - private const val MANUFACTURER_DATA = "ALN_AirPods" - var isAvailable: Boolean = false // set to true when airpods are connected to another device - var batteryBytes: ByteArray = byteArrayOf() - var ancBytes: ByteArray = byteArrayOf() - private lateinit var sharedPreferences: SharedPreferences - private const val PACKET_LOG_KEY = "packet_log" - private var earDetectionStatus = listOf(false, false) - var disconnectionRequested = false - - @SuppressLint("MissingPermission") - fun init(context: Context) { - CoroutineScope(Dispatchers.IO).launch { - Log.d("CrossDevice", "Initializing CrossDevice") - sharedPreferences = context.getSharedPreferences("packet_logs", Context.MODE_PRIVATE) - sharedPreferences.edit { putBoolean("CrossDeviceIsAvailable", false)} - this@CrossDevice.bluetoothAdapter = context.getSystemService(BluetoothManager::class.java).adapter - this@CrossDevice.bluetoothLeAdvertiser = bluetoothAdapter.bluetoothLeAdvertiser - // startAdvertising() - startServer() - initialized = true - } - } - - @SuppressLint("MissingPermission") - private fun startServer() { - CoroutineScope(Dispatchers.IO).launch { - if (!bluetoothAdapter.isEnabled) return@launch -// serverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord("ALNCrossDevice", uuid) - Log.d("CrossDevice", "Server started") - while (serverSocket != null) { - if (!bluetoothAdapter.isEnabled) { - serverSocket?.close() - break - } - if (clientSocket != null) { - try { - clientSocket!!.close() - } catch (e: IOException) { - e.printStackTrace() - } - } - try { - val socket = serverSocket!!.accept() - handleClientConnection(socket) - } catch (e: IOException) { } - } - } - } - - @SuppressLint("MissingPermission", "unused") - private fun startAdvertising() { - CoroutineScope(Dispatchers.IO).launch { - val settings = AdvertiseSettings.Builder() - .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) - .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) - .setConnectable(true) - .build() - - val data = AdvertiseData.Builder() - .setIncludeDeviceName(true) - .addManufacturerData(MANUFACTURER_ID, MANUFACTURER_DATA.toByteArray()) - .addServiceUuid(ParcelUuid(uuid)) - .build() - try { - bluetoothLeAdvertiser.startAdvertising(settings, data, advertiseCallback) - } catch (e: Exception) { - Log.e("CrossDevice", "Failed to start BLE Advertising: ${e.message}") - } - Log.d("CrossDevice", "BLE Advertising started") - } - } - - private val advertiseCallback = object : AdvertiseCallback() { - override fun onStartSuccess(settingsInEffect: AdvertiseSettings) { - Log.d("CrossDevice", "BLE Advertising started successfully") - } - - override fun onStartFailure(errorCode: Int) { - Log.e("CrossDevice", "BLE Advertising failed with error code: $errorCode") - } - } - - fun setAirPodsConnected(connected: Boolean) { - if (connected) { - isAvailable = false - sharedPreferences.edit { putBoolean("CrossDeviceIsAvailable", false)} - clientSocket?.outputStream?.write(CrossDevicePackets.AIRPODS_CONNECTED.packet) - } else { - clientSocket?.outputStream?.write(CrossDevicePackets.AIRPODS_DISCONNECTED.packet) - // Reset state variables - isAvailable = true - } - } - - fun sendReceivedPacket(packet: ByteArray) { - if (clientSocket == null || clientSocket!!.outputStream != null) { - return - } - clientSocket?.outputStream?.write(CrossDevicePackets.AIRPODS_DATA_HEADER.packet + packet) - } - - private fun logPacket(packet: ByteArray, source: String) { - val packetHex = packet.joinToString(" ") { "%02X".format(it) } - val logEntry = "$source: $packetHex" - val logs = sharedPreferences.getStringSet(PACKET_LOG_KEY, mutableSetOf())?.toMutableSet() ?: mutableSetOf() - logs.add(logEntry) - sharedPreferences.edit { putStringSet(PACKET_LOG_KEY, logs)} - } - - @SuppressLint("MissingPermission") - private fun handleClientConnection(socket: BluetoothSocket) { - Log.d("CrossDevice", "Client connected") - notifyAirPodsConnectedRemotely(ServiceManager.getService()?.applicationContext!!) - clientSocket = socket - val inputStream = socket.inputStream - val buffer = ByteArray(1024) - var bytes: Int - setAirPodsConnected(ServiceManager.getService()?.isConnectedLocally == true) - while (true) { - try { - bytes = inputStream.read(buffer) - } catch (e: IOException) { - e.printStackTrace() - notifyAirPodsDisconnectedRemotely(ServiceManager.getService()?.applicationContext!!) - val s = serverSocket?.accept() - if (s != null) { - handleClientConnection(s) - } - break - } - var packet = buffer.copyOf(bytes) - logPacket(packet, "Relay") - Log.d("CrossDevice", "Received packet: ${packet.joinToString("") { "%02x".format(it) }}") - if (bytes == -1) { - notifyAirPodsDisconnectedRemotely(ServiceManager.getService()?.applicationContext!!) - break - } else if (packet.contentEquals(CrossDevicePackets.REQUEST_DISCONNECT.packet) || packet.contentEquals(CrossDevicePackets.REQUEST_DISCONNECT.packet + CrossDevicePackets.AIRPODS_DATA_HEADER.packet)) { - ServiceManager.getService()?.disconnectForCD() - disconnectionRequested = true - CoroutineScope(Dispatchers.IO).launch { - delay(1000) - disconnectionRequested = false - } - } else if (packet.contentEquals(CrossDevicePackets.AIRPODS_CONNECTED.packet)) { - isAvailable = true - sharedPreferences.edit { putBoolean("CrossDeviceIsAvailable", true)} - } else if (packet.contentEquals(CrossDevicePackets.AIRPODS_DISCONNECTED.packet)) { - isAvailable = false - sharedPreferences.edit { putBoolean("CrossDeviceIsAvailable", false)} - } else if (packet.contentEquals(CrossDevicePackets.REQUEST_BATTERY_BYTES.packet)) { - Log.d("CrossDevice", "Received battery request, battery data: ${batteryBytes.joinToString("") { "%02x".format(it) }}") - sendRemotePacket(batteryBytes) - } else if (packet.contentEquals(CrossDevicePackets.REQUEST_ANC_BYTES.packet)) { - Log.d("CrossDevice", "Received ANC request") - sendRemotePacket(ancBytes) - } else if (packet.contentEquals(CrossDevicePackets.REQUEST_CONNECTION_STATUS.packet)) { - Log.d("CrossDevice", "Received connection status request") - sendRemotePacket(if (ServiceManager.getService()?.isConnectedLocally == true) CrossDevicePackets.AIRPODS_CONNECTED.packet else CrossDevicePackets.AIRPODS_DISCONNECTED.packet) - } else { - if (packet.sliceArray(0..3).contentEquals(CrossDevicePackets.AIRPODS_DATA_HEADER.packet)) { - isAvailable = true - sharedPreferences.edit { putBoolean("CrossDeviceIsAvailable", true) } - if (packet.size % 2 == 0) { - val half = packet.size / 2 - if (packet.sliceArray(0 until half).contentEquals(packet.sliceArray(half until packet.size))) { - Log.d("CrossDevice", "Duplicated packet, trimming") - packet = packet.sliceArray(0 until half) - } - } - var trimmedPacket = packet.drop(CrossDevicePackets.AIRPODS_DATA_HEADER.packet.size).toByteArray() - Log.d("CrossDevice", "Received relayed packet: ${trimmedPacket.joinToString("") { "%02x".format(it) }}") - if (ServiceManager.getService()?.isConnectedLocally == true) { - val packetInHex = trimmedPacket.joinToString("") { "%02x".format(it) } -// ServiceManager.getService()?.sendPacket(packetInHex) - } else if (ServiceManager.getService()?.batteryNotification?.isBatteryData(trimmedPacket) == true) { - batteryBytes = trimmedPacket - ServiceManager.getService()?.batteryNotification?.setBattery(trimmedPacket) - Log.d("CrossDevice", "Battery data: ${ServiceManager.getService()?.batteryNotification?.getBattery()[0]?.level}") - ServiceManager.getService()?.updateBattery() - ServiceManager.getService()?.sendBatteryBroadcast() - ServiceManager.getService()?.sendBatteryNotification() - } else if (ServiceManager.getService()?.ancNotification?.isANCData(trimmedPacket) == true) { - ServiceManager.getService()?.ancNotification?.setStatus(trimmedPacket) - ServiceManager.getService()?.sendANCBroadcast() - ServiceManager.getService()?.updateNoiseControlWidget() - ancBytes = trimmedPacket - } else if (ServiceManager.getService()?.earDetectionNotification?.isEarDetectionData(trimmedPacket) == true) { - Log.d("CrossDevice", "Ear detection data: ${trimmedPacket.joinToString("") { "%02x".format(it) }}") - ServiceManager.getService()?.earDetectionNotification?.setStatus(trimmedPacket) - val newEarDetectionStatus = listOf( - ServiceManager.getService()?.earDetectionNotification?.status?.get(0) == 0x00.toByte(), - ServiceManager.getService()?.earDetectionNotification?.status?.get(1) == 0x00.toByte() - ) - if (earDetectionStatus == listOf(false, false) && newEarDetectionStatus.contains(true)) { - ServiceManager.getService()?.applicationContext?.sendBroadcast( - Intent("me.kavishdevar.librepods.cross_device_island") - ) - } - earDetectionStatus = newEarDetectionStatus - } - } - } - } - } - - fun sendRemotePacket(byteArray: ByteArray) { - if (clientSocket == null || clientSocket!!.outputStream == null) { - return - } - clientSocket?.outputStream?.write(byteArray) - clientSocket?.outputStream?.flush() - logPacket(byteArray, "Sent") - Log.d("CrossDevice", "Sent packet to remote device") - } - - fun notifyAirPodsConnectedRemotely(context: Context) { - val intent = Intent("me.kavishdevar.librepods.AIRPODS_CONNECTED_REMOTELY") - context.sendBroadcast(intent) - } - fun notifyAirPodsDisconnectedRemotely(context: Context) { - val intent = Intent("me.kavishdevar.librepods.AIRPODS_DISCONNECTED_REMOTELY") - context.sendBroadcast(intent) - } -} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/utils/IslandWindow.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/IslandWindow.kt index 09279be..c93ca15 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/utils/IslandWindow.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/utils/IslandWindow.kt @@ -240,6 +240,7 @@ class IslandWindow(private val context: Context) { FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT ) + containerView.addView(islandView, containerParams) params = WindowManager.LayoutParams( @@ -379,7 +380,11 @@ class IslandWindow(private val context: Context) { videoView.start() } - windowManager.addView(containerView, params) + try { + windowManager.addView(containerView, params) + } catch (e: Exception) { + e.printStackTrace() + } islandView.post { initialHeight = islandView.height diff --git a/android/app/src/main/java/me/kavishdevar/librepods/utils/PopupWindow.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/PopupWindow.kt index a60e2ef..e93e709 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/utils/PopupWindow.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/utils/PopupWindow.kt @@ -139,7 +139,11 @@ class PopupWindow( vid.start() } - mWindowManager.addView(mView, mParams) + try { + mWindowManager.addView(mView, mParams) + } catch (e: Exception) { + e.printStackTrace() + } val displayMetrics = mView.context.resources.displayMetrics val screenHeight = displayMetrics.heightPixels diff --git a/android/app/src/main/java/me/kavishdevar/librepods/utils/RootlessSupport.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/RootlessSupport.kt new file mode 100644 index 0000000..d45e0bb --- /dev/null +++ b/android/app/src/main/java/me/kavishdevar/librepods/utils/RootlessSupport.kt @@ -0,0 +1,49 @@ +/* + LibrePods - AirPods liberated from Apple’s ecosystem + Copyright (C) 2025 LibrePods contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package me.kavishdevar.librepods.utils + +import android.os.Build +import me.kavishdevar.librepods.BuildConfig + +fun isSupported(): Boolean { + if (BuildConfig.PLAY_BUILD) { + val isPixel = Build.MANUFACTURER.lowercase() == "google" + val isOppoOrOnePlus = Build.MANUFACTURER.lowercase() in listOf("oneplus", "oppo") + + if (isPixel) { + when (Build.VERSION.SDK_INT) { + 36 -> { + return Build.ID == "CP1A.260305.018" || Build.ID == "CP1A.260405.005" + } + + 37 -> { + return true + } + } + } else if (isOppoOrOnePlus) { + return true + } + } + return true +} + + +/*fun isSupported(): Boolean { + return true +}*/ diff --git a/android/app/src/main/java/me/kavishdevar/librepods/utils/TransparencyUtils.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/TransparencyUtils.kt index f085b9b..1bbd46c 100644 --- a/android/app/src/main/java/me/kavishdevar/librepods/utils/TransparencyUtils.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/utils/TransparencyUtils.kt @@ -139,7 +139,7 @@ fun parseTransparencySettingsResponse(data: ByteArray): TransparencySettings { private var debounceJob: Job? = null -fun sendTransparencySettings(attManager: ATTManager, transparencySettings: TransparencySettings) { +fun sendTransparencySettings(writer: (ATTHandles, ByteArray) -> Unit, transparencySettings: TransparencySettings) { debounceJob?.cancel() debounceJob = CoroutineScope(Dispatchers.IO).launch { delay(100) @@ -171,7 +171,7 @@ fun sendTransparencySettings(attManager: ATTManager, transparencySettings: Trans } val data = buffer.array() - attManager.write(ATTHandles.TRANSPARENCY, value = data) + writer(ATTHandles.TRANSPARENCY, data) } catch (e: IOException) { e.printStackTrace() } diff --git a/android/app/src/main/java/me/kavishdevar/librepods/viewmodel/AirPodsViewModel.kt b/android/app/src/main/java/me/kavishdevar/librepods/viewmodel/AirPodsViewModel.kt new file mode 100644 index 0000000..611bf5b --- /dev/null +++ b/android/app/src/main/java/me/kavishdevar/librepods/viewmodel/AirPodsViewModel.kt @@ -0,0 +1,417 @@ +/* + LibrePods - AirPods liberated from Apple’s ecosystem + Copyright (C) 2025 LibrePods contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package me.kavishdevar.librepods.viewmodel + +import android.app.Activity +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.SharedPreferences +import android.util.Log +import androidx.core.content.edit +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import me.kavishdevar.librepods.billing.BillingManager +import me.kavishdevar.librepods.constants.AirPodsNotifications +import me.kavishdevar.librepods.constants.Battery +import me.kavishdevar.librepods.constants.StemAction +import me.kavishdevar.librepods.data.ControlCommandRepository +import me.kavishdevar.librepods.services.AirPodsService +import me.kavishdevar.librepods.utils.AACPManager +import me.kavishdevar.librepods.utils.AACPManager.Companion.ControlCommandIdentifiers +import me.kavishdevar.librepods.utils.ATTHandles +import me.kavishdevar.librepods.utils.AirPodsInstance +import me.kavishdevar.librepods.utils.AirPodsModels +import me.kavishdevar.librepods.utils.Capability + +@Suppress("ArrayInDataClass") +data class AirPodsUiState( + val deviceName: String, + + val isLocallyConnected: Boolean = false, + + val instance: AirPodsInstance? = null, + val capabilities: Set = emptySet(), + + val controlStates: Map = emptyMap(), + val offListeningMode: Boolean = true, + + val battery: List = emptyList(), + val ancMode: Int = 3, + + val modelName: String = "", + val actualModel: String = "", + val serialNumbers: List = emptyList(), + val version1: String = "", + val version2: String = "", + val version3: String = "", + + val headTrackingActive: Boolean = false, + val headGesturesEnabled: Boolean = true, + + val eqData: FloatArray = floatArrayOf(), + + val automaticEarDetectionEnabled: Boolean = true, + val automaticConnectionEnabled: Boolean = true, + + val leftAction: StemAction = StemAction.CYCLE_NOISE_CONTROL_MODES, + val rightAction: StemAction = StemAction.CYCLE_NOISE_CONTROL_MODES, + + val isPremium: Boolean = false, +) + +class AirPodsViewModel( + private val service: AirPodsService, + private val sharedPreferences: SharedPreferences, + private val controlRepo: ControlCommandRepository, + private val appContext: Context +) : ViewModel() { + + private val _uiState = MutableStateFlow(AirPodsUiState(deviceName = sharedPreferences.getString("name", "AirPods Pro") ?: "AirPods Pro")) + val uiState: StateFlow = _uiState + + private val listeners = mutableMapOf< + ControlCommandIdentifiers, + AACPManager.ControlCommandListener + >() + + private lateinit var broadcastReceiver: BroadcastReceiver + + private val _cameraAction = MutableStateFlow( + sharedPreferences.getString("camera_action", null) + ?.let { value -> AACPManager.Companion.StemPressType.entries.find { it.name == value } } + ) + + val cameraAction: StateFlow = _cameraAction + + fun setCameraAction(action: AACPManager.Companion.StemPressType?) { + sharedPreferences.edit { + if (action == null) remove("camera_action") + else putString("camera_action", action.name) + } + _cameraAction.value = action + } + + init { + observeBroadcasts() + loadName() + loadInstance() + loadSharedPreferences() + setupControlObservers() + observeBilling() + } + + override fun onCleared() { + listeners.forEach { (id, listener) -> + controlRepo.remove(id, listener) + } + + appContext.unregisterReceiver(broadcastReceiver) + + super.onCleared() + } + + private fun loadName() { + val name = sharedPreferences.getString("name", "AirPods Pro")!! + _uiState.update { it.copy(deviceName = name) } + } + + private fun observeBilling() { + viewModelScope.launch { + BillingManager.provider.isPremium.collect { premium -> + + if (!premium) { + setControlCommandBoolean(ControlCommandIdentifiers.CONVERSATION_DETECT_CONFIG, false) + setHeadGesturesEnabled(false) + } + + _uiState.update { it.copy(isPremium = premium) } + } + } + } + + private fun observeBroadcasts() { + broadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + when (intent?.action) { + AirPodsNotifications.AIRPODS_CONNECTED -> { + _uiState.update { + it.copy(isLocallyConnected = true) + } + } + + AirPodsNotifications.AIRPODS_DISCONNECTED -> { + _uiState.update { + it.copy(isLocallyConnected = false) + } + } + + AirPodsNotifications.BATTERY_DATA -> { + val data = intent.getParcelableArrayListExtra("data", Battery::class.java)?.toList() ?: emptyList() + _uiState.update { + it.copy(battery = data) + } + } + + AirPodsNotifications.EQ_DATA -> { + val data = intent.getFloatArrayExtra("eqData") ?: floatArrayOf() + + _uiState.update { + it.copy(eqData = data) + } + } + + AirPodsNotifications.AIRPODS_INFORMATION_UPDATED -> { + loadInstance() + } + } + } + } + + val filter = IntentFilter().apply { + addAction(AirPodsNotifications.AIRPODS_CONNECTED) + addAction(AirPodsNotifications.AIRPODS_DISCONNECTED) + addAction(AirPodsNotifications.BATTERY_DATA) + addAction(AirPodsNotifications.EQ_DATA) + addAction(AirPodsNotifications.AIRPODS_INFORMATION_UPDATED) + } + + appContext.registerReceiver( + broadcastReceiver, + filter, + Context.RECEIVER_NOT_EXPORTED + ) + } + + fun setControlCommandValue( + identifier: ControlCommandIdentifiers, + value: ByteArray + ) { + controlRepo.setValue(identifier, value) + _uiState.update { + it.copy( + controlStates = it.controlStates + (identifier to value) + ) + } + } + + fun setControlCommandBoolean( + identifier: ControlCommandIdentifiers, + enabled: Boolean + ) { + setControlCommandValue( + identifier, + if (enabled) byteArrayOf(0x01) else byteArrayOf(0x02) + ) + } + + fun setControlCommandInt( + identifier: ControlCommandIdentifiers, + value: Int + ) { + setControlCommandValue(identifier, byteArrayOf(value.toByte())) + } + + fun setControlCommandByte( + identifier: ControlCommandIdentifiers, + value: Byte + ) { + setControlCommandValue(identifier, byteArrayOf(value)) + } + + fun observeControl(identifier: ControlCommandIdentifiers) { + val listener = controlRepo.observe(identifier) { value -> + _uiState.update { state -> + val current = state.controlStates[identifier] + if (current?.contentEquals(value) == true) return@update state + + state.copy( + controlStates = state.controlStates + (identifier to value) + ) + } + } + + listeners[identifier] = listener + } + + // I'm lazy, sorry. + fun setupControlObservers() { + val identifiersList = listOf( + ControlCommandIdentifiers.MIC_MODE, + ControlCommandIdentifiers.DOUBLE_CLICK_INTERVAL, + ControlCommandIdentifiers.CLICK_HOLD_INTERVAL, + ControlCommandIdentifiers.LISTENING_MODE_CONFIGS, + ControlCommandIdentifiers.ONE_BUD_ANC_MODE, + ControlCommandIdentifiers.LISTENING_MODE, + ControlCommandIdentifiers.AUTO_ANSWER_MODE, + ControlCommandIdentifiers.CHIME_VOLUME, + ControlCommandIdentifiers.VOLUME_SWIPE_INTERVAL, + ControlCommandIdentifiers.CALL_MANAGEMENT_CONFIG, + ControlCommandIdentifiers.VOLUME_SWIPE_MODE, + ControlCommandIdentifiers.ADAPTIVE_VOLUME_CONFIG, + ControlCommandIdentifiers.CONVERSATION_DETECT_CONFIG, + ControlCommandIdentifiers.HEARING_AID, + ControlCommandIdentifiers.AUTO_ANC_STRENGTH, + ControlCommandIdentifiers.HPS_GAIN_SWIPE, + ControlCommandIdentifiers.HEARING_ASSIST_CONFIG, + ControlCommandIdentifiers.ALLOW_OFF_OPTION, + ControlCommandIdentifiers.STEM_CONFIG, + ControlCommandIdentifiers.SLEEP_DETECTION_CONFIG, + ControlCommandIdentifiers.ALLOW_AUTO_CONNECT, + ControlCommandIdentifiers.EAR_DETECTION_CONFIG, + ControlCommandIdentifiers.AUTOMATIC_CONNECTION_CONFIG, + ControlCommandIdentifiers.OWNS_CONNECTION, + ControlCommandIdentifiers.PPE_TOGGLE_CONFIG, + ) + for (identifier in identifiersList) { + observeControl(identifier) + } + } + + fun refreshInitialData() { + service.let { service -> + _uiState.update { + it.copy( + isLocallyConnected = service.isConnected(), + battery = service.getBattery() + ) + } + } + } + + private fun loadSharedPreferences() { + val offListeningModeEnabled = sharedPreferences.getBoolean("off_listening_mode", true) + val automaticEarDetectionEnabled = sharedPreferences.getBoolean("automatic_ear_detection", true) + val automaticConnectionEnabled = sharedPreferences.getBoolean("automatic_connection_ctrl_cmd", true) + val headGesturesEnabled = sharedPreferences.getBoolean("head_gestures", true) + val leftAction = StemAction.valueOf(sharedPreferences.getString("left_long_press_action", "CYCLE_NOISE_CONTROL_MODES") ?: "CYCLE_NOISE_CONTROL_MODES") + val rightAction = StemAction.valueOf(sharedPreferences.getString("right_long_press_action", "CYCLE_NOISE_CONTROL_MODES") ?: "CYCLE_NOISE_CONTROL_MODES") + + _uiState.update { + it.copy( + offListeningMode = offListeningModeEnabled, + automaticEarDetectionEnabled = automaticEarDetectionEnabled, + automaticConnectionEnabled = automaticConnectionEnabled, + headGesturesEnabled = headGesturesEnabled, + leftAction = leftAction, + rightAction = rightAction + ) + } + } + + fun setOffListeningMode(enabled: Boolean) { + sharedPreferences.edit { putBoolean("off_listening_mode", enabled) } + setControlCommandBoolean(ControlCommandIdentifiers.ALLOW_OFF_OPTION, enabled) + Log.d("AirPodsViewModel", "Hello???? $enabled") + _uiState.update { + it.copy(offListeningMode = enabled) + } + } + + fun setHeadGesturesEnabled(enabled: Boolean) { + sharedPreferences.edit { putBoolean("head_gestures", enabled) } + _uiState.update { + it.copy(headGesturesEnabled = enabled) + } + } + + private fun loadInstance() { + val instance = service.airpodsInstance ?: AirPodsInstance( + name = "AirPods", + model = AirPodsModels.getModelByModelNumber("A3049")!!, + actualModelNumber = "A3049", + aacpManager = service.aacpManager, + serialNumber = null, + leftSerialNumber = null, + rightSerialNumber = null, + version1 = null, + version2 = null, + version3 = null, + attManager = null + ) + + _uiState.update { + it.copy( + capabilities = instance.model.capabilities, + instance = instance, + modelName = instance.model.displayName, + actualModel = instance.actualModelNumber, + serialNumbers = listOf(instance.serialNumber ?: "", instance.leftSerialNumber ?: "", instance.rightSerialNumber ?: ""), + version1 = instance.version1 ?: "", + version2 = instance.version2 ?: "", + version3 = instance.version3 ?: "" + ) + } + } + + fun reconnectFromSavedMac() { + service.reconnectFromSavedMac() + } + + fun setName(name: String) { + service.setName(name) + } + + fun startHeadTracking() { + service.startHeadTracking() + _uiState.update { it.copy(headTrackingActive = true) } + } + + fun stopHeadTracking() { + service.stopHeadTracking() + _uiState.update { it.copy(headTrackingActive = false) } + } + + fun setATTCharacteristicValue(handle: ATTHandles, value: ByteArray) { + service.attManager?.write(handle, value) + } + + fun getATTCharacteristicValue(handle: ATTHandles): ByteArray? { + return service.attManager?.read(handle) + } + + fun setAutomaticEarDetectionEnabled(enabled: Boolean) { + sharedPreferences.edit { putBoolean("automatic_ear_detection", enabled) } + setControlCommandBoolean(ControlCommandIdentifiers.EAR_DETECTION_CONFIG, enabled) + _uiState.update { + it.copy( + automaticEarDetectionEnabled = enabled + ) + } + } + + fun setAutomaticConnectionEnabled(enabled: Boolean) { + sharedPreferences.edit { putBoolean("automatic_connection_ctrl_cmd", enabled) } + setControlCommandBoolean(ControlCommandIdentifiers.AUTOMATIC_CONNECTION_CONFIG, enabled) + _uiState.update { + it.copy( + automaticConnectionEnabled = enabled + ) + } + } + + fun purchase(context: Context) { + BillingManager.provider.purchase(context as Activity) + } +} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/viewmodel/AppSettingsViewModel.kt b/android/app/src/main/java/me/kavishdevar/librepods/viewmodel/AppSettingsViewModel.kt new file mode 100644 index 0000000..5508a65 --- /dev/null +++ b/android/app/src/main/java/me/kavishdevar/librepods/viewmodel/AppSettingsViewModel.kt @@ -0,0 +1,158 @@ +package me.kavishdevar.librepods.viewmodel + +import android.app.Activity +import android.app.Application +import android.content.Context +import androidx.core.content.edit +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import me.kavishdevar.librepods.billing.BillingManager +import kotlin.math.roundToInt + +data class AppSettingsUiState( + val showPhoneBatteryInWidget: Boolean = false, + val conversationalAwarenessPauseMusicEnabled: Boolean = false, + val relativeConversationalAwarenessVolumeEnabled: Boolean = true, + val disconnectWhenNotWearing: Boolean = false, + val takeoverWhenDisconnected: Boolean = false, + val takeoverWhenIdle: Boolean = false, + val takeoverWhenMusic: Boolean = false, + val takeoverWhenCall: Boolean = false, + val takeoverWhenRingingCall: Boolean = false, + val takeoverWhenMediaStart: Boolean = false, + val useAlternateHeadTrackingPackets: Boolean = true, + val conversationalAwarenessVolume: Float = 43f, + val showCameraDialog: Boolean = false, + val cameraPackageValue: String = "", + val cameraPackageError: String? = null, + val isPremium: Boolean = false +) + +class AppSettingsViewModel(application: Application) : AndroidViewModel(application) { + private val sharedPreferences = application.getSharedPreferences("settings", Context.MODE_PRIVATE) + + private val _uiState = MutableStateFlow(AppSettingsUiState()) + val uiState = _uiState.asStateFlow() + + init { + loadSettings() + observeBilling() + } + + private fun observeBilling() { + viewModelScope.launch { + BillingManager.provider.isPremium.collect { premium -> + _uiState.update { it.copy(isPremium = premium) } + } + } + } + + private fun loadSettings() { + _uiState.update { currentState -> + currentState.copy( + showPhoneBatteryInWidget = sharedPreferences.getBoolean("show_phone_battery_in_widget", false), + conversationalAwarenessPauseMusicEnabled = sharedPreferences.getBoolean("conversational_awareness_pause_music", false), + relativeConversationalAwarenessVolumeEnabled = sharedPreferences.getBoolean("relative_conversational_awareness_volume", true), + disconnectWhenNotWearing = sharedPreferences.getBoolean("disconnect_when_not_wearing", false), + takeoverWhenDisconnected = sharedPreferences.getBoolean("takeover_when_disconnected", false), + takeoverWhenIdle = sharedPreferences.getBoolean("takeover_when_idle", false), + takeoverWhenMusic = sharedPreferences.getBoolean("takeover_when_music", false), + takeoverWhenCall = sharedPreferences.getBoolean("takeover_when_call", false), + takeoverWhenRingingCall = sharedPreferences.getBoolean("takeover_when_ringing_call", false), + takeoverWhenMediaStart = sharedPreferences.getBoolean("takeover_when_media_start", false), + useAlternateHeadTrackingPackets = sharedPreferences.getBoolean("use_alternate_head_tracking_packets", true), + conversationalAwarenessVolume = sharedPreferences.getInt("conversational_awareness_volume", 43).toFloat(), + cameraPackageValue = sharedPreferences.getString("custom_camera_package", "") ?: "" + ) + } + } + + fun setShowPhoneBatteryInWidget(enabled: Boolean) { + sharedPreferences.edit { putBoolean("show_phone_battery_in_widget", enabled) } + _uiState.update { it.copy(showPhoneBatteryInWidget = enabled) } + } + + fun setConversationalAwarenessPauseMusicEnabled(enabled: Boolean) { + sharedPreferences.edit { putBoolean("conversational_awareness_pause_music", enabled) } + _uiState.update { it.copy(conversationalAwarenessPauseMusicEnabled = enabled) } + } + + fun setRelativeConversationalAwarenessVolumeEnabled(enabled: Boolean) { + sharedPreferences.edit { putBoolean("relative_conversational_awareness_volume", enabled) } + _uiState.update { it.copy(relativeConversationalAwarenessVolumeEnabled = enabled) } + } + + fun setDisconnectWhenNotWearing(enabled: Boolean) { + sharedPreferences.edit { putBoolean("disconnect_when_not_wearing", enabled) } + _uiState.update { it.copy(disconnectWhenNotWearing = enabled) } + } + + fun setTakeoverWhenDisconnected(enabled: Boolean) { + sharedPreferences.edit { putBoolean("takeover_when_disconnected", enabled) } + _uiState.update { it.copy(takeoverWhenDisconnected = enabled) } + } + + fun setTakeoverWhenIdle(enabled: Boolean) { + sharedPreferences.edit { putBoolean("takeover_when_idle", enabled) } + _uiState.update { it.copy(takeoverWhenIdle = enabled) } + } + + fun setTakeoverWhenMusic(enabled: Boolean) { + sharedPreferences.edit { putBoolean("takeover_when_music", enabled) } + _uiState.update { it.copy(takeoverWhenMusic = enabled) } + } + + fun setTakeoverWhenCall(enabled: Boolean) { + sharedPreferences.edit { putBoolean("takeover_when_call", enabled) } + _uiState.update { it.copy(takeoverWhenCall = enabled) } + } + + fun setTakeoverWhenRingingCall(enabled: Boolean) { + sharedPreferences.edit { putBoolean("takeover_when_ringing_call", enabled) } + _uiState.update { it.copy(takeoverWhenRingingCall = enabled) } + } + + fun setTakeoverWhenMediaStart(enabled: Boolean) { + sharedPreferences.edit { putBoolean("takeover_when_media_start", enabled) } + _uiState.update { it.copy(takeoverWhenMediaStart = enabled) } + } + + fun setUseAlternateHeadTrackingPackets(enabled: Boolean) { + sharedPreferences.edit { putBoolean("use_alternate_head_tracking_packets", enabled) } + _uiState.update { it.copy(useAlternateHeadTrackingPackets = enabled) } + } + + fun setConversationalAwarenessVolume(volume: Float) { + sharedPreferences.edit { putInt("conversational_awareness_volume", volume.roundToInt()) } + _uiState.update { it.copy(conversationalAwarenessVolume = volume) } + } + + fun setShowCameraDialog(show: Boolean) { + _uiState.update { it.copy(showCameraDialog = show) } + } + + fun setCameraPackageValue(value: String) { + _uiState.update { it.copy(cameraPackageValue = value) } + } + + fun setCameraPackageError(error: String?) { + _uiState.update { it.copy(cameraPackageError = error) } + } + + fun saveCameraPackage() { + if (_uiState.value.cameraPackageValue.isBlank()) { + sharedPreferences.edit { remove("custom_camera_package") } + } else { + sharedPreferences.edit { putString("custom_camera_package", _uiState.value.cameraPackageValue) } + } + setShowCameraDialog(false) + } + + fun purchase(context: Context) { + BillingManager.provider.purchase(context as Activity) + } +} diff --git a/android/app/src/main/res/value-it/strings.xml b/android/app/src/main/res/value-it/strings.xml index cf02b8e..5933b08 100644 --- a/android/app/src/main/res/value-it/strings.xml +++ b/android/app/src/main/res/value-it/strings.xml @@ -1,217 +1,213 @@ - - LibrePods - Libera i tuoi AirPods dall'ecosistema Apple. - Visualizza lo stato della batteria dei tuoi AirPods direttamente dalla schermata principale! - Accessibilità - Volume Tono - Regola il volume del tono degli effetti sonori riprodotti dagli AirPods. - Audio - Audio Adattivo - Personalizza Audio Adattivo - L'audio adattivo risponde dinamicamente al tuo ambiente e cancella o permette i rumori esterni. Puoi personalizzare l'Audio Adattivo per permettere più o meno rumore. - Auricolari - Custodia - Test - Nome - Modalità di Ascolto - Spento - Trasparenza - Adattivo - Cancellazione del Rumore - Premi e Tieni Premuto sugli AirPods - Premi e tieni premuto sullo stelo per alternare tra le modalità di ascolto selezionate. - Gesti della Testa - Sinistra - Destra - Consapevolezza Conversazionale - Abbassa il volume dei contenuti multimediali e riduce il rumore di fondo quando inizi a parlare con altre persone. - Volume Personalizzato - Regola il volume dei contenuti multimediali in risposta al tuo ambiente. - Cancellazione del Rumore con un Solo AirPod - Consenti agli AirPods di essere messi in modalità di cancellazione del rumore quando è presente un solo AirPod nell'orecchio. - Controllo Volume - Regola il volume scorrendo verso l'alto o verso il basso sul sensore situato sullo stelo degli AirPods Pro. - AirPods non connessi - Si prega di connettere i tuoi AirPods per accedere alle impostazioni. - Indietro - Personalizzazioni - Volume relativo - Riduce a una percentuale del volume corrente invece del volume massimo. - Metti in Pausa la Musica - Quando inizi a parlare, la musica verrà messa in pausa. - ESEMPIO - Aggiungi widget - Controlla la Modalità di Controllo del Rumore direttamente dalla tua Schermata Principale. - Connesso - Connesso a Linux - Connesso - Spostato su Linux - Spostato su %1$s - Riconnetti dalla notifica - Tracciamento della Testa - Annuisci per rispondere alle chiamate e scuoti la testa per rifiutarle. - Generale - Azione del Tile Impostazioni Rapide - Mostra la finestra di dialogo per il controllo del rumore al tocco. - Alterna tra le modalità al tocco. - Sviluppatore - Apri le Impostazioni degli AirPods - Gestisci le funzionalità e le preferenze degli AirPods - Rilevamento Automatico dell'Orecchio - Riproduzione Automatica - Pausa Automatica - Risoluzione dei Problemi - Raccogli i log per diagnosticare i problemi con la connessione degli AirPods - Raccogli Log - Log Salvati - Nessun log salvato trovato - Preferenze di Connessione Automatica - Connetti ai tuoi AirPods quando il loro stato è: - Disconnesso - Gli AirPods non sono connessi a un dispositivo - Inattivo - Un dispositivo è connesso ai tuoi AirPods, ma non riproduce contenuti multimediali né è in chiamata - Riproduzione di contenuti multimediali - Un dispositivo sta riproducendo contenuti multimediali sui tuoi AirPods - In chiamata - Un dispositivo è in chiamata con i tuoi AirPods - Connetti agli AirPods quando il tuo telefono è: - Ricezione di una chiamata - Il tuo telefono inizia a squillare - Avvio della riproduzione di contenuti multimediali - Il tuo telefono inizia a riprodurre contenuti multimediali - Annulla - Puoi personalizzare la modalità Trasparenza per i tuoi AirPods Pro per aiutarti a sentire ciò che ti circonda. - La Riduzione dei Suoni Forti può ridurre attivamente la tua esposizione ai forti rumori ambientali quando in modalità Trasparenza e Adattiva. La Riduzione dei Suoni Forti non è attiva in modalità Spento. - Riduzione dei Suoni Forti - Controlli Chiamata - Connetti automaticamente a questo dispositivo - Quando abilitato, gli AirPods tenteranno di connettersi automaticamente a questo dispositivo. Altrimenti, tenteranno di connettersi automaticamente solo se sono stati connessi in precedenza. - Metti in pausa i contenuti multimediali quando ti addormenti - Modalità Ascolto Disattivata - Quando questa opzione è attiva, le modalità di ascolto degli AirPods includeranno un'opzione "Spento". I livelli di suono forti non vengono ridotti quando la modalità di ascolto è impostata su "Spento". - Microfono - Modalità Microfono - Automatico - Sempre Destro - Sempre Sinistro - Rispondi alla chiamata - Silenzia/Riattiva - Riaggancia - Premi una Volta - Premi Due Volte - Apparecchio Acustico - Regolazioni - Scorri per controllare l'amplificazione - Quando sei in modalità Trasparenza e nessun contenuto multimediale è in riproduzione, scorri verso l'alto e verso il basso sui controlli Touch dei tuoi AirPods Pro per aumentare o diminuire l'amplificazione dei suoni ambientali. - Modalità Trasparenza - Personalizza la Modalità Trasparenza - Velocità di Pressione - Regola la velocità richiesta per premere due o tre volte sui tuoi AirPods. - Durata della Pressione Prolungata - Regola la durata richiesta per premere e tenere premuto sui tuoi AirPods. - Velocità di Scorrimento del Volume - Per evitare regolazioni involontarie del volume, seleziona il tempo di attesa preferito tra gli scorrimenti. - Equalizzatore - Applica EQ a - Telefono - Media - Banda %d - Predefinito - Più lento - Il più lento - Più lungo - Il più lungo - Più scuro - Più luminoso - Meno - Di più - Amplificazione - Bilanciamento - Tono - Riduzione del Rumore Ambientale - Potenziamento Conversazione - Potenziamento Conversazione concentra i tuoi AirPods Pro sulla persona che parla di fronte a te, rendendo più facile sentire in una conversazione faccia a faccia. - Gli AirPods possono utilizzare i risultati di un test dell'udito per apportare modifiche che migliorano la chiarezza delle voci e dei suoni intorno a te.\n\nApparecchio Acustico è destinato solo a persone con perdita dell'udito da lieve a moderata. - Assistenza Media - Gli AirPods Pro possono utilizzare i risultati di un test dell'udito per apportare modifiche che migliorano la chiarezza di musica, video e chiamate. - Regola Musica e Video - Regola Chiamate - Widget - Mostra la batteria del telefono nel widget - Visualizza il livello della batteria del tuo telefono nel widget accanto alla batteria degli AirPods - Volume Consapevolezza Conversazionale - Tile Impostazioni Rapide - Apri finestra di dialogo per il controllo - Se disabilitato, cliccando sul QS si scorrerà tra le modalità. Se abilitato, verrà mostrata una finestra di dialogo per controllare la modalità di controllo del rumore e la consapevolezza conversazionale. - Disconnetti AirPods quando non indossati - Sarai ancora in grado di controllarli con l'app - questo disconnette solo l'audio. - Opzioni Avanzate - Imposta Chiave di Risoluzione Identità (IRK) - Imposta manualmente il valore IRK utilizzato per risolvere gli indirizzi casuali BLE - Imposta Chiave di Crittografia - Imposta manualmente il valore ENC_KEY utilizzato per decrittografare le pubblicità BLE - Utilizza pacchetti alternativi di tracciamento della testa - Abilita questo se il tracciamento della testa non funziona per te. Questo invia dati diversi agli AirPods per richiedere/interrompere i dati di tracciamento della testa. - Comportati come un dispositivo Apple - Abilita la connettività multi-dispositivo e le funzionalità di Accessibilità come la personalizzazione della modalità Trasparenza (amplificazione, tono, riduzione del rumore ambientale, potenziamento conversazione ed EQ) - Potrebbe essere instabile!! Un massimo di due dispositivi possono essere connessi ai tuoi AirPods. Se li stai usando con un dispositivo Apple come un iPad o un Mac, connetti prima quel dispositivo e poi il tuo Android. - Reimposta Offset Hook - Questo cancellerà l'offset hook corrente e richiederà di rifare la procedura di configurazione. Sei sicuro di voler continuare? - Reimposta - Offset hook è stato resettato. Reindirizzamento alla configurazione... - Impossibile reimpostare l'offset hook - IRK impostata correttamente - Chiave di crittografia impostata correttamente - Valore Esadecimale IRK - Valore Esadecimale ENC_KEY - Inserisci IRK di 16 byte come stringa esadecimale (32 caratteri): - Inserisci ENC_KEY di 16 byte come stringa esadecimale (32 caratteri): - Devono essere esattamente 32 caratteri esadecimali - Errore durante la conversione esadecimale: - Offset trovato, riavviare il processo Bluetooth - Assistente Digitale - Attivo - Telecomando Fotocamera - Controllo Fotocamera - Scatta una foto, avvia o interrompi la registrazione e altro utilizzando Premere una Volta o Premere e Tenere Premuto. Quando si utilizzano gli AirPods per le azioni della fotocamera, se si seleziona Premere una Volta, i gesti di controllo dei media non saranno disponibili e, se si seleziona Premere e Tenere Premuto, la modalità di ascolto e i gesti dell'Assistente Digitale non saranno disponibili. - Imposta un pacchetto app personalizzato per il rilevamento della fotocamera - Imposta Appid Fotocamera Personalizzata - Inserisci l'id dell'applicazione della fotocamera: - Appid Fotocamera Personalizzata - Appid fotocamera personalizzata impostata correttamente - Ascoltatore fotocamera - Servizio di ascolto per LibrePods per rilevare quando la fotocamera è attiva per attivare il controllo della fotocamera sugli AirPods. - Licenze Open Source - Aggiorna Test Uditivo - Aggiorna Risultato Test Uditivo - ATT Manager è nullo, prova a riconnetterti. - Sono richieste le seguenti autorizzazioni per utilizzare l'app. Si prega di concederle per continuare. - Scuoti la testa o annuisci! - Accesso Root Richiesto - Questa app ha bisogno dell'accesso root per agganciarsi alla libreria Bluetooth - L'accesso root è stato negato. Si prega di concedere i permessi di root. - Passaggi per la Risoluzione dei Problemi - Si prega di inserire i valori di perdita in dbHL - Informazioni - Nome Modello - Numero Modello - Numero di Serie - Versione - Salute Uditiva - Protezione dell'Udito - Uso in Ambienti di Lavoro - Protezione EN 352 - La protezione EN 352 limita il livello massimo dei media a 82 dBA e soddisfa i requisiti applicabili dello standard EN 352 per la protezione individuale dell'udito. - Rumore Ambientale - Riconnetti all'ultimo dispositivo connesso - Disconnetti - Supportami - Non mostrare più - Di recente ho perso il mio AirPod sinistro. Se hai trovato utile LibrePods, considera di supportarmi su GitHub Sponsors in modo che possa acquistare un sostituto e continuare a lavorare su questo progetto: anche una piccola somma fa molto. Grazie per il tuo supporto! - Supporta LibrePods - Disattiva la gestione del rumore - Lascia entrare i suoni esterni - Regola dinamicamente il rumore esterno - Blocca i suoni esterni + + LibrePods + Libera i tuoi AirPods dall'ecosistema Apple. + Visualizza lo stato della batteria dei tuoi AirPods direttamente dalla schermata principale! + Accessibilità + Volume Tono + Regola il volume del tono degli effetti sonori riprodotti dagli AirPods. + Audio + Audio Adattivo + Personalizza Audio Adattivo + L'audio adattivo risponde dinamicamente al tuo ambiente e cancella o permette i rumori esterni. Puoi personalizzare l'Audio Adattivo per permettere più o meno rumore. + Auricolari + Custodia + Test + Nome + Modalità di Ascolto + Spento + Trasparenza + Adattivo + Cancellazione del Rumore + Premi e Tieni Premuto sugli AirPods + Premi e tieni premuto sullo stelo per alternare tra le modalità di ascolto selezionate. + Gesti della Testa + Sinistra + Destra + Consapevolezza Conversazionale + Abbassa il volume dei contenuti multimediali e riduce il rumore di fondo quando inizi a parlare con altre persone. + Volume Personalizzato + Regola il volume dei contenuti multimediali in risposta al tuo ambiente. + Cancellazione del Rumore con un Solo AirPod + Consenti agli AirPods di essere messi in modalità di cancellazione del rumore quando è presente un solo AirPod nell'orecchio. + Controllo Volume + Regola il volume scorrendo verso l'alto o verso il basso sul sensore situato sullo stelo degli AirPods Pro. + AirPods non connessi + Si prega di connettere i tuoi AirPods per accedere alle impostazioni. + Indietro + Personalizzazioni + Volume relativo + Riduce a una percentuale del volume corrente invece del volume massimo. + Metti in Pausa la Musica + Quando inizi a parlare, la musica verrà messa in pausa. + ESEMPIO + Aggiungi widget + Controlla la Modalità di Controllo del Rumore direttamente dalla tua Schermata Principale. + Connesso + Connesso a Linux + Connesso + Spostato su Linux + Spostato su %1$s + Riconnetti dalla notifica + Tracciamento della Testa + Annuisci per rispondere alle chiamate e scuoti la testa per rifiutarle. + Generale + Azione del Tile Impostazioni Rapide + Mostra la finestra di dialogo per il controllo del rumore al tocco. + Alterna tra le modalità al tocco. + Sviluppatore + Apri le Impostazioni degli AirPods + Gestisci le funzionalità e le preferenze degli AirPods + Rilevamento Automatico dell'Orecchio + Riproduzione Automatica + Pausa Automatica + Risoluzione dei Problemi + Raccogli i log per diagnosticare i problemi con la connessione degli AirPods + Raccogli Log + Log Salvati + Nessun log salvato trovato + Preferenze di Connessione Automatica + Connetti ai tuoi AirPods quando il loro stato è: + Disconnesso + Gli AirPods non sono connessi a un dispositivo + Inattivo + Un dispositivo è connesso ai tuoi AirPods, ma non riproduce contenuti multimediali né è in chiamata + Riproduzione di contenuti multimediali + Un dispositivo sta riproducendo contenuti multimediali sui tuoi AirPods + In chiamata + Un dispositivo è in chiamata con i tuoi AirPods + Connetti agli AirPods quando il tuo telefono è: + Ricezione di una chiamata + Il tuo telefono inizia a squillare + Avvio della riproduzione di contenuti multimediali + Il tuo telefono inizia a riprodurre contenuti multimediali + Annulla + Puoi personalizzare la modalità Trasparenza per i tuoi AirPods Pro per aiutarti a sentire ciò che ti circonda. + La Riduzione dei Suoni Forti può ridurre attivamente la tua esposizione ai forti rumori ambientali quando in modalità Trasparenza e Adattiva. La Riduzione dei Suoni Forti non è attiva in modalità Spento. + Riduzione dei Suoni Forti + Controlli Chiamata + Connetti automaticamente a questo dispositivo + Quando abilitato, gli AirPods tenteranno di connettersi automaticamente a questo dispositivo. Altrimenti, tenteranno di connettersi automaticamente solo se sono stati connessi in precedenza. + Metti in pausa i contenuti multimediali quando ti addormenti + Modalità Ascolto Disattivata + Quando questa opzione è attiva, le modalità di ascolto degli AirPods includeranno un'opzione "Spento". I livelli di suono forti non vengono ridotti quando la modalità di ascolto è impostata su "Spento". + Microfono + Modalità Microfono + Automatico + Sempre Destro + Sempre Sinistro + Rispondi alla chiamata + Silenzia/Riattiva + Riaggancia + Premi una Volta + Premi Due Volte + Apparecchio Acustico + Regolazioni + Scorri per controllare l'amplificazione + Quando sei in modalità Trasparenza e nessun contenuto multimediale è in riproduzione, scorri verso l'alto e verso il basso sui controlli Touch dei tuoi AirPods Pro per aumentare o diminuire l'amplificazione dei suoni ambientali. + Modalità Trasparenza + Personalizza la Modalità Trasparenza + Velocità di Pressione + Regola la velocità richiesta per premere due o tre volte sui tuoi AirPods. + Durata della Pressione Prolungata + Regola la durata richiesta per premere e tenere premuto sui tuoi AirPods. + Velocità di Scorrimento del Volume + Per evitare regolazioni involontarie del volume, seleziona il tempo di attesa preferito tra gli scorrimenti. + Equalizzatore + Applica EQ a + Telefono + Media + Banda %d + Predefinito + Più lento + Il più lento + Più lungo + Il più lungo + Più scuro + Più luminoso + Meno + Di più + Amplificazione + Bilanciamento + Tono + Riduzione del Rumore Ambientale + Potenziamento Conversazione + Potenziamento Conversazione concentra i tuoi AirPods Pro sulla persona che parla di fronte a te, rendendo più facile sentire in una conversazione faccia a faccia. + Gli AirPods possono utilizzare i risultati di un test dell'udito per apportare modifiche che migliorano la chiarezza delle voci e dei suoni intorno a te.\n\nApparecchio Acustico è destinato solo a persone con perdita dell'udito da lieve a moderata. + Assistenza Media + Gli AirPods Pro possono utilizzare i risultati di un test dell'udito per apportare modifiche che migliorano la chiarezza di musica, video e chiamate. + Regola Musica e Video + Regola Chiamate + Widget + Mostra la batteria del telefono nel widget + Visualizza il livello della batteria del tuo telefono nel widget accanto alla batteria degli AirPods + Volume Consapevolezza Conversazionale + Tile Impostazioni Rapide + Apri finestra di dialogo per il controllo + Se disabilitato, cliccando sul QS si scorrerà tra le modalità. Se abilitato, verrà mostrata una finestra di dialogo per controllare la modalità di controllo del rumore e la consapevolezza conversazionale. + Disconnetti AirPods quando non indossati + Sarai ancora in grado di controllarli con l'app - questo disconnette solo l'audio. + Opzioni Avanzate + Imposta Chiave di Risoluzione Identità (IRK) + Imposta manualmente il valore IRK utilizzato per risolvere gli indirizzi casuali BLE + Imposta Chiave di Crittografia + Imposta manualmente il valore ENC_KEY utilizzato per decrittografare le pubblicità BLE + Utilizza pacchetti alternativi di tracciamento della testa + Abilita questo se il tracciamento della testa non funziona per te. Questo invia dati diversi agli AirPods per richiedere/interrompere i dati di tracciamento della testa. + Comportati come un dispositivo Apple + Abilita la connettività multi-dispositivo e le funzionalità di Accessibilità come la personalizzazione della modalità Trasparenza (amplificazione, tono, riduzione del rumore ambientale, potenziamento conversazione ed EQ) + Potrebbe essere instabile!! Un massimo di due dispositivi possono essere connessi ai tuoi AirPods. Se li stai usando con un dispositivo Apple come un iPad o un Mac, connetti prima quel dispositivo e poi il tuo Android. + Reimposta Offset Hook + Questo cancellerà l'offset hook corrente e richiederà di rifare la procedura di configurazione. Sei sicuro di voler continuare? + Reimposta + Offset hook è stato resettato. Reindirizzamento alla configurazione... + Impossibile reimpostare l'offset hook + IRK impostata correttamente + Chiave di crittografia impostata correttamente + Valore Esadecimale IRK + Valore Esadecimale ENC_KEY + Inserisci IRK di 16 byte come stringa esadecimale (32 caratteri): + Inserisci ENC_KEY di 16 byte come stringa esadecimale (32 caratteri): + Devono essere esattamente 32 caratteri esadecimali + Errore durante la conversione esadecimale: + Offset trovato, riavviare il processo Bluetooth + Assistente Digitale + Attivo + Telecomando Fotocamera + Controllo Fotocamera + Scatta una foto, avvia o interrompi la registrazione e altro utilizzando Premere una Volta o Premere e Tenere Premuto. Quando si utilizzano gli AirPods per le azioni della fotocamera, se si seleziona Premere una Volta, i gesti di controllo dei media non saranno disponibili e, se si seleziona Premere e Tenere Premuto, la modalità di ascolto e i gesti dell'Assistente Digitale non saranno disponibili. + Imposta un pacchetto app personalizzato per il rilevamento della fotocamera + Imposta Appid Fotocamera Personalizzata + Inserisci l'id dell'applicazione della fotocamera: + Appid Fotocamera Personalizzata + Appid fotocamera personalizzata impostata correttamente + Ascoltatore fotocamera + Servizio di ascolto per LibrePods per rilevare quando la fotocamera è attiva per attivare il controllo della fotocamera sugli AirPods. + Licenze Open Source + Aggiorna Test Uditivo + Aggiorna Risultato Test Uditivo + ATT Manager è nullo, prova a riconnetterti. + Sono richieste le seguenti autorizzazioni per utilizzare l'app. Si prega di concederle per continuare. + Scuoti la testa o annuisci! + Accesso Root Richiesto + Questa app ha bisogno dell'accesso root per agganciarsi alla libreria Bluetooth + L'accesso root è stato negato. Si prega di concedere i permessi di root. + Passaggi per la Risoluzione dei Problemi + Si prega di inserire i valori di perdita in dbHL + Informazioni + Nome Modello + Numero Modello + Numero di Serie + Versione + Salute Uditiva + Protezione dell'Udito + Uso in Ambienti di Lavoro + Protezione EN 352 + La protezione EN 352 limita il livello massimo dei media a 82 dBA e soddisfa i requisiti applicabili dello standard EN 352 per la protezione individuale dell'udito. + Rumore Ambientale + Riconnetti all'ultimo dispositivo connesso + Disconnetti + Disattiva la gestione del rumore + Lascia entrare i suoni esterni + Regola dinamicamente il rumore esterno + Blocca i suoni esterni diff --git a/android/app/src/main/res/values-es/strings.xml b/android/app/src/main/res/values-es/strings.xml index 9d621d4..27e3510 100644 --- a/android/app/src/main/res/values-es/strings.xml +++ b/android/app/src/main/res/values-es/strings.xml @@ -206,10 +206,6 @@ Ruido ambiental Reconectar al último dispositivo conectado Desconectar - Apóyame - No volver a mostrar - Hace poco perdí mi AirPod izquierdo. Si LibrePods te ha resultado útil, considera apoyarme en GitHub Sponsors para que pueda comprar un reemplazo y seguir trabajando en este proyecto; incluso una pequeña donación es de gran ayuda. ¡Gracias por tu apoyo! - Apoya a LibrePods Desactiva la gestión del ruido Deja entrar los sonidos externos Ajuste dinámico del ruido externo diff --git a/android/app/src/main/res/values-fr/strings.xml b/android/app/src/main/res/values-fr/strings.xml index ad62e67..c87a7d4 100644 --- a/android/app/src/main/res/values-fr/strings.xml +++ b/android/app/src/main/res/values-fr/strings.xml @@ -206,10 +206,6 @@ Bruit environnemental Reconnecter au dernier appareil Déconnecter - Soutenez-moi - Ne plus afficher - J\'ai récemment perdu mon AirPod gauche. Si LibrePods vous est utile, pensez à me soutenir sur GitHub Sponsors pour m\'aider à en racheter un et continuer ce projet — même un petit montant aide beaucoup. Merci pour votre soutien ! - Soutenir LibrePods Désactiver la gestion du bruit Laisser entrer les sons extérieurs Ajuster dynamiquement les sons extérieurs diff --git a/android/app/src/main/res/values-pt/strings.xml b/android/app/src/main/res/values-pt/strings.xml index 41ed556..f214026 100644 --- a/android/app/src/main/res/values-pt/strings.xml +++ b/android/app/src/main/res/values-pt/strings.xml @@ -206,10 +206,6 @@ Ruído Ambiental Reconectar ao último dispositivo conectado Desconectar - Me Apoiar - Nunca mostrar novamente - Recentemente perdi meu AirPod esquerdo. Se você achou o LibrePods útil, considere me apoiar no GitHub Sponsors para que eu possa comprar uma substituição e continuar trabalhando neste projeto - mesmo uma pequena quantia faz muita diferença. Obrigado pelo seu apoio! - Apoiar LibrePods Desativa o gerenciamento de ruído Permite sons externos Ajusta dinamicamente o ruído externo diff --git a/android/app/src/main/res/values-tr/strings.xml b/android/app/src/main/res/values-tr/strings.xml index f87544c..864920d 100644 --- a/android/app/src/main/res/values-tr/strings.xml +++ b/android/app/src/main/res/values-tr/strings.xml @@ -206,10 +206,6 @@ Çevresel Gürültü Son bağlanan cihaza yeniden bağlan Bağlantıyı Kes - Beni destekle - Bir daha gösterme - Yakın zamanda sol AirPod\'umu kaybettim. LibrePods\'u faydalı bulduysanız, bir yedek satın alıp bu proje üzerinde çalışmaya devam edebilmem için GitHub Sponsors\'ta beni desteklemeyi düşünün - küçük bir miktar bile çok işe yarar. Desteğiniz için teşekkürler! - LibrePods\'u Destekle Gürültü yönetimini kapatır Dış sesleri içeri alır Dış gürültüyü dinamik olarak ayarlar diff --git a/android/app/src/main/res/values-uk/strings.xml b/android/app/src/main/res/values-uk/strings.xml index c3ae2a0..267d689 100644 --- a/android/app/src/main/res/values-uk/strings.xml +++ b/android/app/src/main/res/values-uk/strings.xml @@ -206,10 +206,6 @@ Навколишній Шум Перепідключитися до останнього підключеного пристрою Відʼєднатися - Підтримати мене - Ніколи не показувати знову - Нещодавно я втратив свій лівий AirPod. Якщо LibrePods виявилися корисними для вас, розгляньте можливість підтримати мене на GitHub Sponsors, щоб я міг купити заміну та продовжити роботу над цим проектом — навіть невелика допомога має велике значення. Дякую за вашу підтримку! - Підтримати LibrePods Вимикає керування шумом Пропускає зовнішні звуки Динамічно налаштовує зовнішній шум diff --git a/android/app/src/main/res/values-vi/strings.xml b/android/app/src/main/res/values-vi/strings.xml index 044df73..25436c5 100644 --- a/android/app/src/main/res/values-vi/strings.xml +++ b/android/app/src/main/res/values-vi/strings.xml @@ -206,10 +206,6 @@ Tiếng ồn môi trường Kết nối lại với thiết bị được kết nối lần cuối Ngắt kết nối - Hỗ trợ tôi - Không hiển thị lại - Gần đây tôi bị mất tai bên trái của AirPod. Nếu bạn thấy LibrePods hữu ích, hãy cân nhắc hỗ trợ tôi trên GitHub Sponsors để tôi có thể mua cái thay thế và tiếp tục làm việc trên dự án này - ngay cả một khoản nhỏ cũng rất có ý nghĩa. Cảm ơn sự hỗ trợ của bạn! - Hỗ trợ LibrePods Tắt quản lý tiếng ồn Cho phép âm thanh bên ngoài Điều chỉnh động tiếng ồn bên ngoài diff --git a/android/app/src/main/res/values-zh-rCN/strings.xml b/android/app/src/main/res/values-zh-rCN/strings.xml index 3178aba..af388da 100644 --- a/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/android/app/src/main/res/values-zh-rCN/strings.xml @@ -207,12 +207,8 @@ 环境噪音 重新连接到上次连接的设备 断开连接 - 支持我 - 不再显示 - 我最近丢了我的左耳 AirPod。如果你觉得 LibrePods 有用,请考虑在 GitHub Sponsors 上支持我,这样我就可以购买一个替换品并继续从事这个项目——即使是少量捐助也能发挥很大作用。感谢你的支持! - 支持 LibrePods 关闭噪音管理 允许外部声音进入 动态调整外部噪音 阻隔外部声音 - \ No newline at end of file + diff --git a/android/app/src/main/res/values-zh-rTW/strings.xml b/android/app/src/main/res/values-zh-rTW/strings.xml index dc45f8d..3a77d69 100644 --- a/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/android/app/src/main/res/values-zh-rTW/strings.xml @@ -208,12 +208,8 @@ 環境噪音 重新連接至上次連接的裝置 中斷連線 - 贊助我 - 不再顯示 - 我最近弄丟了左耳的 AirPod。如果你覺得 LibrePods 很好用,請考慮在 GitHub Sponsors 上贊助我,讓我能買個替換品並繼續開發這個專案,一點點金額也能帶來很大的幫助。感謝你的支持! - 贊助 LibrePods 關閉噪音管理 允許外部聲音 動態調整外部噪音 阻隔外部聲音 - \ No newline at end of file + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 74ae083..beedccf 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -206,12 +206,9 @@ Environmental Noise Reconnect to last connected device Disconnect - Support me - Never show again - I recently lost my left AirPod. If you\'ve found LibrePods useful, consider supporting me on GitHub Sponsors so I can buy a replacement and continue working on this project- even a little amount goes a long way. Thank you for your support! - Support LibrePods Turns off noise management Lets in external sounds Dynamically adjust external noise Blocks out external sounds + Unlock all features diff --git a/android/app/src/main/cpp/l2c_fcr_hook.cpp b/android/app/src/xposed/cpp/l2c_fcr_hook.cpp similarity index 100% rename from android/app/src/main/cpp/l2c_fcr_hook.cpp rename to android/app/src/xposed/cpp/l2c_fcr_hook.cpp diff --git a/android/app/src/main/cpp/l2c_fcr_hook.h b/android/app/src/xposed/cpp/l2c_fcr_hook.h similarity index 100% rename from android/app/src/main/cpp/l2c_fcr_hook.h rename to android/app/src/xposed/cpp/l2c_fcr_hook.h diff --git a/android/app/src/main/cpp/xz/xz.h b/android/app/src/xposed/cpp/xz/xz.h similarity index 100% rename from android/app/src/main/cpp/xz/xz.h rename to android/app/src/xposed/cpp/xz/xz.h diff --git a/android/app/src/main/cpp/xz/xz_config.h b/android/app/src/xposed/cpp/xz/xz_config.h similarity index 100% rename from android/app/src/main/cpp/xz/xz_config.h rename to android/app/src/xposed/cpp/xz/xz_config.h diff --git a/android/app/src/main/cpp/xz/xz_crc32.c b/android/app/src/xposed/cpp/xz/xz_crc32.c similarity index 100% rename from android/app/src/main/cpp/xz/xz_crc32.c rename to android/app/src/xposed/cpp/xz/xz_crc32.c diff --git a/android/app/src/main/cpp/xz/xz_crc64.c b/android/app/src/xposed/cpp/xz/xz_crc64.c similarity index 100% rename from android/app/src/main/cpp/xz/xz_crc64.c rename to android/app/src/xposed/cpp/xz/xz_crc64.c diff --git a/android/app/src/main/cpp/xz/xz_dec_bcj.c b/android/app/src/xposed/cpp/xz/xz_dec_bcj.c similarity index 100% rename from android/app/src/main/cpp/xz/xz_dec_bcj.c rename to android/app/src/xposed/cpp/xz/xz_dec_bcj.c diff --git a/android/app/src/main/cpp/xz/xz_dec_lzma2.c b/android/app/src/xposed/cpp/xz/xz_dec_lzma2.c similarity index 100% rename from android/app/src/main/cpp/xz/xz_dec_lzma2.c rename to android/app/src/xposed/cpp/xz/xz_dec_lzma2.c diff --git a/android/app/src/main/cpp/xz/xz_dec_stream.c b/android/app/src/xposed/cpp/xz/xz_dec_stream.c similarity index 100% rename from android/app/src/main/cpp/xz/xz_dec_stream.c rename to android/app/src/xposed/cpp/xz/xz_dec_stream.c diff --git a/android/app/src/main/cpp/xz/xz_lzma2.h b/android/app/src/xposed/cpp/xz/xz_lzma2.h similarity index 100% rename from android/app/src/main/cpp/xz/xz_lzma2.h rename to android/app/src/xposed/cpp/xz/xz_lzma2.h diff --git a/android/app/src/main/cpp/xz/xz_private.h b/android/app/src/xposed/cpp/xz/xz_private.h similarity index 100% rename from android/app/src/main/cpp/xz/xz_private.h rename to android/app/src/xposed/cpp/xz/xz_private.h diff --git a/android/app/src/main/cpp/xz/xz_sha256.c b/android/app/src/xposed/cpp/xz/xz_sha256.c similarity index 100% rename from android/app/src/main/cpp/xz/xz_sha256.c rename to android/app/src/xposed/cpp/xz/xz_sha256.c diff --git a/android/app/src/main/cpp/xz/xz_stream.h b/android/app/src/xposed/cpp/xz/xz_stream.h similarity index 100% rename from android/app/src/main/cpp/xz/xz_stream.h rename to android/app/src/xposed/cpp/xz/xz_stream.h diff --git a/android/app/src/main/java/me/kavishdevar/librepods/utils/KotlinModule.kt b/android/app/src/xposed/java/me/kavishdevar/librepods/utils/KotlinModule.kt similarity index 100% rename from android/app/src/main/java/me/kavishdevar/librepods/utils/KotlinModule.kt rename to android/app/src/xposed/java/me/kavishdevar/librepods/utils/KotlinModule.kt diff --git a/android/app/src/main/resources/META-INF/xposed/java_init.list b/android/app/src/xposed/resources/META-INF/xposed/java_init.list similarity index 100% rename from android/app/src/main/resources/META-INF/xposed/java_init.list rename to android/app/src/xposed/resources/META-INF/xposed/java_init.list diff --git a/android/app/src/main/resources/META-INF/xposed/module.prop b/android/app/src/xposed/resources/META-INF/xposed/module.prop similarity index 100% rename from android/app/src/main/resources/META-INF/xposed/module.prop rename to android/app/src/xposed/resources/META-INF/xposed/module.prop diff --git a/android/app/src/main/resources/META-INF/xposed/native_init.list b/android/app/src/xposed/resources/META-INF/xposed/native_init.list similarity index 100% rename from android/app/src/main/resources/META-INF/xposed/native_init.list rename to android/app/src/xposed/resources/META-INF/xposed/native_init.list diff --git a/android/app/src/main/resources/META-INF/xposed/scope.list b/android/app/src/xposed/resources/META-INF/xposed/scope.list similarity index 100% rename from android/app/src/main/resources/META-INF/xposed/scope.list rename to android/app/src/xposed/resources/META-INF/xposed/scope.list diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 31555c0..45682a0 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { alias(libs.plugins.android.application) apply false - alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.kotlin.compose) apply false alias(libs.plugins.aboutLibraries) apply false -} \ No newline at end of file +// alias(libs.plugins.hilt) apply false +} diff --git a/android/gradle.properties b/android/gradle.properties index 2c138d5..8d81701 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -6,7 +6,7 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx8192m -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. For more details, visit # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects @@ -22,4 +22,17 @@ kotlin.code.style=official # thereby reducing the size of the R class for that library android.nonTransitiveRClass=true -android.javaCompile.suppressSourceTargetDeprecationWarning=true \ No newline at end of file +android.javaCompile.suppressSourceTargetDeprecationWarning=true + +org.gradle.caching=true +org.gradle.configuration-cache=true +#android.defaults.buildfeatures.resvalues=true +#android.sdk.defaultTargetSdkToCompileSdkIfUnset=false +#android.enableAppCompileTimeRClass=false +#android.usesSdkInManifest.disallowed=false +#android.uniquePackageNames=false +#android.dependency.useConstraints=true +#android.r8.strictFullModeForKeepRules=false +#android.r8.optimizedResourceShrinking=false +#android.builtInKotlin=false +#android.newDsl=false diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 0b5d1cc..de2c0fa 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -1,21 +1,22 @@ [versions] -accompanistPermissions = "0.36.0" -agp = "8.9.1" -kotlin = "2.1.10" -coreKtx = "1.17.0" -lifecycleRuntimeKtx = "2.8.7" -activityCompose = "1.10.1" -composeBom = "2025.04.00" -annotations = "26.0.2" -navigationCompose = "2.8.9" +accompanistPermissions = "0.37.3" +agp = "9.1.0" +kotlin = "2.3.20" +coreKtx = "1.18.0" +lifecycleRuntimeKtx = "2.10.0" +activityCompose = "1.13.0" +composeBom = "2026.03.01" +annotations = "26.1.0" +navigationCompose = "2.9.7" constraintlayout = "2.2.1" -haze = "1.6.10" -hazeMaterials = "1.6.10" +haze = "1.7.2" +hazeMaterials = "1.7.2" dynamicanimation = "1.1.0" -foundationLayout = "1.9.1" -uiTooling = "1.9.1" -ui = "1.9.2" -aboutLibraries = "13.0.0-rc01" +aboutLibraries = "14.0.1" +materialIconsCore = "1.7.8" +backdrop = "2.0.0-alpha03" +billing = "8.3.0" +hilt = "2.59.2" [libraries] accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" } @@ -33,14 +34,19 @@ androidx-constraintlayout = { group = "androidx.constraintlayout", name = "const haze = { group = "dev.chrisbanes.haze", name = "haze", version.ref = "haze" } haze-materials = { group = "dev.chrisbanes.haze", name = "haze-materials", version.ref = "hazeMaterials" } androidx-dynamicanimation = { group = "androidx.dynamicanimation", name = "dynamicanimation", version.ref = "dynamicanimation" } -androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout", version.ref = "foundationLayout" } -androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "uiTooling" } -androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "ui" } +androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout"} +androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } +androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" } aboutlibraries = { group = "com.mikepenz", name = "aboutlibraries", version.ref = "aboutLibraries" } aboutlibraries-compose-m3 = { group = "com.mikepenz", name = "aboutlibraries-compose-m3", version.ref = "aboutLibraries" } +androidx-compose-material-icons-core = { group = "androidx.compose.material", name = "material-icons-core", version.ref = "materialIconsCore" } +backdrop = { group = "io.github.kyant0", name = "backdrop", version.ref = "backdrop" } +billing = { group = "com.android.billingclient", name = "billing-ktx", version.ref = "billing" } +hilt = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } +hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } -kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } aboutLibraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "aboutLibraries" } +hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 4e7f0c7..c921434 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Oct 07 22:30:36 IST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists