mirror of
https://github.com/kavishdevar/librepods.git
synced 2026-05-16 05:26:04 +00:00
Compare commits
4 Commits
nightly-84
...
nightly-d1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1933c3b67 | ||
|
|
fb44f01ac0 | ||
|
|
93a93cbe68 | ||
|
|
a4898293b8 |
12
README.md
12
README.md
@@ -76,19 +76,15 @@ https://github.com/user-attachments/assets/43911243-0576-4093-8c55-89c1db5ea533
|
|||||||
|
|
||||||
### Root Requirement
|
### Root Requirement
|
||||||
|
|
||||||
The app needs root because of a bug in the Android Bluetooth stack Fluoride/non-compliance of Apple with Bluetooth standards. You must have Xposed installed for the app to workaround this bug and connect to AirPods.
|
LibrePods **may** require root depending on your device/OS and what features you want access to:
|
||||||
|
|
||||||
[https://issuetracker.google.com/issues/371713238](https://issuetracker.google.com/issues/371713238)
|
- Features requiring the VendorID hook ([the features marked with an asterisk here](https://github.com/kavishdevar/librepods#key-features)) will always require root regardless of your device/OS.
|
||||||
|
- On **ColorOS/OxygenOS 16** and **Pixel devices on Android 16 QPR3** (with the latest Google Play system update), LibrePods does not need root for most features (except those requiring the VendorID hook mentioned above).
|
||||||
Please do not comment in the thread. The issue has already been resolved and should be available in Android 17 for all devices.
|
- On other devices, LibrePods needs root because of a bug in the Android Bluetooth stack Fluoride/non-compliance of Apple with Bluetooth standards. You must have Xposed installed for the app to workaround this bug and connect to AirPods. [This issue is being tracked here](https://issuetracker.google.com/issues/371713238). Please do not comment on the issue thread. The issue has already been resolved and should be available in **Android 17** for all devices.
|
||||||
|
|
||||||
However, if you are using ColorOS/OxygenOS 16, Android 16 QPR3 on Pixel (ensure you're on the latest Play system update), you don't need root for most features.
|
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> This workaround with Xposed is not guaranteed to work on all devices.
|
> This workaround with Xposed is not guaranteed to work on all devices.
|
||||||
|
|
||||||
Features requiring the VendorID hook will still require root. These features include customizing transparency mode, setting up hearing aid, and use Bluetooth Multipoint.
|
|
||||||
|
|
||||||
### Troubleshooting steps for common errors
|
### Troubleshooting steps for common errors
|
||||||
- Ensure the correct scope is set in LSPosed/Vector.
|
- Ensure the correct scope is set in LSPosed/Vector.
|
||||||
- Ensure there is no root-hiding module preventing the hook from loading on the Bluetooth app.
|
- Ensure there is no root-hiding module preventing the hook from loading on the Bluetooth app.
|
||||||
|
|||||||
@@ -157,6 +157,48 @@ fun AppSettingsScreen(
|
|||||||
enabled = state.isPremium
|
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(
|
||||||
text = stringResource(R.string.conversational_awareness), style = TextStyle(
|
text = stringResource(R.string.conversational_awareness), style = TextStyle(
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ fun HeadTrackingScreen(viewModel: AirPodsViewModel, navController: NavController
|
|||||||
label = "Head Gestures",
|
label = "Head Gestures",
|
||||||
checked = state.headGesturesEnabled,
|
checked = state.headGesturesEnabled,
|
||||||
onCheckedChange = { viewModel.setHeadGesturesEnabled(it) },
|
onCheckedChange = { viewModel.setHeadGesturesEnabled(it) },
|
||||||
enabled = state.isPremium,
|
enabled = state.isPremium || state.headGesturesEnabled,
|
||||||
description = stringResource(R.string.head_gestures_details)
|
description = stringResource(R.string.head_gestures_details)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ data class AppSettingsUiState(
|
|||||||
val cameraPackageError: String? = null,
|
val cameraPackageError: String? = null,
|
||||||
val vendorIdHook: Boolean = false,
|
val vendorIdHook: Boolean = false,
|
||||||
val isPremium: 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) {
|
class AppSettingsViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
@@ -86,7 +88,9 @@ class AppSettingsViewModel(application: Application) : AndroidViewModel(applicat
|
|||||||
conversationalAwarenessVolume = sharedPreferences.getInt("conversational_awareness_volume", 43).toFloat(),
|
conversationalAwarenessVolume = sharedPreferences.getInt("conversational_awareness_volume", 43).toFloat(),
|
||||||
cameraPackageValue = sharedPreferences.getString("custom_camera_package", "") ?: "",
|
cameraPackageValue = sharedPreferences.getString("custom_camera_package", "") ?: "",
|
||||||
vendorIdHook = xposedRemotePref.getBoolean("vendor_id_hook", false),
|
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)
|
xposedRemotePref.putBoolean("vendor_id_hook", enabled)
|
||||||
_uiState.update { it.copy(vendorIdHook = 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) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1636,6 +1636,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
|
|
||||||
var popupShown = false
|
var popupShown = false
|
||||||
fun showPopup(service: Service, name: String) {
|
fun showPopup(service: Service, name: String) {
|
||||||
|
if (!sharedPreferences.getBoolean("show_bottom_sheet_popup", true)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (!Settings.canDrawOverlays(service)) {
|
if (!Settings.canDrawOverlays(service)) {
|
||||||
Log.d(TAG, "No permission for SYSTEM_ALERT_WINDOW")
|
Log.d(TAG, "No permission for SYSTEM_ALERT_WINDOW")
|
||||||
return
|
return
|
||||||
@@ -1660,6 +1663,9 @@ class AirPodsService : Service(), SharedPreferences.OnSharedPreferenceChangeList
|
|||||||
otherDeviceName: String? = null
|
otherDeviceName: String? = null
|
||||||
) {
|
) {
|
||||||
Log.d(TAG, "Showing island window")
|
Log.d(TAG, "Showing island window")
|
||||||
|
if (!sharedPreferences.getBoolean("show_island_popup", true)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (!Settings.canDrawOverlays(service)) {
|
if (!Settings.canDrawOverlays(service)) {
|
||||||
Log.d(TAG, "No permission for SYSTEM_ALERT_WINDOW")
|
Log.d(TAG, "No permission for SYSTEM_ALERT_WINDOW")
|
||||||
return
|
return
|
||||||
|
|||||||
7
android/app/src/main/res/values-de/strings.xml
Normal file
7
android/app/src/main/res/values-de/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||||
|
<string name="popup_animations">Popup-Animationen</string>
|
||||||
|
<string name="show_bottom_sheet_popup">Popup unten</string>
|
||||||
|
<string name="show_bottom_sheet_popup_description">Zeigt das Popup im iOS-Stil unten an, wenn AirPods sich verbinden.</string>
|
||||||
|
<string name="show_island_popup">Dynamic Island Popup</string>
|
||||||
|
<string name="show_island_popup_description">Zeigt das Popup im Dynamic-Island-Stil oben für Verbindungs- und Übergabe-Ereignisse.</string>
|
||||||
|
</resources>
|
||||||
@@ -210,4 +210,9 @@
|
|||||||
<string name="listening_mode_transparency_description">Deja entrar los sonidos externos</string>
|
<string name="listening_mode_transparency_description">Deja entrar los sonidos externos</string>
|
||||||
<string name="listening_mode_adaptive_description">Ajuste dinámico del ruido externo</string>
|
<string name="listening_mode_adaptive_description">Ajuste dinámico del ruido externo</string>
|
||||||
<string name="listening_mode_noise_cancellation_description">Bloquea los sonidos externos</string>
|
<string name="listening_mode_noise_cancellation_description">Bloquea los sonidos externos</string>
|
||||||
|
<string name="popup_animations">Animaciones emergentes</string>
|
||||||
|
<string name="show_bottom_sheet_popup">Ventana emergente inferior</string>
|
||||||
|
<string name="show_bottom_sheet_popup_description">Muestra la ventana emergente estilo iOS en la parte inferior cuando los AirPods se conectan.</string>
|
||||||
|
<string name="show_island_popup">Ventana emergente Dynamic Island</string>
|
||||||
|
<string name="show_island_popup_description">Muestra la ventana emergente estilo Dynamic Island en la parte superior para eventos de conexión y traspaso.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -210,4 +210,9 @@
|
|||||||
<string name="listening_mode_transparency_description">Laisser entrer les sons extérieurs</string>
|
<string name="listening_mode_transparency_description">Laisser entrer les sons extérieurs</string>
|
||||||
<string name="listening_mode_adaptive_description">Ajuster dynamiquement les sons extérieurs</string>
|
<string name="listening_mode_adaptive_description">Ajuster dynamiquement les sons extérieurs</string>
|
||||||
<string name="listening_mode_noise_cancellation_description">Bloquer les sons extérieurs</string>
|
<string name="listening_mode_noise_cancellation_description">Bloquer les sons extérieurs</string>
|
||||||
|
<string name="popup_animations">Animations contextuelles</string>
|
||||||
|
<string name="show_bottom_sheet_popup">Fenêtre contextuelle en bas</string>
|
||||||
|
<string name="show_bottom_sheet_popup_description">Afficher la fenêtre contextuelle de style iOS en bas de l\'écran lors de la connexion des AirPods.</string>
|
||||||
|
<string name="show_island_popup">Fenêtre Dynamic Island</string>
|
||||||
|
<string name="show_island_popup_description">Afficher la fenêtre de style Dynamic Island en haut de l\'écran pour les événements de connexion et de transfert.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -210,4 +210,9 @@
|
|||||||
<string name="listening_mode_transparency_description">Permite sons externos</string>
|
<string name="listening_mode_transparency_description">Permite sons externos</string>
|
||||||
<string name="listening_mode_adaptive_description">Ajusta dinamicamente o ruído externo</string>
|
<string name="listening_mode_adaptive_description">Ajusta dinamicamente o ruído externo</string>
|
||||||
<string name="listening_mode_noise_cancellation_description">Bloqueia sons externos</string>
|
<string name="listening_mode_noise_cancellation_description">Bloqueia sons externos</string>
|
||||||
|
<string name="popup_animations">Animações de pop-up</string>
|
||||||
|
<string name="show_bottom_sheet_popup">Pop-up inferior</string>
|
||||||
|
<string name="show_bottom_sheet_popup_description">Exibe o pop-up estilo iOS na parte inferior quando os AirPods se conectam.</string>
|
||||||
|
<string name="show_island_popup">Pop-up Dynamic Island</string>
|
||||||
|
<string name="show_island_popup_description">Exibe o pop-up estilo Dynamic Island no topo da tela em eventos de conexão e transferência.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -140,6 +140,11 @@
|
|||||||
<string name="widget">Widget</string>
|
<string name="widget">Widget</string>
|
||||||
<string name="show_phone_battery_in_widget">Show phone battery in widget</string>
|
<string name="show_phone_battery_in_widget">Show phone battery in widget</string>
|
||||||
<string name="show_phone_battery_in_widget_description">Display your phone\'s battery level in the widget alongside AirPods battery</string>
|
<string name="show_phone_battery_in_widget_description">Display your phone\'s battery level in the widget alongside AirPods battery</string>
|
||||||
|
<string name="popup_animations">Popup Animations</string>
|
||||||
|
<string name="show_bottom_sheet_popup">Bottom sheet popup</string>
|
||||||
|
<string name="show_bottom_sheet_popup_description">Show the iOS-style modal popup at the bottom when AirPods connect.</string>
|
||||||
|
<string name="show_island_popup">Dynamic Island popup</string>
|
||||||
|
<string name="show_island_popup_description">Show the Dynamic Island-style popup at the top for connection and takeover events.</string>
|
||||||
<string name="conversational_awareness_volume">Conversational Awareness Volume</string>
|
<string name="conversational_awareness_volume">Conversational Awareness Volume</string>
|
||||||
<string name="quick_settings_tile">Quick Settings Tile</string>
|
<string name="quick_settings_tile">Quick Settings Tile</string>
|
||||||
<string name="open_dialog_for_controlling">Open dialog for controlling</string>
|
<string name="open_dialog_for_controlling">Open dialog for controlling</string>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"version": "v0.2.6",
|
"version": "v0.2.6",
|
||||||
"versionCode": 46,
|
"versionCode": 46,
|
||||||
"zipUrl": "https://github.com/kavishdevar/librepods/releases/download/v0.2.3/LibrePods-FOSS-v0.2.3-release.zip",
|
"zipUrl": "https://github.com/kavishdevar/librepods/releases/download/v0.2.6/LibrePods-FOSS-v0.2.6-release.zip",
|
||||||
"changelog": "https://raw.githubusercontent.com/kavishdevar/librepods/main/CHANGELOG.md"
|
"changelog": "https://raw.githubusercontent.com/kavishdevar/librepods/main/extras/CHANGELOG.md"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user