Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
86551be
android: add accessibility stuff
kavishdevar Sep 7, 2025
4ee9b27
docs: update transparency mode format
kavishdevar Sep 7, 2025
d1bf540
android: don't start service every time MainActivity is launched
kavishdevar Sep 9, 2025
df9f443
android: add basic multidevice capabilities
kavishdevar Sep 10, 2025
0e9aadd
android: clean up a bit of AI gen'd code
kavishdevar Sep 10, 2025
aecbb06
android: clean up main service and remove minimum API on head gestures
kavishdevar Sep 10, 2025
fa00620
android: clean up a lot of stuff
kavishdevar Sep 10, 2025
c53356f
android: implement the accessiblity settings page
kavishdevar Sep 11, 2025
9e6d971
android: add EQ settings for phone and media
kavishdevar Sep 15, 2025
5bef8c3
android: add toggle for DID hook
kavishdevar Sep 15, 2025
792629a
docs: add 'has ownership' control cmd
kavishdevar Sep 15, 2025
93328d2
android: fix balance NaN error when amplification L/R is both zero
kavishdevar Sep 18, 2025
65d074e
android: bring back some accessiblity settings and add listeners for …
kavishdevar Sep 19, 2025
5c9beeb
android: add header to ATTManager
kavishdevar Sep 19, 2025
032b94e
android: use device name sent by the connected device in island
kavishdevar Sep 19, 2025
3699ee6
android: fix track color in tone volume
kavishdevar Sep 19, 2025
b5103a2
android: remove unused composable
kavishdevar Sep 19, 2025
5eff5b9
android: update eq sliders style
kavishdevar Sep 19, 2025
63baa15
android: fix text color in selectors
kavishdevar Sep 19, 2025
71a1f83
android: add delay before starting head tracking again
kavishdevar Sep 19, 2025
bb69a74
android: add a few options
kavishdevar Sep 19, 2025
6fd3cc1
android: a small ui fix
kavishdevar Sep 19, 2025
3cca786
docs: a few more control cmds
kavishdevar Sep 19, 2025
5aeb47b
android: add microphone setting
kavishdevar Sep 20, 2025
ecfdc05
android: improve dropdowns
kavishdevar Sep 20, 2025
3ace0e1
android: move attmanager to service to avoid trying to connect multip…
kavishdevar Sep 21, 2025
fe69082
android: add ui for hearing stuff
kavishdevar Sep 21, 2025
ce229be
android: add media assist options in hearing aid
kavishdevar Sep 22, 2025
4751f70
android: add hearing aid adjustments
kavishdevar Sep 22, 2025
4bc76de
android: liquidglass sliders
kavishdevar Sep 22, 2025
8760757
android: improve liquid glass sliders
kavishdevar Sep 22, 2025
26de422
android: little more liquid glass
kavishdevar Sep 22, 2025
173e06c
android: fix hearing aid parsing
kavishdevar Sep 22, 2025
29a35ce
android: remove customdeviceactivity from manifest
kavishdevar Sep 23, 2025
5f08edd
android: remove unused strings
kavishdevar Sep 23, 2025
7e5ee67
android: small ui tweaks
kavishdevar Sep 23, 2025
86a6a28
android: a very big commit
kavishdevar Sep 25, 2025
ab55096
android: move padding to StyledScaffold's content
kavishdevar Sep 25, 2025
56307c9
android: revert accidental capitalization on toggle label
kavishdevar Sep 25, 2025
d9795c4
merge main into multi-device-and-accessibility
kavishdevar Sep 25, 2025
8dc7a97
android: update usages for toggle
kavishdevar Sep 26, 2025
08738a1
android: liquidglass, maybe?
kavishdevar Sep 28, 2025
3f582b8
remove bleonly mode, use CAPod instead
kavishdevar Sep 28, 2025
1152f45
remove bleonly mode, use CAPod instead
kavishdevar Sep 28, 2025
5bc1dd2
android: fix switch styling
kavishdevar Sep 28, 2025
55cb69f
android: remove fade from transition
kavishdevar Sep 28, 2025
1076218
android: add A16's new bluetooth identifier for log collection
kavishdevar Sep 28, 2025
e9da7a2
android: fix crash in head gestures screen
kavishdevar Sep 28, 2025
147e511
android: show head gestures status in the navigation button
kavishdevar Sep 28, 2025
e158ba1
android: don't crash if att not available
kavishdevar Sep 28, 2025
5ec300a
android: use lazycolumn in airpods settings for better performance an…
kavishdevar Sep 28, 2025
48b715a
android: fix text color in troubshooting button and pressandhold sett…
kavishdevar Sep 28, 2025
504e703
android: bring back original confirmation dialog
kavishdevar Sep 28, 2025
bdb93ef
android: prevent hearing aid turning off itself
kavishdevar Sep 28, 2025
3a388da
android: hide media assist, not implemented
kavishdevar Sep 28, 2025
c2ebbef
docs: update README with new features
kavishdevar Sep 28, 2025
9d60dc3
docs: add demo video
kavishdevar Sep 28, 2025
b43e5f7
docs: add new screenshots for android
kavishdevar Sep 28, 2025
78ae31c
docs: update demo video position
kavishdevar Sep 28, 2025
6914dab
docs: app3 compatibility
kavishdevar Sep 29, 2025
395feab
docs: new control cmds '25 (again)
kavishdevar Sep 29, 2025
650b128
docs: change section title in control cmd doc
kavishdevar Sep 29, 2025
993f022
android: ui tweaks
kavishdevar Sep 30, 2025
8b49440
android: update styled slider thumb
kavishdevar Sep 30, 2025
342745e
android: add accessiblity service for camera control
kavishdevar Sep 30, 2025
c7dc545
android: add camera control, finally
kavishdevar Sep 30, 2025
b799cd1
android: add option to change camera app id
kavishdevar Sep 30, 2025
d9469c2
android: not use relative paths for executing commands
kavishdevar Sep 30, 2025
37313fb
android: fix transparency and noise cancellation flags
kavishdevar Sep 30, 2025
3a9c118
android: revert to using relative paths for su
kavishdevar Oct 1, 2025
993ba1b
android: bump version
kavishdevar Oct 1, 2025
0e0af35
android: don't crash if self MAC is not available
kavishdevar Oct 1, 2025
b7cc27f
android: remove unused LOCAL_ADDRESS permission
kavishdevar Oct 6, 2025
39a64ec
android: add opensource licenses
kavishdevar Oct 10, 2025
4a135fa
android: move navigation button to activity level
kavishdevar Oct 10, 2025
942ff82
android: update animation time on switch tap
kavishdevar Oct 10, 2025
a9b78ef
android: implement setting hearing test results
kavishdevar Oct 16, 2025
814eba8
android: update title in hearing test screen
kavishdevar Oct 16, 2025
c83ffca
docs: add screenshot for hearing test
kavishdevar Oct 16, 2025
ee9de99
android: fix haze for dialog when enabling hearing aid
kavishdevar Oct 19, 2025
1a2f513
android: parse device info
kavishdevar Oct 22, 2025
10fc96d
android: add support for various models
kavishdevar Oct 22, 2025
02edb51
android: fix a2dp connection
kavishdevar Oct 22, 2025
cea09b2
android: remove stray eq config in accessibility settings
kavishdevar Oct 26, 2025
55768be
android: improve connection handling
kavishdevar Oct 26, 2025
9b950e1
android: add a (very important) support dialog
kavishdevar Oct 26, 2025
63b6e2a
docs: add note for DID hook on android
kavishdevar Oct 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 14 additions & 37 deletions AAP Definitions.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,50 +279,27 @@ duplicated thrice for some reason
## Customize Transparency mode

```
52 18 00
For left bud
[Enabled]
12 18 00 [enabled]
<left bud>
[EQ1][EQ2][EQ3][EQ4][EQ5][EQ6][EQ7][EQ8]
[Amplification]
[Tone]
[Conversation Boost]
[Ambient Noise Reduction]
00 0080 3F
<same for the right bud>
<repeat for right bud>
```

<!-- demo packet
52 18 00 00 00 00 00 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 00 00 80 3f 00 00 80 3f 00 00 80 3f 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 00 00 80 3f 00 00 80 3f 00 00 80 3f

-->
<!--
5218 0000 0080 3F62 10DA 413D 0AF0 4160
E50C 42AC 9C1E 421B 2F29 429E 6F33 4293
1846 4293 1846 4206 9476 BF00 576E BB00
0080 3F00 0080 3F62 10DA 413D 0AF0 4160
E50C 42AC 9C1E 421B 2F29 429E 6F33 4293
1846 4293 1846 4200 0080 BF00 576E BB00
0080 3F00 0080 3F
-->

<!--
5218 0000 0000 0062 10DA 413D 0AF0 4160
E50C 42AC 9C1E 421B 2F29 429E 6F33 4293
1846 4293 1846 4206 9476 BF00 576E BB00
0080 3F00 0080 3F62 10DA 413D 0AF0 4160
E50C 42AC 9C1E 421B 2F29 429E 6F33 4293
1846 4293 1846 4200 0080 BF00 576E BB00
0080 3F00 0080 3F
-->

All values are formatted as Little Endian from float values.
| Data | Type | Value range |
|---------------------|---------------|-------------|
| Enabled | Little Endian | 0 or 1 |
| EQ | Little Endian | 0 to 100 |
| Amplification | Little Endian | -1 to 1 |
| Tone | Little Endian | -1 to 1 |
| Conversation Boost | Little Endian | 0 or 1 |

All values are formatted as IEEE 754 floats in little endian order.
| Data | Type | Range |
|-------------------------|---------------|-------|
| Enabled | IEEE754 Float | 0/1 |
| EQ | IEEE754 Float | 0-100 |
| Amplification | IEEE754 Float | 0-2 |
| Tone | IEEE754 Float | 0-2 |
| Conversation Boost | IEEE754 Float | 0/1 |
| Ambient Noise Reduction | IEEE754 Float | 0-1 |
| Ambient Noise Reduction | IEEE754 Float | 0-1 |

> [!IMPORTANT]
> Also send the [Headphone Accomodation](#headphone-accomodation) after this.
Expand Down
83 changes: 47 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@

## What is LibrePods?

LibrePods unlocks Apple's exclusive AirPods features on non-Apple devices. Get access to noise control modes, adaptive transparency, ear detection, battery status, and more - all the premium features you paid for but Apple locked to their ecosystem.
LibrePods unlocks Apple's exclusive AirPods features on non-Apple devices. Get access to noise control modes, adaptive transparency, ear detection, hearing aid, customized transparency mode, battery status, and more - all the premium features you paid for but Apple locked to their ecosystem.

## Device Compatibility

| Status | Device | Features |
|--------|--------|----------|
| ✅ | AirPods Pro (2nd Gen) | Fully supported and tested |
| ⚠️ | Other AirPods models | Basic features (battery status, ear detection) should work |
| Status | Device | Features |
| ------ | --------------------- | ---------------------------------------------------------- |
| ✅ | AirPods Pro (2nd Gen) | Fully supported and tested |
| ✅ | AirPods Pro (3rd Gen) | Fully supported (except heartrate monitoring) |
| ⚠️ | Other AirPods models | Basic features (battery status, ear detection) should work |

Most features should work with any AirPods. Currently, testing is only performed with AirPods Pro 2.
Most features should work with any AirPods. Currently, I've only got AirPods Pro 2 to test with.

## Key Features

Expand All @@ -29,6 +30,9 @@ Most features should work with any AirPods. Currently, testing is only performed
- **Battery Status**: Accurate battery levels
- **Head Gestures**: Answer calls just by nodding your head
- **Conversational Awareness**: Volume automatically lowers when you speak
- **Hearing Aid\***
- **Customize Transparency Mode\***
- **Multi-device connectivity\*** (upto 2 devices)
- **Other customizations**:
- Rename your AirPods
- Customize long-press actions
Expand Down Expand Up @@ -58,12 +62,18 @@ For installation and detailed info, see the [Linux README](/linux/README.md).

#### Screenshots

| | | |
|-------------------|-------------------|-------------------|
| ![Settings 1](/android/imgs/settings-1.png) | ![Settings 2](/android/imgs/settings-2.png) | ![Debug Screen](/android/imgs/debug.png) |
| ![Battery Notification and QS Tile for NC Mode](/android/imgs/notification-and-qs.png) | ![Popup](/android/imgs/popup.png) | ![Head Tracking and Gestures](/android/imgs/head-tracking-and-gestures.png) |
| ![Long Press Configuration](/android/imgs/long-press.png) | ![Widget](/android/imgs/widget.png) | ![Customizations 1](/android/imgs/customizations-1.png) |
| ![Customizations 2](/android/imgs/customizations-2.png) | ![audio-popup](/android/imgs/audio-connected-island.png) | |
| | | |
| -------------------------------------------------------------------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------- |
| ![Settings 1](/android/imgs/settings-1.png) | ![Settings 2](/android/imgs/settings-2.png) | ![Debug Screen](/android/imgs/debug.png) |
| ![Battery Notification and QS Tile for NC Mode](/android/imgs/notification-and-qs.png) | ![Popup](/android/imgs/popup.png) | ![Head Tracking and Gestures](/android/imgs/head-tracking-and-gestures.png) |
| ![Long Press Configuration](/android/imgs/long-press.png) | ![Widget](/android/imgs/widget.png) | ![Customizations 1](/android/imgs/customizations-1.png) |
| ![Customizations 2](/android/imgs/customizations-2.png) | ![accessibility](/android/imgs/accessibility.png) | ![transparency](/android/imgs/transparency.png) |
| ![hearing-aid](/android/imgs/hearing-aid.png) | ![hearing-test](/android/imgs/hearing-test.png) | ![hearing-aid-adjustments](/android/imgs/hearing-aid-adjustments.png) |


here's a very unprofessional demo video

https://github.com/user-attachments/assets/43911243-0576-4093-8c55-89c1db5ea533

#### Root Requirement

Expand All @@ -72,24 +82,44 @@ For installation and detailed info, see the [Linux README](/linux/README.md).
>
> There are **no exceptions** to the root requirement until Google merges the fix.

## Bluetooth DID (Device Identification) Hook

Turns out, if you change the manufacturerid to that of Apple, you get access to several special features!

### Multi-device Connectivity

Upto two devices can be simultaneously connected to AirPods, for audio and control both. Seamless connection switching. The same notification shows up on Apple device when Android takes over the AirPods as if it were an Apple device ("Move to iPhone"). Android also shows a popup when the other device takes over.

### Accessibility Settings and Hearing Aid

Accessibility settings like customizing transparency mode (amplification, balance, tone, conversation boost, and ambient noise reduction), and loud sound reduction can be configured.

The hearing aid feature can now also be used. Currently it can only be used to adjust the settings, not actually take a hearing test because it requires much more precision. It is much better to use an already available audiogram result.

>[!NOTE]
> To enable these features, enable App Settings -> `act as Apple Device`.
> This only works if you use the Xposed method or patch the library yourself. The root module method does not support this feature currently.

#### Installation Methods

##### Method 1: Xposed Module (Recommended)
This method is less intrusive and should be tried first:

1. Install LSPosed, or another Xposed provider on your rooted device
2. Download the LibrePods app from the releases section, and install it.
3. Enable the Xposed module for the bluetooth app in your Xposed manager
4. Follow the instructions in the app to set up the module.
5. Open the app and connect your AirPods
3. Enable the Xposed module for the bluetooth app in your Xposed manager.
4. Disable unmount modules for the Bluetooth app if enabled.
5. Follow the instructions in the app to set up the module.
6. Open the app and connect your AirPods

##### Method 2: Root Module (Backup Option)
If the Xposed method doesn't work for you:

1. Download the `btl2capfix.zip` module from the releases section
2. Install it using your preferred root manager (KernelSU, Apatch, or Magisk).
3. Reboot your device
4. Connect your AirPods
3. Disable Unmount modules for the Bluetooth aop if enabled.
4. Reboot your device
5. Connect your AirPods

##### Method 3: Patching it yourself
If you prefer to patch the Bluetooth stack yourself, follow these steps:
Expand All @@ -111,25 +141,6 @@ If you're unfamiliar with these steps, search for tutorials online or ask in And

- When renaming your AirPods through the app, you'll need to re-pair them with your phone for the name change to take effect. This is a limitation of how Bluetooth device naming works on Android.

## Development Resources

For developers interested in the protocol details, check out the [AAP Definitions](/AAP%20Definitions.md) documentation.

## CrossDevice Stuff

> [!IMPORTANT]
> This feature is still in early development and might not work as expected. No support is provided for this feature yet.

### Features in Development

- **Battery Status Sync**: Get battery status on any device when you connect your AirPods to one of them
- **Cross-device Controls**: Control your AirPods from either device when connected to one
- **Automatic Device Switching**: Seamlessly switch between Linux and Android devices based on active audio sources

Check out the demo below:

https://github.com/user-attachments/assets/d08f8a51-cd52-458b-8e55-9b44f4d5f3ab

## Star History

[![Star History Chart](https://api.star-history.com/svg?repos=kavishdevar/librepods&type=Date)](https://star-history.com/#kavishdevar/librepods&Date)
Expand Down
1 change: 1 addition & 0 deletions android/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
crowdin.yml
*.iml
.gradle
/local.properties
Expand Down
33 changes: 28 additions & 5 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@ plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.aboutLibraries)
id("kotlin-parcelize")
}

android {
namespace = "me.kavishdevar.librepods"
compileSdk = 35
compileSdk = 36

defaultConfig {
applicationId = "me.kavishdevar.librepods"
minSdk = 28
targetSdk = 35
versionCode = 7
versionName = "0.1.0-rc.4"
targetSdk = 36
versionCode = 8
versionName = "0.2.0-beta.1"
}

buildTypes {
Expand Down Expand Up @@ -43,6 +44,11 @@ android {
version = "3.22.1"
}
}
sourceSets {
getByName("main") {
res.srcDirs("src/main/res", "src/main/res-apple")
}
}
}

dependencies {
Expand All @@ -62,5 +68,22 @@ dependencies {
implementation(libs.haze)
implementation(libs.haze.materials)
implementation(libs.androidx.dynamicanimation)
compileOnly(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar"))))
implementation(libs.androidx.compose.ui)
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"))
}

aboutLibraries {
export{
prettyPrint = true
excludeFields = listOf("generated")
outputFile = file("src/main/res/raw/aboutlibraries.json")
}
}
Binary file added android/app/libs/backdrop-debug.aar
Binary file not shown.
Binary file added android/app/libs/backdrop-release.aar
Binary file not shown.
36 changes: 19 additions & 17 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
android:required="false" />

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"
tools:ignore="ForegroundServicesPolicy" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"
tools:ignore="ProtectedPermissions" />
Expand All @@ -30,11 +31,10 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="30" />

<protected-broadcast android:name="batterywidget.impl.action.update_bluetooth_data" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
android:maxSdkVersion="30" />

<application
android:allowBackup="true"
Expand All @@ -60,6 +60,7 @@
android:name="android.appwidget.provider"
android:resource="@xml/noise_control_widget_info" />
</receiver>

<receiver
android:name=".widgets.BatteryWidget"
android:exported="false">
Expand All @@ -72,15 +73,6 @@
android:resource="@xml/battery_widget_info" />
</receiver>

<activity
android:name=".CustomDevice"
android:exported="true"
android:label="@string/title_activity_custom_device"
android:theme="@style/Theme.LibrePods">
<intent-filter>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:exported="true"
Expand All @@ -90,13 +82,13 @@

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<!-- <intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="librepods"
<data android:scheme="librepods"
android:host="add-magic-keys" />
</intent-filter>
</intent-filter> -->
</activity>

<activity
Expand Down Expand Up @@ -124,7 +116,17 @@
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>

<service
android:name=".services.AppListenerService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/app_listener_service_config" />
</service>
<receiver
android:name=".receivers.BootReceiver"
android:enabled="true"
Expand Down
Loading