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.