From d1933c3b67f1459f6e5978a3751565f83e247827 Mon Sep 17 00:00:00 2001
From: thisisAcidic <63687253+thisisAcidic@users.noreply.github.com>
Date: Tue, 5 May 2026 09:18:22 +0200
Subject: [PATCH] android: add popup toggles (#561)
* android: add toggles to disable bottom sheet and dynamic island popups
* android: translations for popup customization (de, es, fr, pt)
---
.../presentation/screens/AppSettingsScreen.kt | 42 +++++++++++++++++++
.../viewmodel/AppSettingsViewModel.kt | 18 +++++++-
.../librepods/services/AirPodsService.kt | 6 +++
.../app/src/main/res/values-de/strings.xml | 7 ++++
.../app/src/main/res/values-es/strings.xml | 5 +++
.../app/src/main/res/values-fr/strings.xml | 5 +++
.../app/src/main/res/values-pt/strings.xml | 5 +++
android/app/src/main/res/values/strings.xml | 5 +++
8 files changed, 91 insertions(+), 2 deletions(-)
create mode 100644 android/app/src/main/res/values-de/strings.xml
diff --git a/android/app/src/main/java/me/kavishdevar/librepods/presentation/screens/AppSettingsScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/presentation/screens/AppSettingsScreen.kt
index a8d4c67..04655d2 100644
--- a/android/app/src/main/java/me/kavishdevar/librepods/presentation/screens/AppSettingsScreen.kt
+++ b/android/app/src/main/java/me/kavishdevar/librepods/presentation/screens/AppSettingsScreen.kt
@@ -157,6 +157,48 @@ fun AppSettingsScreen(
enabled = state.isPremium
)
+ Text(
+ text = stringResource(R.string.popup_animations), 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)
+ )
+
+ Spacer(modifier = Modifier.height(2.dp))
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(
+ backgroundColor, RoundedCornerShape(28.dp)
+ )
+ .padding(vertical = 4.dp)
+ ) {
+ StyledToggle(
+ label = stringResource(R.string.show_bottom_sheet_popup),
+ description = stringResource(R.string.show_bottom_sheet_popup_description),
+ checked = state.showBottomSheetPopup,
+ onCheckedChange = viewModel::setShowBottomSheetPopup,
+ independent = false
+ )
+
+ HorizontalDivider(
+ thickness = 1.dp,
+ color = Color(0x40888888),
+ modifier = Modifier.padding(horizontal = 12.dp)
+ )
+
+ StyledToggle(
+ label = stringResource(R.string.show_island_popup),
+ description = stringResource(R.string.show_island_popup_description),
+ checked = state.showIslandPopup,
+ onCheckedChange = viewModel::setShowIslandPopup,
+ independent = false
+ )
+ }
+
Text(
text = stringResource(R.string.conversational_awareness), style = TextStyle(
fontSize = 14.sp,
diff --git a/android/app/src/main/java/me/kavishdevar/librepods/presentation/viewmodel/AppSettingsViewModel.kt b/android/app/src/main/java/me/kavishdevar/librepods/presentation/viewmodel/AppSettingsViewModel.kt
index 8125fa6..b662b60 100644
--- a/android/app/src/main/java/me/kavishdevar/librepods/presentation/viewmodel/AppSettingsViewModel.kt
+++ b/android/app/src/main/java/me/kavishdevar/librepods/presentation/viewmodel/AppSettingsViewModel.kt
@@ -32,7 +32,9 @@ data class AppSettingsUiState(
val cameraPackageError: String? = null,
val vendorIdHook: Boolean = false,
val isPremium: Boolean = false,
- val connectionSuccessful: Boolean = false
+ val connectionSuccessful: Boolean = false,
+ val showBottomSheetPopup: Boolean = true,
+ val showIslandPopup: Boolean = true
)
class AppSettingsViewModel(application: Application) : AndroidViewModel(application) {
@@ -86,7 +88,9 @@ class AppSettingsViewModel(application: Application) : AndroidViewModel(applicat
conversationalAwarenessVolume = sharedPreferences.getInt("conversational_awareness_volume", 43).toFloat(),
cameraPackageValue = sharedPreferences.getString("custom_camera_package", "") ?: "",
vendorIdHook = xposedRemotePref.getBoolean("vendor_id_hook", false),
- connectionSuccessful = sharedPreferences.getBoolean("connection_successful", false)
+ connectionSuccessful = sharedPreferences.getBoolean("connection_successful", false),
+ showBottomSheetPopup = sharedPreferences.getBoolean("show_bottom_sheet_popup", true),
+ showIslandPopup = sharedPreferences.getBoolean("show_island_popup", true)
)
}
}
@@ -176,4 +180,14 @@ class AppSettingsViewModel(application: Application) : AndroidViewModel(applicat
xposedRemotePref.putBoolean("vendor_id_hook", enabled)
_uiState.update { it.copy(vendorIdHook = enabled) }
}
+
+ fun setShowBottomSheetPopup(enabled: Boolean) {
+ sharedPreferences.edit { putBoolean("show_bottom_sheet_popup", enabled) }
+ _uiState.update { it.copy(showBottomSheetPopup = enabled) }
+ }
+
+ fun setShowIslandPopup(enabled: Boolean) {
+ sharedPreferences.edit { putBoolean("show_island_popup", enabled) }
+ _uiState.update { it.copy(showIslandPopup = enabled) }
+ }
}
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 464eddd..2468c4c 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
@@ -1636,6 +1636,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
var popupShown = false
fun showPopup(service: Service, name: String) {
+ if (!sharedPreferences.getBoolean("show_bottom_sheet_popup", true)) {
+ return
+ }
if (!Settings.canDrawOverlays(service)) {
Log.d(TAG, "No permission for SYSTEM_ALERT_WINDOW")
return
@@ -1660,6 +1663,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
otherDeviceName: String? = null
) {
Log.d(TAG, "Showing island window")
+ if (!sharedPreferences.getBoolean("show_island_popup", true)) {
+ return
+ }
if (!Settings.canDrawOverlays(service)) {
Log.d(TAG, "No permission for SYSTEM_ALERT_WINDOW")
return
diff --git a/android/app/src/main/res/values-de/strings.xml b/android/app/src/main/res/values-de/strings.xml
new file mode 100644
index 0000000..51b4077
--- /dev/null
+++ b/android/app/src/main/res/values-de/strings.xml
@@ -0,0 +1,7 @@
+
+ Popup-Animationen
+ Popup unten
+ Zeigt das Popup im iOS-Stil unten an, wenn AirPods sich verbinden.
+ Dynamic Island Popup
+ Zeigt das Popup im Dynamic-Island-Stil oben für Verbindungs- und Übergabe-Ereignisse.
+
diff --git a/android/app/src/main/res/values-es/strings.xml b/android/app/src/main/res/values-es/strings.xml
index 2d7ea81..34125e3 100644
--- a/android/app/src/main/res/values-es/strings.xml
+++ b/android/app/src/main/res/values-es/strings.xml
@@ -210,4 +210,9 @@
Deja entrar los sonidos externos
Ajuste dinámico del ruido externo
Bloquea los sonidos externos
+ Animaciones emergentes
+ Ventana emergente inferior
+ Muestra la ventana emergente estilo iOS en la parte inferior cuando los AirPods se conectan.
+ Ventana emergente Dynamic Island
+ Muestra la ventana emergente estilo Dynamic Island en la parte superior para eventos de conexión y traspaso.
diff --git a/android/app/src/main/res/values-fr/strings.xml b/android/app/src/main/res/values-fr/strings.xml
index 8595b37..9a24bcd 100644
--- a/android/app/src/main/res/values-fr/strings.xml
+++ b/android/app/src/main/res/values-fr/strings.xml
@@ -210,4 +210,9 @@
Laisser entrer les sons extérieurs
Ajuster dynamiquement les sons extérieurs
Bloquer les sons extérieurs
+ Animations contextuelles
+ Fenêtre contextuelle en bas
+ Afficher la fenêtre contextuelle de style iOS en bas de l\'écran lors de la connexion des AirPods.
+ Fenêtre Dynamic Island
+ Afficher la fenêtre de style Dynamic Island en haut de l\'écran pour les événements de connexion et de transfert.
diff --git a/android/app/src/main/res/values-pt/strings.xml b/android/app/src/main/res/values-pt/strings.xml
index 92e969d..c646198 100644
--- a/android/app/src/main/res/values-pt/strings.xml
+++ b/android/app/src/main/res/values-pt/strings.xml
@@ -210,4 +210,9 @@
Permite sons externos
Ajusta dinamicamente o ruído externo
Bloqueia sons externos
+ Animações de pop-up
+ Pop-up inferior
+ Exibe o pop-up estilo iOS na parte inferior quando os AirPods se conectam.
+ Pop-up Dynamic Island
+ Exibe o pop-up estilo Dynamic Island no topo da tela em eventos de conexão e transferência.
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 3a1db0b..fbbea37 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -140,6 +140,11 @@
Widget
Show phone battery in widget
Display your phone\'s battery level in the widget alongside AirPods battery
+ Popup Animations
+ Bottom sheet popup
+ Show the iOS-style modal popup at the bottom when AirPods connect.
+ Dynamic Island popup
+ Show the Dynamic Island-style popup at the top for connection and takeover events.
Conversational Awareness Volume
Quick Settings Tile
Open dialog for controlling