This commit removes the `BUILDTIME` field from `BuildConfig` across all modules. This change improves build caching and reproducibility by eliminating a value that changed with every build.
The `BUILDTIME` was previously used in `VERSION_DESCRIPTION_LONG` but is no longer included.
This commit refactors the `buildSrc` module by:
- Removing the `setupLibraryDefaults()` extension function from `ProjectConfig.kt` as its functionality is largely covered by standard Gradle plugin configurations.
- Inlining version numbers for Moshi and various testing libraries directly into `Dependencies.kt`, removing them from `Versions.kt`. This simplifies version management for these specific libraries.
- Removing unused version declarations from `Versions.kt`.
Additionally, this commit updates the following dependencies:
- Kotlin from 2.1.21 to 2.2.10
- Android Gradle Plugin from 8.11.0 to 8.12.2
- KSP from 2.1.21-2.0.1 to 2.2.10-2.0.2
- Dagger from 2.56.2 to 2.57.1
- AndroidX Navigation from 2.8.9 to 2.9.3
The `targetSdk` was also removed from the `app-common` module's `defaultConfig` as it's typically inherited or set at the app level.
This commit introduces a new setting that allows users to keep the "connected" notification visible even after the monitored device disconnects.
When this setting is enabled and the "Extra notification" is also active, the `MonitorWorker` will no longer cancel the connected notification upon finishing. This provides users with the option to see the last known battery levels after disconnection.
A new preference `keepConnectedNotificationAfterDisconnect` is added to `GeneralSettings` and the UI.
This commit introduces localized error messages for Google Play billing issues and refactors the error handling in `UpgradeRepoGplay`.
- `BillingException` and `BillingResultException` now implement `HasLocalizedError` to provide user-friendly error messages.
- New string translations for billing error labels and descriptions have been added for various languages.
- `UpgradeRepoGplay` now includes an `error` field in its `Info` state to propagate billing errors.
- The `upgradeInfo` flow in `UpgradeRepoGplay` has been updated with a `retryWhen` operator to handle errors more gracefully. If an error occurs but the pro state was recent, it emits a grace period state. Otherwise, it emits the error.
- `OverviewFragmentVM` now observes `upgradeState` and posts any non-pro errors to `errorEvents` for display.
This commit updates the Jekyll configuration file (`_config.yml`) to:
- Include `README.md` and `CHANGELOG.md` in the site build.
- Exclude `CONTRIBUTING.md` and `CLAUDE.md` from the site build.
This commit introduces a `CLAUDE.md` file.
This file contains instructions and context for the Claude AI to assist with development in this repository. It includes:
- Common build, test, and code quality commands.
- An overview of the project's multi-module architecture, core patterns (MVVM, DI with Hilt, Coroutines, Repository), and key components like `PodMonitor` and the reaction system.
- Details on build flavors (FOSS, Google Play) and build types (debug, beta, release).
- A description of the data flow architecture.
- Information on the testing strategy and key dependencies.
- Development notes regarding Bluetooth LE implementation and multi-platform considerations for phone and Wear OS.
This commit incorporates various translation improvements across multiple languages (Ukrainian, Greek, French, Turkish, Spanish (Mexico), Arabic, Korean, Urdu (India), Indonesian, Romanian, Zulu) for UI strings and Play Store metadata.
Additionally, this commit introduces new translations for Norwegian Bokmål (nb) for the Play Store listing (title, short description, full description) and adds initial string resources for Romansh (rm).
This commit introduces an `EdgeToEdgeHelper` class to centralize and simplify the application of window insets for edge-to-edge display.
Key changes:
- Created `EdgeToEdgeHelper` to manage padding based on system bar insets.
- Migrated various Fragments (`OverviewFragment`, `OnboardingFragment`, `SettingsFragment`, `TroubleShooterFragment`) to use `EdgeToEdgeHelper` for consistent padding.
- Removed manual inset handling from `Activity2` and `Fragment2` as `EdgeToEdgeHelper` now manages this.
- Enabled edge-to-edge display in `MainActivity`.
- Minor updates to `BillingClientConnection` for product details fetching and `build.gradle.kts` for configuration.
This commit updates:
- Android Gradle Plugin to 8.11.0
- Compile/Target SDK to 36
- Google Play Billing Library to 8.0.0
The `BillingClient` is updated to use the new `PendingPurchasesParams` and `QueryPurchasesParams` APIs.
This commit modifies the handling of Identity Resolving Key (IRK) and Encryption Key (EncKey) to allow users to clear these values by providing an empty input.
Specifically:
- In `GeneralSettingsFragment.kt`, the `onKey` callbacks for both IRK and EncKey dialogs now use `takeIf { it.isNotEmpty() }` after converting the input hex string to a byte array. This ensures that an empty input results in `null` being set for the respective key.
- In `PodMonitor.kt`, when determining the main device, `mainDeviceIdentityKey.value` is now checked with `takeIf { it.isNotEmpty() }` to ensure an empty IRK is treated as no IRK being configured.
- In `AppleFactory.kt`, when attempting to decrypt the private payload, `mainDeviceEncryptionKey.value` is now checked with `takeIf { it.isNotEmpty() }` to ensure an empty EncKey prevents decryption attempts.
This commit modifies `PodMonitor.kt` to ensure that the `isIRKMatch` flag is only used to determine the main device if an Identity Resolving Key (IRK) is actually configured in the general settings.
If no IRK is set, the main device determination will fall back to other logic, preventing a device from being incorrectly selected as "main" solely based on an IRK match when no specific IRK is being looked for.
- Prevent heuristic device detection when IRK/ENC keys are configured
- Maintain last IRK-matched device state instead of fallback
- Auto-enable LOW_POWER scan mode when IRK device is found
- Fixes false positives in crowded places
This commit updates the app title in the Google Play Store metadata for Tamil (ta-IN), Romanian (ro), and Swedish (sv-SE) by removing or adjusting a word to meet length constraints.
This commit corrects a typographical error in the Lao translation of the app title.
Specifically, it changes "ຄູ່ຮ່ວມສຳລັບ" to "ຄູ່ຮ່ວມສຳລັບ" in `fastlane/metadata/android/lo-LA/title.txt`.
This commit updates various Ruby gems in `Gemfile.lock`, most notably upgrading `fastlane` from version 2.213.0 to 2.228.0.
It also introduces new Fastlane lanes for Wear OS builds:
- `beta_wearos`
- `production_wearos`
The `fastlane/README.md` has been updated to reflect these new lanes.
This commit refactors the `crowdin.yaml` configuration file to:
- Use YAML anchors (`&stringmapping`, `*stringmapping`) to define and reuse the `languages_mapping` for different source files. This reduces redundancy and improves maintainability.
- Update language codes for Sardinian ("sc") and Norwegian ("no") to "sc-rIT" and "nb" respectively in the `stringmapping`.
- Update language codes for Sardinian ("sc") and Albanian ("sq") to "sc-IT" and "sq-AL" respectively in the `playstoremapping`.
This commit introduces new translations and updates existing ones for the CAPod application in Filipino (fil), Albanian (sq-rAL), Sardinian (sc-rIT), and Urdu (ur-rIN).
Specifically, it:
- Adds comprehensive translations for various UI elements, settings, permissions, and status messages in Albanian (`app/src/main/res/values-sq-rAL/strings.xml` and `app-common/src/main/res/values-sq-rAL/strings.xml`).
- Adds translations for Google Play related strings in Sardinian (`app/src/gplay/res/values-sc-rIT/strings.xml`).
- Adds translations for FOSS-specific upgrade options in Sardinian (`app/src/foss/res/values-sc-rIT/strings.xml`).
- Adds comprehensive translations for various UI elements, settings, permissions, and status messages in Sardinian (`app/src/main/res/values-sc-rIT/strings.xml`).
- Updates and expands translations in Filipino (`app-common/src/main/res/values-fil/strings.xml`), including terms for app names, general UI elements, permissions, device states, and settings.
- Adds comprehensive translations for various UI elements, settings, permissions, and status messages in Urdu (`app-common/src/main/res/values-ur-rIN/strings.xml`).
This commit updates the `crowdin.yaml` file to include language mappings for Sardinian (sc-IT) and Albanian (sq-AL).
Specifically, it adds:
- `sc-IT: sc-rIT` and `sq-AL: sq-rAL` to the `android_code` language mappings for various files.
- `sc-IT: sc-IT` and `sq-AL: sq-AL` to the `locale` language mappings under `playstoremapping`.
This commit corrects the escaping of quotation marks in several translated strings. Specifically, it replaces `\\\"` with `\"` in:
- Galician (gl-rES) translations for various permission descriptions.
- Telugu (te-rIN) translations for settings descriptions and troubleshooter text.
- Romansh (rm) translations for settings descriptions and troubleshooter text.
This commit introduces new translations for the app in Zulu (zu), Finnish (fi), Galician (gl-rES), Kyrgyz (ky-rKG), Romansh (rm), Telugu (te-rIN), and Uzbek (uz).
This commit updates the Android Gradle Plugin (AGP) from version 8.10.0 to 8.10.1 and the Kotlin Symbol Processing (KSP) plugin from version 2.1.20-2.0.0 to 2.1.21-2.0.1.
These changes are applied in the root `build.gradle.kts` file and the `buildSrc/build.gradle.kts` file.
This commit introduces support for the PowerBeats Pro 2 device.
Had to change the PowerBeats Pro 1 device code due to conflicting matches. Previously was using the half code variant. Might have broken that, but don't have any feedback from actual users, someone will have to speak up...
Closes#298
This commit adds default changelog files for various languages in the Fastlane metadata directory. Each file contains a generic message about bug fixes, performance improvements, and potential new features, along with a link to the full changelog and a note about the developer being a single person.
This commit refactors the Liquid templating in `CHANGELOG.md` to:
- Indent Liquid logic for better readability.
- Improve spacing around headings (##, ###) and list items (-) for a cleaner rendered output.
This commit updates the Jekyll configuration to include `jekyll-github-metadata` and `jemoji` plugins. It also introduces a `CHANGELOG.md` file that automatically generates a formatted changelog page from GitHub releases. The default changelog text for new app versions in Fastlane has also been updated to be more engaging and directly link to the new changelog page.
This commit corrects the `isIRKMatch` flag assignment in `AppleFactory.kt`. Previously, it was hardcoded to `true`. Now, it correctly uses the `isIrkMatch` variable determined by the Identity Resolving Key (IRK) comparison.
This commit introduces translations for a new notification channel "Device connected" and updates some existing string descriptions in various languages.
Specifically, it:
- Adds translations for `settings_monitor_connected_notification_label`, `settings_monitor_connected_notification_description`, and `notification_channel_device_status_connected_label` in Spanish (es), French (fr), Estonian (et-rEE), Norwegian (no), Czech (cs), Polish (pl), Catalan (ca), Greek (el), and German (de).
- Updates the full app descriptions in `fastlane/metadata/android/` for Catalan (ca), German (de-DE), Spanish (es-ES), French (fr-FR), Estonian (et), Norwegian (no-NO), Czech (cs-CZ), Polish (pl-PL), and Russian (ru-RU) to replace "phone" with "device" in the context of connecting to AirPods.
- Adds missing Arabic (ar) translations for various strings related to debug logging, settings, troubleshooting, and onboarding.
- Fixes a typo in the German translation for `settings_general_description`.
- Corrects Hebrew (iw) translations for Bluetooth permission descriptions.
This commit enhances the test coverage for `AirPodsProTest` by:
- Verifying the hex representation of the public payload data.
- Confirming that the private payload is null when no IRK/EncKey is set.
- Verifying the hex representation of the private payload data when IRK/EncKey are set.
Additionally, a fix is introduced in `AppleFactory.kt` to correctly parse the public payload by taking only the first 9 bytes instead of 16. This ensures that the private payload is only processed if the incoming message length matches the expected `PAIRING_MESSAGE_LENGTH`.
This commit modifies `SingleApplePods`, `AirPodsMaxUsbc`, and `AirPodsMax` to prioritize battery information from the decrypted private payload when available.
Specifically:
- `SingleApplePods.batteryHeadsetPercent`: Now checks `payload.private?.asBatteryState(1)?.level` first. If the private payload is not available or doesn't contain battery information at position 1, it falls back to the existing logic using `pubPodsBattery`.
- `AirPodsMaxUsbc.isHeadsetBeingCharged` and `AirPodsMax.isHeadsetBeingCharged`: Now check `payload.private?.asBatteryState(1)?.isCharging` first. If the private payload is not available or doesn't contain charging status at position 1, they fall back to the existing logic using `pubFlags.isBitSet(0)`.
This change allows for more accurate battery level and charging status reporting when the private payload can be decrypted.
This commit refines the logic for selecting the main Pod device. Previously, the `RPAChecker` was used directly to verify the Identity Resolving Key (IRK).
The new approach leverages the `isIRKMatch` flag within the `ApplePods` class. This flag is set during the device parsing process, indicating whether the device's address matches the configured IRK.
This change:
- Simplifies the `PodMonitor` by removing the direct dependency on `RPAChecker`.
- Encapsulates the IRK matching logic within the `ApplePods` class, improving modularity.
- Ensures that the main device selection correctly prioritizes devices whose IRK has been matched.
This commit introduces a new `Flags` data class within `ApplePods` to store boolean flags related to the device, specifically `isIRKMatch`.
This flag is now passed during the creation of `ApplePods` instances in all `ApplePodsFactory` implementations and the central `AppleFactory`.
The UI has been updated to display a key icon in the overview cards (`DualPodsCardVH` and `SinglePodsCardVH`):
- The icon is only visible if `flags.isIRKMatch` is true.
- The icon changes to a filled key (`ic_key_24`) if `payload.private` is not null (indicating successful decryption), otherwise an outline key (`ic_key_outline_24`) is shown.
- Minor layout adjustments were made in `overview_pods_single_item.xml` and `overview_pods_dual_item.xml` to accommodate the new key icon.
This commit introduces a new `BatteryState` data class and an extension function `ProximityPayload.Private.asBatteryState` to simplify the parsing of battery level and charging status from the proximity payload.
The `asBatteryState` function takes the position of the battery data in the payload and returns a `BatteryState` object, or null if the data is invalid.
This change improves code readability and maintainability by encapsulating the battery state parsing logic.
The previous logic for determining if the case was charging relied on the `batteryCasePercent` being non-null. This commit refines the logic to check if the `batteryCasePercent` falls within the valid range of 0.0 to 1.0, providing a more accurate assessment of the case's charging status.
This moves the history tracking logic for discovered Apple Pods from individual `ApplePodsFactory` implementations to a new `PodHistoryRepo` class.
Benefits:
- Centralized logic for device history management.
- Simplifies individual `ApplePodsFactory` implementations.
- Improves code maintainability and testability.
Relocate RPAChecker to the `pods.core.apple.protocol` package as it's specific to Apple's protocol.
Introduce type aliases `IdentityResolvingKey` and `ProximityEncryptionKey` for `ByteArray` to improve code clarity and type safety when dealing with these keys. Update relevant classes to use these new type aliases.
This commit introduces a typealias `BluetoothAddress` for `String` to improve code clarity and type safety when dealing with Bluetooth MAC addresses.
The following changes were made:
- Created `BluetoothAddress.kt` defining the typealias.
- Updated various classes to use `BluetoothAddress` instead of `String` for Bluetooth addresses:
- `RPAChecker`
- `PopUpReaction`
- `DeviceSelectionDialogFactory`
- `MonitorWorker`
- `GeneralSettings`
- `BluetoothDevice2`
This commit introduces a new helper function `fromHex()` that converts a HEX string (with optional separators like spaces or hyphens) into a byte array. It also adds a corresponding `toHex()` function for converting a byte array to a HEX string.
These helpers are now used in various parts of the codebase, including:
- Fake BLE data generation
- AirPod key input dialog
- RPA checker tests
- BaseAirPodsTest
Additionally, a unit test for these conversion functions has been added.
This commit adds a test case to `AirPodsProTest` to verify the decryption of AirPods Pro data with and without encryption/IRK keys. It also introduces a new test class `RPACheckerTest` to validate the RPA checking mechanism.
Helper functions for converting hex strings to byte arrays and setting mock keys have been added to `BaseAirPodsTest` to simplify test setup.
The encryption key setting for the main device is now disabled if the identity key is not set. This prevents users from trying to set an encryption key for a device that doesn't have an identity key.
This change introduces the capability to decrypt the last 16 bytes of the proximity pairing message if an encryption key is available.
The decrypted payload provides more precise battery information (0-100% instead of 0-10%) and charging status for each pod and the case.
This information is now used by `DualApplePods` to enhance battery reporting accuracy.
The `RPAChecker` has also been updated with improved error logging.
Replace `values()` with `entries` for enum iteration, as recommended by Kotlin for improved performance and consistency.
Also:
- Suppress unused warning in `ByteArrayAdapter`
- Improve logging in `WebpageTool` by including the exception details.
This commit introduces settings for users to input their AirPods' Identity Resolving Key (IRK) and Encryption Key. These keys, obtainable via a MacBook, allow CAPod to reliably identify the AirPods and decrypt detailed status information.
Key changes:
- Added new preferences in General Settings for IRK and Encryption Key.
- Implemented a custom dialog for key input, including validation and a link to a guide.
- Added a Moshi adapter for serializing/deserializing `ByteArray` to/from Base64 for storing the keys.
- Included new string resources for labels, descriptions, and explanations related to the keys.
- Added new icons for the key preferences.
```
Caused by java.lang.IllegalStateException: WorkManager is not initialized properly. You have explicitly disabled WorkManagerInitializer in your manifest, have not manually called WorkManager#initialize at this point, and your Application does not implement Configuration.Provider.
at androidx.work.impl.WorkManagerImpl.getInstance (WorkManagerImpl.java)
at androidx.work.impl.foreground.SystemForegroundDispatcher.<init> (SystemForegroundDispatcher.java)
at androidx.work.impl.foreground.SystemForegroundService.initializeDispatcher (SystemForegroundService.java)
at androidx.work.impl.foreground.SystemForegroundService.onCreate (SystemForegroundService.java)
at android.app.ActivityThread.handleCreateService (ActivityThread.java:4726)
```
```java
12:49:29.433 AndroidRuntime E FATAL EXCEPTION: main
Process: eu.darken.capod, PID: 15164
java.lang.SecurityException: Starting FGS with type connectedDevice callerApp=ProcessRecord{cb03698 15164:eu.darken.capod/u0a352} targetSDK=34 requires permissions: all of the permissions allOf=true [android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE] any of the permissions allOf=false [android.permission.BLUETOOTH_ADVERTISE, android.permission.BLUETOOTH_CONNECT, android.permission.BLUETOOTH_SCAN, android.permission.CHANGE_NETWORK_STATE, android.permission.CHANGE_WIFI_STATE, android.permission.CHANGE_WIFI_MULTICAST_STATE, android.permission.NFC, android.permission.TRANSMIT_IR, android.permission.UWB_RANGING, USB Device, USB Accessory]
at android.os.Parcel.createExceptionOrNull(Parcel.java:3242)
at android.os.Parcel.createException(Parcel.java:3226)
at android.os.Parcel.readException(Parcel.java:3209)
at android.os.Parcel.readException(Parcel.java:3151)
at android.app.IActivityManager$Stub$Proxy.setServiceForeground(IActivityManager.java:7167)
at android.app.Service.startForeground(Service.java:863)
at androidx.work.impl.foreground.SystemForegroundService$Api31Impl.startForeground(SystemForegroundService.java:194)
at androidx.work.impl.foreground.SystemForegroundService$1.run(SystemForegroundService.java:130)
at android.os.Handler.handleCallback(Handler.java:959)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loopOnce(Looper.java:232)
at android.os.Looper.loop(Looper.java:317)
at android.app.ActivityThread.main(ActivityThread.java:8699)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:886)
This commit modifies all major elements of the UI for phones to use Material3 elements, including the toolbar.
Signed-off-by: Ricky Cheung <rcheung844@gmail.com>
Bump workmanager `2.7.1` -> `2.8.1` to fix
```java
android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service eu.darken.capod/androidx.work.impl.foreground.SystemForegroundService
```
> Your app does not display the time of day clearly at the top of the app home screen and any ongoing activity screens. We recommend that you display the time of day at the top of all activities except dialog and confirmation screens. For more information, see Show the time.
* More IAP improvements
* More refactoring
* More tolerance towards GPlay API issues.
* Bump billing dependency
* Migrate away from deprecated methods.
* wip
* Setup bugsnag only in gplay builds.
* Bugsnag does not support uploading mapping files or build information from library modules. This should be done from the application module which produces your APK instead.
* Only apply Bugsnag plugin for the right flavors.
* Run unit tests in debug, otherwise we run issues requiring api keys in the env
* Improve compat options: Add alternative method for receiving BLE scan results via PendingIntents
* Add missing logtags
* Reduce log spam
* Make the linter happy
* Make pod features more granular
Don't assume all AirPods/Beats return a correct connection state.
* Refactoring
* Fix refactoring regression
* AirPods Gen1 don't support state detection
* Update airpods pro icons to SVG variant
* Fix AirPodsPro2 not using the new icons
* Revert "Update airpods pro icons to SVG variant"
This reverts commit d7ca13d439.
* Add padding to icons
* Preliminary support for AirPods Pro 2, based on #31, assuming identifier is `11 20`
* Fix AirPods Pro 2 identifier (it's 0x1420)
* Add second AirPods Pro 2 test case from reddit user.
* Update Readme with AirPods Pro 2
* I do not sell, monetize or otherwise misappropriate any collected data.
# Privacy policy
This is the privacy policy for the Android app "CAPod - Companion for AirPods" by Matthias Urhahn (darken).
Anonymous device information may be collected in the event of a crash (see [Automatic crash reports](#automatic-crash-reports)).
## Preamble
CAPod respects your privacy.
I do not collect, share or sell personal information.
Send a [quick mail](mailto:support@darken.eu) if you have questions.
My underlying privacy principle is the [Golden Rule](https://en.wikipedia.org/wiki/Golden_Rule).
Send a [quick mail](mailto:support@darken.eu) if you have further questions.
## Location data
## Automatic crashreports
CAPod does not collect, shareor sell location data.
The app uses "Bugsnag" for automatic crash reports:
Location permissions are required to receive Bluetooth Low Energy (BLE) data and ebale its core functionality.
The permission "access fine location" (`ACCESS_FINE_LOCATION`) and "access coarse location" (`ACCESS_COARSE_LOCATION`) are required to receive Bluetooth Low Energy data on Android 11 and lower.
On Android 12+ the newer and more fine grained `BLUETOOTH_SCAN` permission is used instead.
Bluetooth Low Energy is a technology that devices like AirPods use to communicate their status to nearby devices.
CAPod requests location permissions because these permissions are required to work with Bluetooth Low Energy data.
This is a privacy measure on Android's side because you could determine someones location by scanning for Bluetooth
devices:
If you know the physical location of a Bluetooth device (e.g. AirTags) you could use Bluetooth data to calculate your
position.
### Location access in the background
CAPod uses the "location access in the background" permission (`ACCESS_BACKGROUND_LOCATION`) on Android 11 and older to
receive Bluetooth Low Energy data while the app is in the background. This permission enables the "Show popup" and "
Autoconnect" features and allows CAPod to react to nearby devices whil the app is closed.
## Automatic error reports
*This was removed in v2.11.0+*
If an error occurs, an automated report may be sent to help me fix the issue.
This is optional and you can opt out of this in the settings.
Error reports are collected using "Bugsnag":
https://www.bugsnag.com/
Bugsnags privacy policy can be found here:
Bugsnag's privacy policy can be found here:
https://docs.bugsnag.com/legal/privacy-policy/
Crash reports may contain device and app related information.
Error reports contain device and app information related to the error that occured.
Additional information about the error context may also be included, e.g. what this app did shortly before the error.
You can disable automatic crash reports in the app's settings.
Additional details about the type of data that is collected by the error tracking SDK can be found here:
Error reports are pseudonymous. Unless you tell me your install-ID, I don't know that an error report came from you.
Error reports are automatically deleted after 90 days.
## Debug logs
The app has a debug log feature that can be used to assist troubleshooting efforts. This feature creates a log file that contains verbose output of what the app is doing.
It is manually triggered by the user through an option in the app settings. The recorded log file can be shared through compatible apps (e.g. your email app) using the system's share dialog. As this log file may contain sensitive information (e.g. details about files or installed applications) it should only be shared with trusted parties.
[](https://github.com/d4rken-org/capod/edit/main/README.md#download)
A companion app that adds support for AirPod specific features to Android:
@@ -12,43 +15,63 @@ A companion app that adds support for AirPod specific features to Android:
* Additional infos about connection, microphone & case.
* Can receive and show all nearby devices.
* Ear detection with automatic play/pause.
* Automatically connect phone & airpods.
* Automatically connect phone & AirPods.
* Show popup when case is opened.
* Support for Wear OS
* Widgets
CAPod is ad-free. Some additional features require an in-app purchase.
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.