From 2bd0a3a20ce0f920213fea4da69deb705bcd7bd6 Mon Sep 17 00:00:00 2001 From: Kavish Devar Date: Sat, 3 May 2025 21:04:40 +0530 Subject: [PATCH] a few small changes --- .github/workflows/ci.yml | 2 +- AAP Definitions.md | 4 +- CHANGELOG.md | 4 +- CONTRIBUTING.md | 12 +- README.md | 136 +-- android/app/build.gradle.kts | 14 +- android/app/src/main/AndroidManifest.xml | 17 +- android/app/src/main/cpp/l2c_fcr_hook.cpp | 233 ++++- android/app/src/main/cpp/l2c_fcr_hook.h | 7 + .../aln/services/AirPodsQSService.kt | 142 ---- .../me/kavishdevar/aln/utils/KotlinModule.kt | 41 - .../CustomDeviceActivity.kt | 10 +- .../{aln => librepods}/MainActivity.kt | 72 +- .../librepods/QuickSettingsDialogActivity.kt | 621 ++++++++++++++ .../composables/AccessibilitySettings.kt | 10 +- .../composables/AdaptiveStrengthSlider.kt | 8 +- .../composables/AudioSettings.kt | 10 +- .../composables/BatteryIndicator.kt | 8 +- .../composables/BatteryView.kt | 18 +- .../composables/ControlCenterButton.kt | 104 +++ ...ontrolCenterNoiseControlSegmentedButton.kt | 242 ++++++ .../ConversationalAwarenessSwitch.kt | 8 +- .../composables/CustomDropdown.kt | 8 +- .../composables/IndependentToggle.kt | 8 +- .../composables/LoudSoundReductionSwitch.kt | 8 +- .../composables/NameField.kt | 6 +- .../composables/NavigationButton.kt | 6 +- .../composables/NoiseControlButton.kt | 8 +- .../composables/NoiseControlSettings.kt | 50 +- .../composables/PersonalizedVolumeSwitch.kt | 8 +- .../composables/PressAndHoldSettings.kt | 8 +- .../composables/SinglePodANCSwitch.kt | 8 +- .../composables/StyledSwitch.kt | 6 +- .../composables/ToneVolumeSlider.kt | 10 +- .../composables/TransparencySettings.kt | 6 +- .../composables/VerticalVolumeSlider.kt | 190 +++++ .../composables/VolumeControlSwitch.kt | 8 +- .../receivers/BootReceiver.kt | 8 +- .../screens/AirPodsSettingsScreen.kt | 40 +- .../screens/AppSettingsScreen.kt | 18 +- .../{aln => librepods}/screens/DebugScreen.kt | 16 +- .../screens/HeadTrackingScreen.kt | 14 +- .../{aln => librepods}/screens/Onboarding.kt | 90 +- .../screens/PressAndHoldSettingsScreen.kt | 10 +- .../screens/RenameScreen.kt | 10 +- .../librepods/services/AirPodsQSService.kt | 263 ++++++ .../services/AirPodsService.kt | 75 +- .../{aln => librepods}/ui/theme/Color.kt | 6 +- .../{aln => librepods}/ui/theme/Theme.kt | 18 +- .../{aln => librepods}/ui/theme/Type.kt | 6 +- .../{aln => librepods}/utils/CrossDevice.kt | 16 +- .../utils/GestureDetector.kt | 6 +- .../utils/GestureFeedback.kt | 4 +- .../utils/HeadOrientation.kt | 2 +- .../{aln => librepods}/utils/IslandWindow.kt | 12 +- .../librepods/utils/KotlinModule.kt | 793 ++++++++++++++++++ .../utils/MediaController.kt | 8 +- .../{aln => librepods}/utils/Packets.kt | 24 +- .../{aln => librepods}/utils/PopupWindow.kt | 156 ++-- .../utils/RadareOffsetFinder.kt | 173 +++- .../librepods/utils/SystemAPIUtils.kt | 285 +++++++ .../widgets/BatteryWidget.kt | 12 +- .../widgets/NoiseControlWidget.kt | 10 +- .../drawable-v24/ic_launcher_background.xml | 10 + .../drawable-v24/ic_launcher_foreground.xml | 57 +- .../drawable-v24/ic_launcher_monochrome.xml | 22 + .../res/drawable/ic_launcher_background.xml | 170 ---- .../src/main/res/layout/battery_widget.xml | 4 +- .../main/res/layout/noise_control_widget.xml | 4 +- .../res/mipmap-anydpi-v26/ic_launcher.xml | 6 +- .../mipmap-anydpi-v26/ic_launcher_round.xml | 4 +- .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 1404 -> 0 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 2898 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 982 -> 0 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 1772 -> 0 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 1900 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 3918 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 2884 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 5914 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 3844 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 7778 -> 0 bytes .../src/main/res/values-night-v31/themes.xml | 2 +- .../app/src/main/res/values-night/colors.xml | 3 +- .../app/src/main/res/values-v21/styles.xml | 4 +- .../app/src/main/res/values-v31/styles.xml | 4 +- .../app/src/main/res/values-v31/themes.xml | 2 +- android/app/src/main/res/values/colors.xml | 21 +- android/app/src/main/res/values/strings.xml | 12 +- android/app/src/main/res/values/styles.xml | 4 +- android/app/src/main/res/values/themes.xml | 32 +- .../resources/META-INF/xposed/java_init.list | 2 +- .../main/resources/META-INF/xposed/scope.list | 6 +- android/gradle.properties | 4 +- android/gradle/libs.versions.toml | 35 +- android/settings.gradle.kts | 2 +- imgs/banner.png | Bin 0 -> 272455 bytes root-module/customize.sh | 2 +- root-module/module.prop | 2 +- update.json | 4 +- 99 files changed, 3668 insertions(+), 886 deletions(-) delete mode 100644 android/app/src/main/java/me/kavishdevar/aln/services/AirPodsQSService.kt delete mode 100644 android/app/src/main/java/me/kavishdevar/aln/utils/KotlinModule.kt rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/CustomDeviceActivity.kt (97%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/MainActivity.kt (89%) create mode 100644 android/app/src/main/java/me/kavishdevar/librepods/QuickSettingsDialogActivity.kt rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/AccessibilitySettings.kt (96%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/AdaptiveStrengthSlider.kt (95%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/AudioSettings.kt (93%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/BatteryIndicator.kt (95%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/BatteryView.kt (93%) create mode 100644 android/app/src/main/java/me/kavishdevar/librepods/composables/ControlCenterButton.kt create mode 100644 android/app/src/main/java/me/kavishdevar/librepods/composables/ControlCenterNoiseControlSegmentedButton.kt rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/ConversationalAwarenessSwitch.kt (94%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/CustomDropdown.kt (96%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/IndependentToggle.kt (95%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/LoudSoundReductionSwitch.kt (94%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/NameField.kt (96%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/NavigationButton.kt (95%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/NoiseControlButton.kt (91%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/NoiseControlSettings.kt (91%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/PersonalizedVolumeSwitch.kt (94%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/PressAndHoldSettings.kt (97%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/SinglePodANCSwitch.kt (94%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/StyledSwitch.kt (93%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/ToneVolumeSlider.kt (95%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/TransparencySettings.kt (98%) create mode 100644 android/app/src/main/java/me/kavishdevar/librepods/composables/VerticalVolumeSlider.kt rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/composables/VolumeControlSwitch.kt (94%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/receivers/BootReceiver.kt (85%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/screens/AirPodsSettingsScreen.kt (92%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/screens/AppSettingsScreen.kt (97%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/screens/DebugScreen.kt (98%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/screens/HeadTrackingScreen.kt (98%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/screens/Onboarding.kt (88%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/screens/PressAndHoldSettingsScreen.kt (98%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/screens/RenameScreen.kt (96%) create mode 100644 android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsQSService.kt rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/services/AirPodsService.kt (97%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/ui/theme/Color.kt (82%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/ui/theme/Theme.kt (78%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/ui/theme/Type.kt (89%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/utils/CrossDevice.kt (96%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/utils/GestureDetector.kt (98%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/utils/GestureFeedback.kt (99%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/utils/HeadOrientation.kt (98%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/utils/IslandWindow.kt (95%) create mode 100644 android/app/src/main/java/me/kavishdevar/librepods/utils/KotlinModule.kt rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/utils/MediaController.kt (97%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/utils/Packets.kt (94%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/utils/PopupWindow.kt (51%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/utils/RadareOffsetFinder.kt (70%) create mode 100644 android/app/src/main/java/me/kavishdevar/librepods/utils/SystemAPIUtils.kt rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/widgets/BatteryWidget.kt (79%) rename android/app/src/main/java/me/kavishdevar/{aln => librepods}/widgets/NoiseControlWidget.kt (92%) create mode 100644 android/app/src/main/res/drawable-v24/ic_launcher_background.xml create mode 100644 android/app/src/main/res/drawable-v24/ic_launcher_monochrome.xml delete mode 100644 android/app/src/main/res/drawable/ic_launcher_background.xml delete mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher.webp delete mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp delete mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher.webp delete mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp delete mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp delete mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp delete mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp delete mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp delete mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp delete mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 imgs/banner.png diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f3b3d4..b5d6b55 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 - name: Export APK_NAME for later use - run: echo "APK_NAME=ALN-$(echo ${{ github.sha }} | cut -c1-7).apk" >> $GITHUB_ENV + run: echo "APK_NAME=LibrePods-$(echo ${{ github.sha }} | cut -c1-7).apk" >> $GITHUB_ENV - name: Rename .apk file run: mv "./Debug APK/debug/"*.apk "./$APK_NAME" - name: Decode keystore file diff --git a/AAP Definitions.md b/AAP Definitions.md index c5baa19..802a21e 100644 --- a/AAP Definitions.md +++ b/AAP Definitions.md @@ -429,8 +429,8 @@ Once tracking is active, the AirPods stream sensor packets with the following co # LICENSE -AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! -Copyright (C) 2024 Kavish Devar +LibrePods - AirPods liberated from Apple’s ecosystem +Copyright (C) 2025 LibrePods contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fa26ba..ca8385c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ ## btl2capfix v0.0.3 -- ([#34](https://github.com/kavishdevar/aln/pull/34)) @devnoname120 Add on-device libbluetooth patcher using a Magisk/KernelSU module (arm64-only) +- ([#34](https://github.com/kavishdevar/librepods/pull/34)) @devnoname120 Add on-device libbluetooth patcher using a Magisk/KernelSU module (arm64-only) -_[See more here](https://github.com/kavishdevar/aln/releases)_ +_[See more here](https://github.com/kavishdevar/librepods/releases)_ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0584667..91629e3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ -# Welcome to AirPods Like Normal contributing guide +# Welcome to LibrePods contributing guide -Thank you for considering a contribution to AirPods Like Normal! Your support helps bring Apple-exclusive AirPods features to Linux and Android. +Thank you for considering a contribution to LibrePods! Your support helps bring Apple-exclusive AirPods features to Linux and Android. Read our [Code of Conduct](./CODE_OF_CONDUCT.md) to keep our community approachable and respectful. @@ -25,11 +25,11 @@ To develop for the Android App, Android Studio is the preferred IDE. And you can #### Create a new issue -If you find a bug or want to suggest a feature, check if an issue already exists by searching through our [existing issues](https://github.com/kavishdevar/aln/issues). If no relevant issue exists, open a new one and fill in the details. +If you find a bug or want to suggest a feature, check if an issue already exists by searching through our [existing issues](https://github.com/kavishdevar/librepods/issues). If no relevant issue exists, open a new one and fill in the details. #### Solve an issue -Browse our [issues list](https://github.com/kavishdevar/aln/issues) to find an interesting issue to work on. Use labels to filter issues and pick one that matches your expertise. If you’d like to work on an issue, open a PR with your solution. +Browse our [issues list](https://github.com/kavishdevar/librepods/issues) to find an interesting issue to work on. Use labels to filter issues and pick one that matches your expertise. If you’d like to work on an issue, open a PR with your solution. ### Make Changes @@ -37,7 +37,7 @@ Browse our [issues list](https://github.com/kavishdevar/aln/issues) to find an i 1. Fork the repository and clone it to your local environment. ``` -git clone https://github.com/kavishdevar/aln.git +git clone https://github.com/kavishdevar/librepods.git cd AirPods-Like-Normal ``` 2. Create a working branch to start your changes. @@ -67,4 +67,4 @@ Once your PR is open, a team member will review it. They may ask questions or re ### Your PR is merged! -Congratulations! :tada: Once merged, your contributions will be publicly available in AirPodsLikeNormal. \ No newline at end of file +Congratulations! :tada: Once merged, your contributions will be publicly available in LibrePods. \ No newline at end of file diff --git a/README.md b/README.md index 7777c61..9f65e86 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,58 @@ -# ALN - AirPodsLikeNormal -*Bringing AirPods' Apple-exclusive features on linux and android!* +# LibrePods -## [XDAForums Thread](https://xdaforums.com/t/app-root-for-now-airpodslikenormal-unlock-apple-exclusive-airpods-features-on-android.4707585/) +![LibrePods Banner](/imgs/banner.png) -## Tested device(s) -- AirPods Pro 2 +*AirPods liberated from Apple's ecosystem* -Other devices might work too. Features like ear detection and battery should be available for any AirPods! Although the app will show unsupported features/settings. I will not be able test any other devices than the ones I already have (i.e. the AirPods Pro 2). +[![XDA Thread](https://img.shields.io/badge/XDA_Forums-Thread-orange)](https://xdaforums.com/t/app-root-for-now-airpodslikenormal-unlock-apple-exclusive-airpods-features-on-android.4707585/) -## Features +## What is LibrePods? -Check the [pinned issue](https://github.com/kavishdevar/aln/issues/20) for a list. +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. +## Device Compatibility -## CrossDevice Stuff +| Status | Device | Features | +|--------|--------|----------| +| ✅ | AirPods Pro (2nd Gen) | Fully supported and tested | +| ⚠️ | Other AirPods models | Basic features (battery status, ear detection) should work | -> [!IMPORTANT] -> This feature is still in development and might not work as expected. No support is provided for this feature. +Most features should work with any AirPods. Currently, testing is only performed with AirPods Pro 2. -### Features +## Key Features -- **Battery Status**: Get battery status on any device when you connect your AirPods to one of them. -- **Control AirPods**: Control your AirPods from either of your device when you connect to one, like changing the noise control mode, toggling conversational awareness, and more. -- **Automatic Device Switching**: Automatically switch between your Linux and Android device, like when you receive a call, start playing music on Android while you're connected to Linux, and more! +- **Noise Control Modes**: Easily switch between noise control modes without having to reach out to your AirPods to long press +- **Ear Detection**: Controls your music automatically when you put your AirPods in or take them out, and switch to phone speaker when you take them out +- **Battery Status**: Accurate battery levels +- **Head Gestures**: Answer calls just by nodding your head +- **Conversational Awareness**: Volume automatically lowers when you speak +- **Other customizations**: + - Rename your AirPods + - Customize long-press actions + - Few accessibility features + - And more! -Check out the demo below! +See our [pinned issue](https://github.com/kavishdevar/librepods/issues/20) for a complete feature list and roadmap. -https://github.com/user-attachments/assets/d08f8a51-cd52-458b-8e55-9b44f4d5f3ab +## Platform Support +### Linux -## Linux +The Linux version runs as a system tray app. Connect your AirPods and enjoy: -The Linux version is a simple tray-app, with a modern and adaptive ui. Still WIP, but most things work (battery, ear-detection, auto-pause, rename, etc.) +- Battery monitoring +- Ear detection with auto-pause +- Device renaming +- Media controls -Check out the README file in [linux](/linux) folder for more info. +> [!NOTE] +> Work in progress, but core functionality is stable and usable. -## Android +For installation and detailed info, see the [Linux README](/linux/README.md). -> Can I use aln without root? +### Android -**No, it's not possible to use aln without root.** You will have to root your device if you want to use aln, and there is no way around it. **No exceptions.** - -### Screenshots +#### Screenshots | | | | |-------------------|-------------------|-------------------| @@ -50,46 +61,75 @@ Check out the README file in [linux](/linux) folder for more info. | ![Long Press Configuration](/android/imgs/long-press.png) | ![Widget](/android/imgs/widget.png) | ![Customizations](/android/imgs/customizations.png) | | ![audio-popup](/android/imgs/audio-connected-island.png) | | | -### Installation - -Currently, there's a [bug in the Android Bluetooth stack](https://issuetracker.google.com/issues/371713238) that prevents the app from working (upvote the issue - click the '+1' icon on the top right corner of IssueTracker). +#### Root Requirement > [!CAUTION] -> Until Google merges the fix **you will only be able to use aln if you are rooted**. There are **no exceptions**, don't ask about it. +> **You must have a rooted device to use LibrePods on Android.** This is due to a [bug in the Android Bluetooth stack](https://issuetracker.google.com/issues/371713238). Please upvote the issue by clicking the '+1' icon on the IssueTracker page. +> +> There are **no exceptions** to the root requirement until Google merges the fix. -In order to use aln you will have to install the module using your favorite root manager in OverlayFS mode (KernelSU, Apatch, or Magisk). If you don't know what this means, no support is provided: you will have to search by yourself on Google or ask in some Android rooting communities on Telegram. +#### Installation Methods -The module to install is available in the releases section under the name `btl2capfix.zip`. +##### Method 1: Xposed Module (Recommended) +This method is less intrusive and should be tried first: -### Android – features +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 -#### Renaming the Airpods -When you rename the Airpods using the app, you'll need to re-pair it with your phone. Currently, user-level apps cannot directly rename a Bluetooth device. After re-pairing, your phone will display the updated name! +##### Method 2: Root Module (Backup Option) +If the Xposed method doesn't work for you: -#### Noise Control Modes +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 -- Active Noise Cancellation (ANC): Blocks external sounds using microphones and advanced algorithms for an immersive audio experience; ideal for noisy environments. -- Transparency Mode: Allows external sounds to blend with audio for situational awareness; best for environments where you need to stay alert. -- Off Mode: Disables noise control for a natural listening experience, conserving battery in quiet settings. -- Adaptive Transparency: Dynamically reduces sudden loud noises while maintaining environmental awareness, adjusting seamlessly to fluctuating noise levels. +##### Method 3: Patching it yourself +If you prefer to patch the Bluetooth stack yourself, follow these steps: + +1. Look for the library in use by running `lsof | grep libbluetooth` +2. Find the library path (e.g., `/system/lib64/libbluetooth_jni.so`) +3. Find the `l2c_fcr_chk_chan_modes` function in the library +4. Patch the function to always return `1` (true) +5. Repack the library and push it back to the device. You can do this by creating a root module yourself. +6. Reboot your device + +If you're unfamiliar with these steps, search for tutorials online or ask in Android rooting communities. + +#### A few notes + +- Due to recent AirPods' firmware upgrades, you must enable `Off listening mode` to switch to `Off`. This is because in this mode, louds sounds are not reduced! + +- If you have take both AirPods out, the app will automatically switch to the phone speaker. But, Android might keep on trying to connect to the AirPods because the phone is still connected to them, just the A2DP profile is not connected. The app tries to disconnect the A2DP profile as soon as it detects that Android has connected again if they're not in the ear. + +- 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] -> Due to recent AirPods' firmware upgrades, you must enable `Off listening mode` to switch to `Off`. This is because in this mode, louds sounds are not reduced! +> This feature is still in early development and might not work as expected. No support is provided for this feature yet. -#### Conversational Awareness +### Features in Development -Automatically lowers audio volume and enhances voices when you start speaking, making it easier to engage in conversations without removing your AirPods. +- **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 -#### Automatic Ear Detection +Check out the demo below: -Recognizes when the AirPods are in your ears to automatically play or pause audio and adjust functionality accordingly. - -## Check out the packet definitions at [AAP Definitions](/AAP%20Definitions.md) +https://github.com/user-attachments/assets/d08f8a51-cd52-458b-8e55-9b44f4d5f3ab # License -AirPodsLikeNormal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! -Copyright (C) 2024 Kavish Devar +LibrePods - AirPods liberated from Apple’s ecosystem +Copyright (C) 2025 LibrePods contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index e97992e..05f1c78 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -6,21 +6,15 @@ plugins { } android { - namespace = "me.kavishdevar.aln" + namespace = "me.kavishdevar.librepods" compileSdk = 35 defaultConfig { - applicationId = "me.kavishdevar.aln" + applicationId = "me.kavishdevar.librepods" minSdk = 28 targetSdk = 35 - versionCode = 3 - versionName = "0.0.3" - - externalNativeBuild { - cmake { - cppFlags += "" - } - } + versionCode = 4 + versionName = "0.1.0" } buildTypes { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index d5c7988..4c1f11b 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -5,6 +5,8 @@ + @@ -37,7 +39,7 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/Theme.ALN" + android:theme="@style/Theme.LibrePods" tools:ignore="UnusedAttribute" tools:targetApi="31"> + android:theme="@style/Theme.LibrePods"> @@ -75,7 +77,7 @@ + android:theme="@style/Theme.LibrePods"> @@ -83,6 +85,15 @@ + + (p_ccb); - LOGI("Original FCR mode: 0x%02x", ccb->our_cfg.fcr.mode); ccb->our_cfg.fcr.mode = 0; - ccb->our_cfg.fcr_present = true; - ccb->peer_cfg.fcr.mode = 0; ccb->peer_cfg.fcr_present = true; @@ -141,14 +144,38 @@ uint8_t fake_l2c_fcr_chk_chan_modes(void* p_ccb) { return 1; } +void fake_l2cu_process_our_cfg_req(tL2C_CCB* p_ccb, tL2CAP_CFG_INFO* p_cfg) { + original_l2cu_process_our_cfg_req(p_ccb, p_cfg); + p_ccb->our_cfg.fcr.mode = 0x00; + LOGI("Set FCR mode to Basic Mode in outgoing config request"); +} + +void fake_l2c_csm_config(tL2C_CCB* p_ccb, uint8_t event, void* p_data) { + // Call the original function first to handle the specific code path where the FCR mode is checked + original_l2c_csm_config(p_ccb, event, p_data); + + // Check if this happens during CONFIG_RSP event handling + if (event == L2CEVT_L2CAP_CONFIG_RSP) { + p_ccb->our_cfg.fcr.mode = p_ccb->peer_cfg.fcr.mode; + LOGI("Forced compatibility in l2c_csm_config: set our_mode=%d to match peer_mode=%d", + p_ccb->our_cfg.fcr.mode, p_ccb->peer_cfg.fcr.mode); + } +} + +// Replacement function that does nothing +void fake_l2cu_send_peer_info_req(tL2C_LCB* p_lcb, uint16_t info_type) { + LOGI("Intercepted l2cu_send_peer_info_req for info_type 0x%04x - doing nothing", info_type); + // Just return without doing anything + return; +} + uintptr_t loadHookOffset([[maybe_unused]] const char* package_name) { - const char* property_name = "persist.aln.hook_offset"; + const char* property_name = "persist.librepods.hook_offset"; char value[PROP_VALUE_MAX] = {0}; int len = __system_property_get(property_name, value); if (len > 0) { LOGI("Read hook offset from property: %s", value); - uintptr_t offset; char* endptr = nullptr; @@ -172,6 +199,96 @@ uintptr_t loadHookOffset([[maybe_unused]] const char* package_name) { return 0x00a55e30; } +uintptr_t loadL2cuProcessCfgReqOffset() { + const char* property_name = "persist.librepods.cfg_req_offset"; + char value[PROP_VALUE_MAX] = {0}; + + int len = __system_property_get(property_name, value); + if (len > 0) { + LOGI("Read l2cu_process_our_cfg_req offset from property: %s", value); + uintptr_t offset; + char* endptr = nullptr; + + const char* parse_start = value; + if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) { + parse_start = value + 2; + } + + errno = 0; + offset = strtoul(parse_start, &endptr, 16); + + if (errno == 0 && endptr != parse_start && *endptr == '\0' && offset > 0) { + LOGI("Parsed l2cu_process_our_cfg_req offset: 0x%x", offset); + return offset; + } + + LOGE("Failed to parse l2cu_process_our_cfg_req offset from property value: %s", value); + } + + // Return 0 if not found - we'll skip this hook + return 0; +} + +uintptr_t loadL2cCsmConfigOffset() { + const char* property_name = "persist.librepods.csm_config_offset"; + char value[PROP_VALUE_MAX] = {0}; + + int len = __system_property_get(property_name, value); + if (len > 0) { + LOGI("Read l2c_csm_config offset from property: %s", value); + uintptr_t offset; + char* endptr = nullptr; + + const char* parse_start = value; + if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) { + parse_start = value + 2; + } + + errno = 0; + offset = strtoul(parse_start, &endptr, 16); + + if (errno == 0 && endptr != parse_start && *endptr == '\0' && offset > 0) { + LOGI("Parsed l2c_csm_config offset: 0x%x", offset); + return offset; + } + + LOGE("Failed to parse l2c_csm_config offset from property value: %s", value); + } + + // Return 0 if not found - we'll skip this hook + return 0; +} + +uintptr_t loadL2cuSendPeerInfoReqOffset() { + const char* property_name = "persist.librepods.peer_info_req_offset"; + char value[PROP_VALUE_MAX] = {0}; + + int len = __system_property_get(property_name, value); + if (len > 0) { + LOGI("Read l2cu_send_peer_info_req offset from property: %s", value); + uintptr_t offset; + char* endptr = nullptr; + + const char* parse_start = value; + if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) { + parse_start = value + 2; + } + + errno = 0; + offset = strtoul(parse_start, &endptr, 16); + + if (errno == 0 && endptr != parse_start && *endptr == '\0' && offset > 0) { + LOGI("Parsed l2cu_send_peer_info_req offset: 0x%x", offset); + return offset; + } + + LOGE("Failed to parse l2cu_send_peer_info_req offset from property value: %s", value); + } + + // Return 0 if not found - we'll skip this hook + return 0; +} + uintptr_t getModuleBase(const char *module_name) { FILE *fp; char line[1024]; @@ -211,20 +328,84 @@ bool findAndHookFunction([[maybe_unused]] const char *library_path) { return false; } - uintptr_t offset = loadHookOffset(nullptr); + // Load all offsets from system properties - no hardcoding + uintptr_t l2c_fcr_offset = loadHookOffset(nullptr); + uintptr_t l2cu_process_our_cfg_req_offset = loadL2cuProcessCfgReqOffset(); + uintptr_t l2c_csm_config_offset = loadL2cCsmConfigOffset(); + uintptr_t l2cu_send_peer_info_req_offset = loadL2cuSendPeerInfoReqOffset(); - void* target = reinterpret_cast(base_addr + offset); - LOGI("Using offset: 0x%x, base: %p, target: %p", offset, (void*)base_addr, target); + bool success = false; - int result = hook_func(target, (void*)fake_l2c_fcr_chk_chan_modes, (void**)&original_l2c_fcr_chk_chan_modes); + // Hook l2c_fcr_chk_chan_modes - this is our primary hook + if (l2c_fcr_offset > 0) { + void* target = reinterpret_cast(base_addr + l2c_fcr_offset); + LOGI("Hooking l2c_fcr_chk_chan_modes at offset: 0x%x, base: %p, target: %p", + l2c_fcr_offset, (void*)base_addr, target); - if (result == 0) { + int result = hook_func(target, (void*)fake_l2c_fcr_chk_chan_modes, (void**)&original_l2c_fcr_chk_chan_modes); + if (result != 0) { + LOGE("Failed to hook l2c_fcr_chk_chan_modes, error: %d", result); + return false; + } LOGI("Successfully hooked l2c_fcr_chk_chan_modes"); - return true; + success = true; } else { - LOGE("Failed to hook function, error: %d", result); + LOGE("No valid offset for l2c_fcr_chk_chan_modes found, cannot proceed"); return false; } + + // Hook l2cu_process_our_cfg_req if offset is available + if (l2cu_process_our_cfg_req_offset > 0) { + void* target = reinterpret_cast(base_addr + l2cu_process_our_cfg_req_offset); + LOGI("Hooking l2cu_process_our_cfg_req at offset: 0x%x, base: %p, target: %p", + l2cu_process_our_cfg_req_offset, (void*)base_addr, target); + + int result = hook_func(target, (void*)fake_l2cu_process_our_cfg_req, (void**)&original_l2cu_process_our_cfg_req); + if (result != 0) { + LOGE("Failed to hook l2cu_process_our_cfg_req, error: %d", result); + // Continue even if this hook fails + } else { + LOGI("Successfully hooked l2cu_process_our_cfg_req"); + } + } else { + LOGI("Skipping l2cu_process_our_cfg_req hook as offset is not available"); + } + + // Hook l2c_csm_config if offset is available + if (l2c_csm_config_offset > 0) { + void* target = reinterpret_cast(base_addr + l2c_csm_config_offset); + LOGI("Hooking l2c_csm_config at offset: 0x%x, base: %p, target: %p", + l2c_csm_config_offset, (void*)base_addr, target); + + int result = hook_func(target, (void*)fake_l2c_csm_config, (void**)&original_l2c_csm_config); + if (result != 0) { + LOGE("Failed to hook l2c_csm_config, error: %d", result); + // Continue even if this hook fails + } else { + LOGI("Successfully hooked l2c_csm_config"); + } + } else { + LOGI("Skipping l2c_csm_config hook as offset is not available"); + } + + // Hook l2cu_send_peer_info_req if offset is available + if (l2cu_send_peer_info_req_offset > 0) { + void* target = reinterpret_cast(base_addr + l2cu_send_peer_info_req_offset); + LOGI("Hooking l2cu_send_peer_info_req at offset: 0x%x, base: %p, target: %p", + l2cu_send_peer_info_req_offset, (void*)base_addr, target); + + int result = hook_func(target, (void*)fake_l2cu_send_peer_info_req, (void**)&original_l2cu_send_peer_info_req); + if (result != 0) { + LOGE("Failed to hook l2cu_send_peer_info_req, error: %d", result); + // Continue even if this hook fails + } else { + LOGI("Successfully hooked l2cu_send_peer_info_req"); + } + } else { + LOGI("Skipping l2cu_send_peer_info_req hook as offset is not available"); + } + + return success; } void on_library_loaded(const char *name, [[maybe_unused]] void *handle) { diff --git a/android/app/src/main/cpp/l2c_fcr_hook.h b/android/app/src/main/cpp/l2c_fcr_hook.h index b7bfac4..cff43d4 100644 --- a/android/app/src/main/cpp/l2c_fcr_hook.h +++ b/android/app/src/main/cpp/l2c_fcr_hook.h @@ -17,5 +17,12 @@ typedef struct { [[maybe_unused]] typedef NativeOnModuleLoaded (*NativeInit)(const NativeAPIEntries *entries); +typedef struct t_l2c_ccb tL2C_CCB; +typedef struct t_l2c_lcb tL2C_LCB; + uintptr_t loadHookOffset(const char* package_name); uintptr_t getModuleBase(const char *module_name); +uintptr_t loadL2cuProcessCfgReqOffset(); +uintptr_t loadL2cCsmConfigOffset(); +uintptr_t loadL2cuSendPeerInfoReqOffset(); +bool findAndHookFunction(const char *library_path); diff --git a/android/app/src/main/java/me/kavishdevar/aln/services/AirPodsQSService.kt b/android/app/src/main/java/me/kavishdevar/aln/services/AirPodsQSService.kt deleted file mode 100644 index b4f6148..0000000 --- a/android/app/src/main/java/me/kavishdevar/aln/services/AirPodsQSService.kt +++ /dev/null @@ -1,142 +0,0 @@ -/* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! - * - * Copyright (C) 2024 Kavish Devar - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package me.kavishdevar.aln.services - -import android.annotation.SuppressLint -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.os.Build -import android.service.quicksettings.Tile -import android.service.quicksettings.TileService -import android.util.Log -import me.kavishdevar.aln.utils.AirPodsNotifications -import me.kavishdevar.aln.utils.NoiseControlMode - -class AirPodsQSService: TileService() { - private val ancModes = listOf(NoiseControlMode.NOISE_CANCELLATION.name, NoiseControlMode.TRANSPARENCY.name, NoiseControlMode.ADAPTIVE.name) - private var currentModeIndex = 2 - private lateinit var ancStatusReceiver: BroadcastReceiver - private lateinit var availabilityReceiver: BroadcastReceiver - - @SuppressLint("InlinedApi", "UnspecifiedRegisterReceiverFlag") - override fun onStartListening() { - super.onStartListening() - currentModeIndex = (ServiceManager.getService()?.getANC()?.minus(1)) ?: -1 - if (currentModeIndex == -1) { - currentModeIndex = 2 - } - - if (ServiceManager.getService() == null) { - qsTile.state = Tile.STATE_UNAVAILABLE - qsTile.updateTile() - } - if (ServiceManager.getService()?.isConnectedLocally == true) { - qsTile.state = Tile.STATE_ACTIVE - qsTile.updateTile() - } - else { - qsTile.state = Tile.STATE_UNAVAILABLE - qsTile.updateTile() - } - - ancStatusReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - val ancStatus = intent.getIntExtra("data", 4) - currentModeIndex = if (ancStatus == 2) 0 else if (ancStatus == 3) 1 else if (ancStatus == 4) 2 else 2 - updateTile() - } - } - - availabilityReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - if (intent.action == AirPodsNotifications.Companion.AIRPODS_CONNECTED) { - qsTile.state = Tile.STATE_ACTIVE - qsTile.updateTile() - } - else if (intent.action == AirPodsNotifications.Companion.AIRPODS_DISCONNECTED) { - qsTile.state = Tile.STATE_UNAVAILABLE - qsTile.updateTile() - } - } - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - registerReceiver( - ancStatusReceiver, - IntentFilter(AirPodsNotifications.Companion.ANC_DATA), RECEIVER_EXPORTED - ) - } else { - registerReceiver( - ancStatusReceiver, - IntentFilter(AirPodsNotifications.Companion.ANC_DATA) - ) - } - qsTile.state = if (ServiceManager.getService()?.isConnectedLocally == true) Tile.STATE_ACTIVE else Tile.STATE_UNAVAILABLE - val ancIndex = ServiceManager.getService()?.getANC() - currentModeIndex = if (ancIndex != null) { if (ancIndex == 2) 0 else if (ancIndex == 3) 1 else if (ancIndex == 4) 2 else 2 } else 0 - updateTile() - } - - override fun onStopListening() { - super.onStopListening() - try { - unregisterReceiver(ancStatusReceiver) - } - catch ( - _: IllegalArgumentException - ) - { - Log.e("QuickSettingTileService", "Receiver not registered") - } - try { - unregisterReceiver(availabilityReceiver) - } - catch ( - _: IllegalArgumentException - ) - { - Log.e("QuickSettingTileService", "Receiver not registered") - } - } - - override fun onClick() { - super.onClick() - Log.d("QuickSettingTileService", "ANC tile clicked") - currentModeIndex = (currentModeIndex + 1) % ancModes.size - Log.d("QuickSettingTileService", "New mode index: $currentModeIndex, would be set to ${currentModeIndex + 1}") - switchAncMode() - } - - private fun updateTile() { - val currentMode = ancModes[currentModeIndex % ancModes.size] - qsTile.label = currentMode.replace("_", " ").lowercase().replaceFirstChar { it.uppercase() } - qsTile.state = Tile.STATE_ACTIVE - qsTile.updateTile() - } - - private fun switchAncMode() { - val airPodsService = ServiceManager.getService() - Log.d("QuickSettingTileService", "Setting ANC mode to ${currentModeIndex + 2}") - airPodsService?.setANCMode(currentModeIndex + 2) - Log.d("QuickSettingTileService", "ANC mode set to ${currentModeIndex + 2}") - updateTile() - } -} \ No newline at end of file diff --git a/android/app/src/main/java/me/kavishdevar/aln/utils/KotlinModule.kt b/android/app/src/main/java/me/kavishdevar/aln/utils/KotlinModule.kt deleted file mode 100644 index c8f280e..0000000 --- a/android/app/src/main/java/me/kavishdevar/aln/utils/KotlinModule.kt +++ /dev/null @@ -1,41 +0,0 @@ -package me.kavishdevar.aln.utils - -import android.content.pm.ApplicationInfo -import android.util.Log -import io.github.libxposed.api.XposedInterface -import io.github.libxposed.api.XposedModule -import io.github.libxposed.api.XposedModuleInterface -import io.github.libxposed.api.XposedModuleInterface.ModuleLoadedParam - -private const val TAG = "AirPodsHook" -private lateinit var module: KotlinModule - -class KotlinModule(base: XposedInterface, param: ModuleLoadedParam): XposedModule(base, param) { - init { - Log.i(TAG, "AirPodsHook module initialized at :: ${param.processName}") - module = this - } - - override fun onPackageLoaded(param: XposedModuleInterface.PackageLoadedParam) { - super.onPackageLoaded(param) - Log.i(TAG, "onPackageLoaded :: ${param.packageName}") - - if (param.packageName == "com.android.bluetooth") { - Log.i(TAG, "Bluetooth app detected, hooking l2c_fcr_chk_chan_modes") - - try { - if (param.isFirstPackage) { - Log.i(TAG, "Loading native library for Bluetooth hook") - System.loadLibrary("l2c_fcr_hook") - Log.i(TAG, "Native library loaded successfully") - } - } catch (e: Exception) { - Log.e(TAG, "Failed to load native library: ${e.message}", e) - } - } - } - - override fun getApplicationInfo(): ApplicationInfo { - return super.applicationInfo - } -} diff --git a/android/app/src/main/java/me/kavishdevar/aln/CustomDeviceActivity.kt b/android/app/src/main/java/me/kavishdevar/librepods/CustomDeviceActivity.kt similarity index 97% rename from android/app/src/main/java/me/kavishdevar/aln/CustomDeviceActivity.kt rename to android/app/src/main/java/me/kavishdevar/librepods/CustomDeviceActivity.kt index 7cead86..98398aa 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/CustomDeviceActivity.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/CustomDeviceActivity.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln +package me.kavishdevar.librepods import android.Manifest import android.annotation.SuppressLint @@ -47,7 +47,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import me.kavishdevar.aln.ui.theme.ALNTheme +import me.kavishdevar.librepods.ui.theme.LibrePodsTheme import org.lsposed.hiddenapibypass.HiddenApiBypass import java.util.UUID @@ -57,7 +57,7 @@ class CustomDevice : ComponentActivity() { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { - ALNTheme { + LibrePodsTheme { val connect = remember { mutableStateOf(false) } Scaffold( modifier = Modifier.fillMaxSize(), diff --git a/android/app/src/main/java/me/kavishdevar/aln/MainActivity.kt b/android/app/src/main/java/me/kavishdevar/librepods/MainActivity.kt similarity index 89% rename from android/app/src/main/java/me/kavishdevar/aln/MainActivity.kt rename to android/app/src/main/java/me/kavishdevar/librepods/MainActivity.kt index 6035db3..ce25b1b 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/MainActivity.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/MainActivity.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln +package me.kavishdevar.librepods import android.annotation.SuppressLint import android.content.BroadcastReceiver @@ -100,18 +100,18 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.MultiplePermissionsState import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.rememberMultiplePermissionsState -import me.kavishdevar.aln.screens.AirPodsSettingsScreen -import me.kavishdevar.aln.screens.AppSettingsScreen -import me.kavishdevar.aln.screens.DebugScreen -import me.kavishdevar.aln.screens.HeadTrackingScreen -import me.kavishdevar.aln.screens.LongPress -import me.kavishdevar.aln.screens.Onboarding -import me.kavishdevar.aln.screens.RenameScreen -import me.kavishdevar.aln.services.AirPodsService -import me.kavishdevar.aln.ui.theme.ALNTheme -import me.kavishdevar.aln.utils.AirPodsNotifications -import me.kavishdevar.aln.utils.CrossDevice -import me.kavishdevar.aln.utils.RadareOffsetFinder +import me.kavishdevar.librepods.screens.AirPodsSettingsScreen +import me.kavishdevar.librepods.screens.AppSettingsScreen +import me.kavishdevar.librepods.screens.DebugScreen +import me.kavishdevar.librepods.screens.HeadTrackingScreen +import me.kavishdevar.librepods.screens.LongPress +import me.kavishdevar.librepods.screens.Onboarding +import me.kavishdevar.librepods.screens.RenameScreen +import me.kavishdevar.librepods.services.AirPodsService +import me.kavishdevar.librepods.ui.theme.LibrePodsTheme +import me.kavishdevar.librepods.utils.AirPodsNotifications +import me.kavishdevar.librepods.utils.CrossDevice +import me.kavishdevar.librepods.utils.RadareOffsetFinder lateinit var serviceConnection: ServiceConnection lateinit var connectionStatusReceiver: BroadcastReceiver @@ -129,7 +129,7 @@ class MainActivity : ComponentActivity() { enableEdgeToEdge() setContent { - ALNTheme { + LibrePodsTheme { getSharedPreferences("settings", MODE_PRIVATE).edit().putLong("textColor", MaterialTheme.colorScheme.onSurface.toArgb().toLong()).apply() Main() @@ -180,6 +180,7 @@ fun Main() { val hookAvailable = RadareOffsetFinder(LocalContext.current).isHookOffsetAvailable() val context = LocalContext.current var canDrawOverlays by remember { mutableStateOf(Settings.canDrawOverlays(context)) } + val overlaySkipped = remember { mutableStateOf(context.getSharedPreferences("settings", MODE_PRIVATE).getBoolean("overlay_permission_skipped", false)) } val permissionState = rememberMultiplePermissionsState( permissions = listOf( @@ -197,7 +198,7 @@ fun Main() { canDrawOverlays = Settings.canDrawOverlays(context) } - if (permissionState.allPermissionsGranted && canDrawOverlays) { + if (permissionState.allPermissionsGranted && (canDrawOverlays || overlaySkipped.value)) { val context = LocalContext.current context.startService(Intent(context, AirPodsService::class.java)) @@ -310,7 +311,7 @@ fun Main() { } } -@OptIn(ExperimentalPermissionsApi::class) +@OptIn(ExperimentalPermissionsApi::class, ExperimentalMaterial3Api::class) @Composable fun PermissionsScreen( permissionState: MultiplePermissionsState, @@ -325,6 +326,8 @@ fun PermissionsScreen( val scrollState = rememberScrollState() + val basicPermissionsGranted = permissionState.permissions.all { it.status.isGranted } + val infiniteTransition = rememberInfiniteTransition(label = "pulse") val pulseScale by infiniteTransition.animateFloat( initialValue = 1f, @@ -514,6 +517,39 @@ fun PermissionsScreen( ), ) } + + if (!canDrawOverlays && basicPermissionsGranted) { + Spacer(modifier = Modifier.height(12.dp)) + + Button( + onClick = { + val editor = context.getSharedPreferences("settings", MODE_PRIVATE).edit() + editor.putBoolean("overlay_permission_skipped", true) + editor.apply() + + val intent = Intent(context, MainActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + context.startActivity(intent) + }, + modifier = Modifier + .fillMaxWidth() + .height(55.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Color(0xFF757575) + ), + shape = RoundedCornerShape(8.dp) + ) { + Text( + "Continue without overlay", + style = TextStyle( + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + fontFamily = FontFamily(Font(R.font.sf_pro)), + color = Color.White + ), + ) + } + } } } diff --git a/android/app/src/main/java/me/kavishdevar/librepods/QuickSettingsDialogActivity.kt b/android/app/src/main/java/me/kavishdevar/librepods/QuickSettingsDialogActivity.kt new file mode 100644 index 0000000..c6d1214 --- /dev/null +++ b/android/app/src/main/java/me/kavishdevar/librepods/QuickSettingsDialogActivity.kt @@ -0,0 +1,621 @@ +package me.kavishdevar.librepods + +import android.content.BroadcastReceiver +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.ServiceConnection +import android.media.AudioManager +import android.os.Build +import android.os.Bundle +import android.os.IBinder +import android.util.Log +import android.view.Gravity +import android.view.WindowManager +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.animation.Crossfade +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.FastOutSlowInEasing +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.spring +import androidx.compose.animation.core.tween +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.detectVerticalDragGestures +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import kotlinx.coroutines.launch +import me.kavishdevar.librepods.composables.AdaptiveRainbowBrush +import me.kavishdevar.librepods.composables.IconAreaSize +import me.kavishdevar.librepods.composables.ControlCenterNoiseControlSegmentedButton +import me.kavishdevar.librepods.composables.VerticalVolumeSlider +import me.kavishdevar.librepods.services.AirPodsService +import me.kavishdevar.librepods.ui.theme.LibrePodsTheme +import me.kavishdevar.librepods.utils.AirPodsNotifications +import me.kavishdevar.librepods.utils.NoiseControlMode +import kotlin.math.abs + +data class DismissAnimationValues( + val offsetY: Dp = 0.dp, + val scale: Float = 1f, + val alpha: Float = 1f +) + +class QuickSettingsDialogActivity : ComponentActivity() { + + private var airPodsService: AirPodsService? = null + private var isBound = false + + private var isNoiseControlExpandedState by mutableStateOf(false) + + private val connection = object : ServiceConnection { + override fun onServiceConnected(className: ComponentName, service: IBinder) { + val binder = service as AirPodsService.LocalBinder + airPodsService = binder.getService() + isBound = true + Log.d("QSActivity", "Service bound") + setContent { + LibrePodsTheme { + DraggableDismissBox( + onDismiss = { finish() }, + onlyCollapseWhenClicked = { + if (isNoiseControlExpandedState) { + isNoiseControlExpandedState = false + true + } else { + false + } + } + ) { + if (isBound && airPodsService != null) { + NewControlCenterDialogContent( + service = airPodsService, + isNoiseControlExpanded = isNoiseControlExpandedState, + onNoiseControlExpandedChange = { isNoiseControlExpandedState = it } + ) + } else { + } + } + } + } + } + + override fun onServiceDisconnected(arg0: ComponentName) { + isBound = false + airPodsService = null + Log.d("QSActivity", "Service unbound") + finish() + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL) + window.addFlags(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH) + window.addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND) + window.setGravity(Gravity.BOTTOM) + + Intent(this, AirPodsService::class.java).also { intent -> + bindService(intent, connection, Context.BIND_AUTO_CREATE) + } + + setContent { + LibrePodsTheme { + DraggableDismissBox( + onDismiss = { finish() }, + onlyCollapseWhenClicked = { + if (isNoiseControlExpandedState) { + isNoiseControlExpandedState = false + true + } else { + false + } + } + ) { + if (isBound && airPodsService != null) { + NewControlCenterDialogContent( + service = airPodsService, + isNoiseControlExpanded = isNoiseControlExpandedState, + onNoiseControlExpandedChange = { isNoiseControlExpandedState = it } + ) + } else { + } + } + } + } + } + + override fun onDestroy() { + super.onDestroy() + if (isBound) { + unbindService(connection) + isBound = false + } + } +} + +@Composable +fun DraggableDismissBox( + onDismiss: () -> Unit, + onlyCollapseWhenClicked: () -> Boolean, + content: @Composable () -> Unit +) { + val coroutineScope = rememberCoroutineScope() + val density = LocalDensity.current + + var dragOffset by remember { mutableFloatStateOf(0f) } + var isDragging by remember { mutableStateOf(false) } + val dismissThreshold = 400f + + val animatedOffset = remember { Animatable(0f) } + val animatedScale = remember { Animatable(1f) } + val animatedAlpha = remember { Animatable(1f) } + + val backgroundAlpha by animateFloatAsState( + targetValue = if (isDragging) { + val dragProgress = (abs(dragOffset) / 800f).coerceIn(0f, 0.8f) + 1f - dragProgress + } else 1f, + label = "BackgroundFade" + ) + + LaunchedEffect(isDragging) { + if (!isDragging) { + if (abs(dragOffset) < dismissThreshold) { + val springSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessHigh, + visibilityThreshold = 0.1f + ) + launch { animatedOffset.animateTo(0f, springSpec) } + launch { animatedScale.animateTo(1f, springSpec) } + launch { animatedAlpha.animateTo(1f, tween(100)) } + dragOffset = 0f + } + } + } + + LaunchedEffect(dragOffset, isDragging) { + if (isDragging) { + val dragDirection = if (dragOffset > 0) 1f else -1f + val dragProgress = (abs(dragOffset) / 1000f).coerceIn(0f, 0.5f) + + animatedOffset.snapTo(dragOffset) + animatedScale.snapTo(1f - dragProgress * 0.3f) + animatedAlpha.snapTo(1f - dragProgress * 0.7f) + } + } + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color.Black.copy(alpha = 0.5f * backgroundAlpha)) + .pointerInput(Unit) { + detectVerticalDragGestures( + onDragStart = { isDragging = true }, + onDragEnd = { + isDragging = false + if (abs(dragOffset) > dismissThreshold) { + coroutineScope.launch { + val direction = if (dragOffset > 0) 1f else -1f + + launch { + animatedOffset.animateTo( + direction * 1500f, + tween(350, easing = FastOutSlowInEasing) + ) + } + launch { animatedScale.animateTo(0.7f, tween(350)) } + launch { animatedAlpha.animateTo(0f, tween(250)) } + + kotlinx.coroutines.delay(350) + onDismiss() + } + } + }, + onDragCancel = { isDragging = false }, + onVerticalDrag = { change, dragAmount -> + change.consume() + dragOffset += dragAmount + } + ) + } + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = null + ) { + onlyCollapseWhenClicked() + }, + contentAlignment = Alignment.BottomCenter + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .graphicsLayer( + translationY = animatedOffset.value, + scaleX = animatedScale.value, + scaleY = animatedScale.value, + alpha = animatedAlpha.value + ), + contentAlignment = Alignment.BottomCenter + ) { + content() + } + } +} + +@Composable +fun NewControlCenterDialogContent( + service: AirPodsService?, + isNoiseControlExpanded: Boolean, + onNoiseControlExpandedChange: (Boolean) -> Unit +) { + val context = LocalContext.current + val sharedPreferences = context.getSharedPreferences("settings", Context.MODE_PRIVATE) + val textColor = Color.White + + var currentAncMode by remember { mutableStateOf(NoiseControlMode.TRANSPARENCY) } + var isConvAwarenessEnabled by remember { mutableStateOf(false) } + + val isOffModeEnabled = remember { sharedPreferences.getBoolean("off_listening_mode", true) } + val availableModes = remember(isOffModeEnabled) { + mutableListOf( + NoiseControlMode.TRANSPARENCY, + NoiseControlMode.ADAPTIVE, + NoiseControlMode.NOISE_CANCELLATION + ).apply { + if (isOffModeEnabled) { + add(0, NoiseControlMode.OFF) + } + } + } + + val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + val maxVolume = remember { audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) } + var currentVolumeInt by remember { mutableIntStateOf(audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)) } + val animatedVolumeFraction by animateFloatAsState( + targetValue = currentVolumeInt.toFloat() / maxVolume.toFloat(), + animationSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessMediumLow + ), + label = "VolumeAnimation" + ) + var liveDragFraction by remember { mutableFloatStateOf(animatedVolumeFraction) } + var isDraggingVolume by remember { mutableStateOf(false) } + LaunchedEffect(animatedVolumeFraction, isDraggingVolume) { + if (!isDraggingVolume) { + liveDragFraction = animatedVolumeFraction + } + } + + DisposableEffect(service, availableModes) { + val ancReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action == AirPodsNotifications.ANC_DATA && service != null) { + val newModeOrdinal = intent.getIntExtra("data", NoiseControlMode.TRANSPARENCY.ordinal + 1) - 1 + val newMode = NoiseControlMode.entries.getOrElse(newModeOrdinal) { NoiseControlMode.TRANSPARENCY } + if (availableModes.contains(newMode)) { + currentAncMode = newMode + } else if (newMode == NoiseControlMode.OFF && !isOffModeEnabled) { + currentAncMode = NoiseControlMode.TRANSPARENCY + } + Log.d("QSActivity", "ANC Receiver updated mode to: $currentAncMode (available: ${availableModes.joinToString()})") + } + } + } + val filter = IntentFilter(AirPodsNotifications.ANC_DATA) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + context.registerReceiver(ancReceiver, filter, Context.RECEIVER_EXPORTED) + } else { + context.registerReceiver(ancReceiver, filter) + } + + service?.let { + val initialModeOrdinal = it.getANC().minus(1) ?: NoiseControlMode.TRANSPARENCY.ordinal + var initialMode = NoiseControlMode.entries.getOrElse(initialModeOrdinal) { NoiseControlMode.TRANSPARENCY } + if (!availableModes.contains(initialMode)) { + initialMode = NoiseControlMode.TRANSPARENCY + } + currentAncMode = initialMode + isConvAwarenessEnabled = sharedPreferences.getBoolean("conversational_awareness", true) + Log.d("QSActivity", "Initial ANC: $currentAncMode, ConvAware: $isConvAwarenessEnabled") + } + + onDispose { + context.unregisterReceiver(ancReceiver) + } + } + + DisposableEffect(Unit) { + val volumeReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action == "android.media.VOLUME_CHANGED_ACTION") { + val newVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) + if (newVolume != currentVolumeInt) { + currentVolumeInt = newVolume + Log.d("QSActivity", "Volume Receiver updated volume to: $currentVolumeInt") + } + } + } + } + val filter = IntentFilter("android.media.VOLUME_CHANGED_ACTION") + context.registerReceiver(volumeReceiver, filter) + onDispose { + context.unregisterReceiver(volumeReceiver) + } + } + + val deviceName = remember { sharedPreferences.getString("name", "AirPods") ?: "AirPods" } + + Column( + modifier = Modifier + .fillMaxSize() + .background(Color.Transparent) + .padding(horizontal = 24.dp) + .pointerInput(Unit) { + awaitPointerEventScope { + while (true) { + awaitPointerEvent() + } + } + }, + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceBetween + ) { + if (service != null) { + Spacer(modifier = Modifier.weight(1f)) + + Column( + modifier = Modifier + .weight(2f) + .fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Icon( + painter = painterResource(id = R.drawable.airpods), + contentDescription = "Device Icon", + tint = textColor.copy(alpha = 0.8f), + modifier = Modifier.size(48.dp) + ) + + Spacer(modifier = Modifier.height(4.dp)) + + Text( + text = deviceName, + color = textColor, + fontSize = 16.sp, + fontWeight = FontWeight.Medium + ) + + Spacer(modifier = Modifier.height(32.dp)) + + VerticalVolumeSlider( + displayFraction = animatedVolumeFraction, + maxVolume = maxVolume, + onVolumeChange = { newVolume -> + currentVolumeInt = newVolume + try { + audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, newVolume, 0) + } catch (e: Exception) { Log.e("QSActivity", "Failed to set volume", e) } + }, + initialFraction = animatedVolumeFraction, + onDragStateChange = { dragging -> isDraggingVolume = dragging }, + baseSliderHeight = 400.dp, + baseSliderWidth = 145.dp, + baseCornerRadius = 48.dp, + maxStretchFactor = 1.15f, + minCompressionFactor = 0.875f, + stretchSensitivity = 0.3f, + compressionSensitivity = 0.3f, + cornerRadiusChangeFactor = -0.5f, + directionalStretchRatio = 0.75f, + modifier = Modifier + .width(145.dp) + .padding(vertical = 8.dp) + ) + } + + Spacer(modifier = Modifier.weight(1f)) + + Box( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 72.dp) + .animateContentSize( + animationSpec = spring( + dampingRatio = Spring.DampingRatioMediumBouncy, + stiffness = Spring.StiffnessMedium + ) + ), + contentAlignment = Alignment.Center + ) { + Crossfade( + targetState = isNoiseControlExpanded, + animationSpec = tween(durationMillis = 300), + label = "NoiseControlCrossfade" + ) { expanded -> + if (expanded) { + ControlCenterNoiseControlSegmentedButton( + availableModes = availableModes, + selectedMode = currentAncMode, + onModeSelected = { newMode -> + service.setANCMode(newMode.ordinal + 1) + currentAncMode = newMode + }, + modifier = Modifier.fillMaxWidth(0.8f) + ) + } else { + Row( + modifier = Modifier.fillMaxWidth(0.85f), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.Top + ) { + val noiseControlButtonBrush = if (currentAncMode == NoiseControlMode.ADAPTIVE) { + AdaptiveRainbowBrush + } else { + null + } + + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier.weight(1f) + ) { + Box( + modifier = Modifier + .size(IconAreaSize) + .clip(CircleShape) + .background( + brush = noiseControlButtonBrush ?: + Brush.linearGradient(colors = listOf(Color(0xFF0A84FF), Color(0xFF0A84FF))) + ) + .clickable( + onClick = { onNoiseControlExpandedChange(true) }, + indication = null, + interactionSource = remember { MutableInteractionSource() } + ), + contentAlignment = Alignment.Center + ) { + Icon( + painter = painterResource(id = getModeIconRes(currentAncMode)), + contentDescription = getModeLabel(currentAncMode), + tint = Color.White, + modifier = Modifier.size(32.dp) + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = getModeLabel(currentAncMode), + color = Color.White, + fontSize = 12.sp, + fontWeight = FontWeight.Medium, + textAlign = androidx.compose.ui.text.style.TextAlign.Center + ) + } + + Spacer(modifier = Modifier.width(24.dp)) + + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier.weight(1f) + ) { + Box( + modifier = Modifier + .size(IconAreaSize) + .clip(CircleShape) + .background( + Brush.linearGradient( + colors = listOf( + if (isConvAwarenessEnabled) Color(0xFF0A84FF) else Color(0x593C3C3E), + if (isConvAwarenessEnabled) Color(0xFF0A84FF) else Color(0x593C3C3E) + ) + ) + ) + .clickable( + onClick = { + val newState = !isConvAwarenessEnabled + service.setCAEnabled(newState) + isConvAwarenessEnabled = newState + }, + indication = null, + interactionSource = remember { MutableInteractionSource() } + ), + contentAlignment = Alignment.Center + ) { + Icon( + painter = painterResource(id = R.drawable.airpods), + contentDescription = "Conversational Awareness", + tint = Color.White, + modifier = Modifier.size(32.dp) + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = "Conversational\nAwareness", + color = Color.White, + fontSize = 12.sp, + fontWeight = FontWeight.Medium, + textAlign = androidx.compose.ui.text.style.TextAlign.Center, + lineHeight = 14.sp + ) + } + } + } + } + } + + } else { + Spacer(modifier = Modifier.weight(1f)) + Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) { + Text("Loading...", color = textColor) + } + Spacer(modifier = Modifier.weight(1f)) + } + } +} + +private fun getModeIconRes(mode: NoiseControlMode): Int { + return when (mode) { + NoiseControlMode.OFF -> R.drawable.noise_cancellation + NoiseControlMode.TRANSPARENCY -> R.drawable.transparency + NoiseControlMode.ADAPTIVE -> R.drawable.adaptive + NoiseControlMode.NOISE_CANCELLATION -> R.drawable.noise_cancellation + } +} + +private fun getModeLabel(mode: NoiseControlMode): String { + return when (mode) { + NoiseControlMode.OFF -> "Off" + NoiseControlMode.TRANSPARENCY -> "Transparency" + NoiseControlMode.ADAPTIVE -> "Adaptive" + NoiseControlMode.NOISE_CANCELLATION -> "Noise Cancel" + } +} diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/AccessibilitySettings.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/AccessibilitySettings.kt similarity index 96% rename from android/app/src/main/java/me/kavishdevar/aln/composables/AccessibilitySettings.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/AccessibilitySettings.kt index ab126eb..929d45d 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/AccessibilitySettings.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/AccessibilitySettings.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import android.content.Context import android.content.SharedPreferences @@ -45,8 +45,8 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import me.kavishdevar.aln.R -import me.kavishdevar.aln.services.AirPodsService +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.services.AirPodsService @Composable fun AccessibilitySettings(service: AirPodsService, sharedPreferences: SharedPreferences) { diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/AdaptiveStrengthSlider.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/AdaptiveStrengthSlider.kt similarity index 95% rename from android/app/src/main/java/me/kavishdevar/aln/composables/AdaptiveStrengthSlider.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/AdaptiveStrengthSlider.kt index a898bf1..56e5295 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/AdaptiveStrengthSlider.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/AdaptiveStrengthSlider.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import android.content.Context import android.content.SharedPreferences @@ -50,7 +50,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import me.kavishdevar.aln.services.AirPodsService +import me.kavishdevar.librepods.services.AirPodsService import kotlin.math.roundToInt @OptIn(ExperimentalMaterial3Api::class) diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/AudioSettings.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/AudioSettings.kt similarity index 93% rename from android/app/src/main/java/me/kavishdevar/aln/composables/AudioSettings.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/AudioSettings.kt index 4c9afa9..157e597 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/AudioSettings.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/AudioSettings.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import android.content.Context import android.content.SharedPreferences @@ -37,8 +37,8 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import me.kavishdevar.aln.R -import me.kavishdevar.aln.services.AirPodsService +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.services.AirPodsService @Composable fun AudioSettings(service: AirPodsService, sharedPreferences: SharedPreferences) { diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/BatteryIndicator.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/BatteryIndicator.kt similarity index 95% rename from android/app/src/main/java/me/kavishdevar/aln/composables/BatteryIndicator.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/BatteryIndicator.kt index 26248fa..130f71a 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/BatteryIndicator.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/BatteryIndicator.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import androidx.compose.animation.core.animateFloatAsState @@ -48,7 +48,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import me.kavishdevar.aln.R +import me.kavishdevar.librepods.R @Composable fun BatteryIndicator(batteryPercentage: Int, charging: Boolean = false) { diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/BatteryView.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/BatteryView.kt similarity index 93% rename from android/app/src/main/java/me/kavishdevar/aln/composables/BatteryView.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/BatteryView.kt index 8f66bbf..dd2cfa5 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/BatteryView.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/BatteryView.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import android.content.BroadcastReceiver import android.content.Context @@ -44,12 +44,12 @@ import androidx.compose.ui.res.imageResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import me.kavishdevar.aln.R -import me.kavishdevar.aln.services.AirPodsService -import me.kavishdevar.aln.utils.AirPodsNotifications -import me.kavishdevar.aln.utils.Battery -import me.kavishdevar.aln.utils.BatteryComponent -import me.kavishdevar.aln.utils.BatteryStatus +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.services.AirPodsService +import me.kavishdevar.librepods.utils.AirPodsNotifications +import me.kavishdevar.librepods.utils.Battery +import me.kavishdevar.librepods.utils.BatteryComponent +import me.kavishdevar.librepods.utils.BatteryStatus @Composable fun BatteryView(service: AirPodsService, preview: Boolean = false) { diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/ControlCenterButton.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/ControlCenterButton.kt new file mode 100644 index 0000000..31df084 --- /dev/null +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/ControlCenterButton.kt @@ -0,0 +1,104 @@ +/* + * LibrePods - AirPods liberated from Apple’s ecosystem + * + * Copyright (C) 2025 Kavish Devar + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package me.kavishdevar.librepods.composables + +import androidx.compose.animation.animateColorAsState +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +private val SelectedColorBlue = Color(0xFF0A84FF) +private val UnselectedColor = Color(0x593C3C3E) +private val TextColor = Color.White +private val IconTint = Color.White + +@Composable +fun ControlCenterButton( + label: String, + icon: Painter, + onClick: () -> Unit, + modifier: Modifier = Modifier, + iconAreaSize: Dp, + isSelected: Boolean, + backgroundBrush: Brush? = null +) { + val targetBackgroundColor = if (isSelected) SelectedColorBlue else UnselectedColor + val backgroundColor by animateColorAsState( + targetValue = targetBackgroundColor, + label = "ButtonBackground" + ) + + Column( + modifier = modifier, + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Box( + modifier = Modifier + .size(iconAreaSize) + .clip(CircleShape) + .background(backgroundBrush ?: Brush.linearGradient(colors=listOf(backgroundColor, backgroundColor))) + .clickable( + onClick = onClick, + indication = null, + interactionSource = remember { MutableInteractionSource() } + ), + contentAlignment = Alignment.Center + ) { + Icon( + painter = icon, + contentDescription = null, + tint = IconTint, + modifier = Modifier.size(32.dp) + ) + } + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = label, + color = TextColor, + fontSize = 12.sp, + fontWeight = FontWeight.Medium, + textAlign = TextAlign.Center, + maxLines = 2 + ) + } +} diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/ControlCenterNoiseControlSegmentedButton.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/ControlCenterNoiseControlSegmentedButton.kt new file mode 100644 index 0000000..9daece6 --- /dev/null +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/ControlCenterNoiseControlSegmentedButton.kt @@ -0,0 +1,242 @@ +/* + * LibrePods - AirPods liberated from Apple’s ecosystem + * + * Copyright (C) 2025 Kavish Devar + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package me.kavishdevar.librepods.composables + +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.spring +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.utils.NoiseControlMode + +private val ContainerColor = Color(0x593C3C3E) +private val SelectedIndicatorColorGray = Color(0xFF6C6C6E) +private val SelectedIndicatorColorBlue = Color(0xFF0A84FF) +private val TextColor = Color.White +private val IconTintUnselected = Color.White +private val IconTintSelected = Color.White + +internal val AdaptiveRainbowBrush = Brush.sweepGradient( + colors = listOf( + Color(0xFFB03A2F), Color(0xFFB07A2F), Color(0xFFB0A22F), Color(0xFF6AB02F), + Color(0xFF2FAAB0), Color(0xFF2F5EB0), Color(0xFF7D2FB0), Color(0xFFB02F7D), + Color(0xFFB03A2F) + ) +) + +internal val IconAreaSize = 72.dp +private val IconSize = 42.dp +private val IconRowHeight = IconAreaSize + 12.dp +private val TextRowHeight = 24.dp +private val TextSize = 12.sp + +@Composable +fun ControlCenterNoiseControlSegmentedButton( + modifier: Modifier = Modifier, + availableModes: List, + selectedMode: NoiseControlMode, + onModeSelected: (NoiseControlMode) -> Unit +) { + val selectedIndex = availableModes.indexOf(selectedMode).coerceAtLeast(0) + val density = LocalDensity.current + var iconRowWidthPx by remember { mutableStateOf(0f) } + val itemCount = availableModes.size + + val itemSlotWidthPx = remember(iconRowWidthPx, itemCount) { + if (itemCount > 0 && iconRowWidthPx > 0) { + iconRowWidthPx / itemCount + } else { + 0f + } + } + val itemSlotWidthDp = remember(itemSlotWidthPx) { with(density) { itemSlotWidthPx.toDp() } } + val iconAreaSizePx = remember { with(density) { IconAreaSize.toPx() } } + + val targetIndicatorStartPx = remember(selectedIndex, itemSlotWidthPx, iconAreaSizePx) { + if (itemSlotWidthPx > 0) { + val slotCenterPx = (selectedIndex + 0.5f) * itemSlotWidthPx + slotCenterPx - (iconAreaSizePx / 2f) + } else { + 0f + } + } + + val indicatorOffset: Dp by animateDpAsState( + targetValue = with(density) { targetIndicatorStartPx.toDp() }, + animationSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessMedium + ), + label = "IndicatorOffset" + ) + + val indicatorBackground = remember(selectedMode) { + when (selectedMode) { + NoiseControlMode.ADAPTIVE -> AdaptiveRainbowBrush + NoiseControlMode.OFF -> Brush.linearGradient(colors=listOf(SelectedIndicatorColorGray, SelectedIndicatorColorGray)) + NoiseControlMode.TRANSPARENCY, + NoiseControlMode.NOISE_CANCELLATION -> Brush.linearGradient(colors=listOf(SelectedIndicatorColorBlue, SelectedIndicatorColorBlue)) + } + } + + Column( + modifier = modifier, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .height(IconRowHeight) + .clip(CircleShape) + .background(ContainerColor) + .onSizeChanged { iconRowWidthPx = it.width.toFloat() }, + contentAlignment = Alignment.Center + ) { + Box( + Modifier + .align(Alignment.CenterStart) + .offset(x = indicatorOffset) + .size(IconAreaSize) + .clip(CircleShape) + .background(indicatorBackground) + ) + + Row( + modifier = Modifier.fillMaxWidth().align(Alignment.Center), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceAround + ) { + availableModes.forEach { mode -> + val isSelected = selectedMode == mode + NoiseControlIconItem( + modifier = Modifier.size(IconAreaSize), + mode = mode, + isSelected = isSelected, + onClick = { onModeSelected(mode) } + ) + } + } + } + + Spacer(modifier = Modifier.height(4.dp)) + + Row( + modifier = Modifier + .fillMaxWidth() + .height(TextRowHeight), + horizontalArrangement = Arrangement.SpaceAround, + verticalAlignment = Alignment.CenterVertically + ) { + availableModes.forEach { mode -> + val isSelected = selectedMode == mode + Text( + text = getModeLabel(mode), + color = TextColor, + fontSize = TextSize, + fontWeight = if (isSelected) FontWeight.SemiBold else FontWeight.Normal, + textAlign = TextAlign.Center, + modifier = Modifier.width(itemSlotWidthDp.coerceAtLeast(1.dp)) + ) + } + } + } +} + +@Composable +private fun NoiseControlIconItem( + modifier: Modifier = Modifier, + mode: NoiseControlMode, + isSelected: Boolean, + onClick: () -> Unit +) { + val iconRes = remember(mode) { getModeIconRes(mode) } + + val tint = IconTintUnselected + + Box( + modifier = modifier + .clip(CircleShape) + .clickable( + onClick = onClick, + indication = null, + interactionSource = remember { MutableInteractionSource() } + ), + contentAlignment = Alignment.Center + ) { + Icon( + painter = painterResource(id = iconRes), + contentDescription = getModeLabel(mode), + tint = if (isSelected && mode == NoiseControlMode.ADAPTIVE) IconTintSelected else tint, + modifier = Modifier.size(IconSize) + ) + } +} + + +private fun getModeIconRes(mode: NoiseControlMode): Int { + return when (mode) { + NoiseControlMode.OFF -> R.drawable.noise_cancellation + NoiseControlMode.TRANSPARENCY -> R.drawable.transparency + NoiseControlMode.ADAPTIVE -> R.drawable.adaptive + NoiseControlMode.NOISE_CANCELLATION -> R.drawable.noise_cancellation + } +} + +private fun getModeLabel(mode: NoiseControlMode): String { + return when (mode) { + NoiseControlMode.OFF -> "Off" + NoiseControlMode.TRANSPARENCY -> "Transparency" + NoiseControlMode.ADAPTIVE -> "Adaptive" + NoiseControlMode.NOISE_CANCELLATION -> "Noise Cancellation" + } +} + diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/ConversationalAwarenessSwitch.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/ConversationalAwarenessSwitch.kt similarity index 94% rename from android/app/src/main/java/me/kavishdevar/aln/composables/ConversationalAwarenessSwitch.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/ConversationalAwarenessSwitch.kt index 6476062..de2f8ec 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/ConversationalAwarenessSwitch.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/ConversationalAwarenessSwitch.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import android.content.SharedPreferences import androidx.compose.foundation.background @@ -45,7 +45,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import me.kavishdevar.aln.services.AirPodsService +import me.kavishdevar.librepods.services.AirPodsService @Composable fun ConversationalAwarenessSwitch(service: AirPodsService, sharedPreferences: SharedPreferences) { diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/CustomDropdown.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/CustomDropdown.kt similarity index 96% rename from android/app/src/main/java/me/kavishdevar/aln/composables/CustomDropdown.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/CustomDropdown.kt index ba7f8eb..a4d37b6 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/CustomDropdown.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/CustomDropdown.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import androidx.compose.animation.core.Spring import androidx.compose.animation.core.animateDpAsState @@ -55,7 +55,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Popup import androidx.compose.ui.window.PopupProperties -import me.kavishdevar.aln.R +import me.kavishdevar.librepods.R class DropdownItem(val name: String, val onSelect: () -> Unit) { fun select() { diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/IndependentToggle.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/IndependentToggle.kt similarity index 95% rename from android/app/src/main/java/me/kavishdevar/aln/composables/IndependentToggle.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/IndependentToggle.kt index 47bb8fa..f310f29 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/IndependentToggle.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/IndependentToggle.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import android.content.SharedPreferences import androidx.compose.animation.animateColorAsState @@ -45,7 +45,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import me.kavishdevar.aln.services.AirPodsService +import me.kavishdevar.librepods.services.AirPodsService @Composable fun IndependentToggle(name: String, service: AirPodsService? = null, functionName: String? = null, sharedPreferences: SharedPreferences, default: Boolean = false) { diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/LoudSoundReductionSwitch.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/LoudSoundReductionSwitch.kt similarity index 94% rename from android/app/src/main/java/me/kavishdevar/aln/composables/LoudSoundReductionSwitch.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/LoudSoundReductionSwitch.kt index 0cb817c..2f971f9 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/LoudSoundReductionSwitch.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/LoudSoundReductionSwitch.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import android.content.SharedPreferences import androidx.compose.foundation.background @@ -45,7 +45,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import me.kavishdevar.aln.services.AirPodsService +import me.kavishdevar.librepods.services.AirPodsService @Composable fun LoudSoundReductionSwitch(service: AirPodsService, sharedPreferences: SharedPreferences) { diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/NameField.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/NameField.kt similarity index 96% rename from android/app/src/main/java/me/kavishdevar/aln/composables/NameField.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/NameField.kt index 8b4f6c7..399adc4 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/NameField.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/NameField.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.tween diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/NavigationButton.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/NavigationButton.kt similarity index 95% rename from android/app/src/main/java/me/kavishdevar/aln/composables/NavigationButton.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/NavigationButton.kt index 2f74e89..28e71bf 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/NavigationButton.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/NavigationButton.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.tween diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/NoiseControlButton.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/NoiseControlButton.kt similarity index 91% rename from android/app/src/main/java/me/kavishdevar/aln/composables/NoiseControlButton.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/NoiseControlButton.kt index 63bdfa0..504c9d0 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/NoiseControlButton.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/NoiseControlButton.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource @@ -35,7 +35,7 @@ import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.res.imageResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import me.kavishdevar.aln.R +import me.kavishdevar.librepods.R @Composable fun NoiseControlButton( diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/NoiseControlSettings.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/NoiseControlSettings.kt similarity index 91% rename from android/app/src/main/java/me/kavishdevar/aln/composables/NoiseControlSettings.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/NoiseControlSettings.kt index d7d6289..4b7b111 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/NoiseControlSettings.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/NoiseControlSettings.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import android.annotation.SuppressLint import android.content.BroadcastReceiver @@ -72,15 +72,18 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.zIndex -import me.kavishdevar.aln.R -import me.kavishdevar.aln.services.AirPodsService -import me.kavishdevar.aln.utils.AirPodsNotifications -import me.kavishdevar.aln.utils.NoiseControlMode +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.services.AirPodsService +import me.kavishdevar.librepods.utils.AirPodsNotifications +import me.kavishdevar.librepods.utils.NoiseControlMode import kotlin.math.roundToInt @SuppressLint("UnspecifiedRegisterReceiverFlag", "UnusedBoxWithConstraintsScope") @Composable -fun NoiseControlSettings(service: AirPodsService) { +fun NoiseControlSettings( + service: AirPodsService, + onModeSelectedCallback: () -> Unit = {} // Callback parameter remains, but won't finish activity +) { val context = LocalContext.current val sharedPreferences = context.getSharedPreferences("settings", Context.MODE_PRIVATE) val offListeningMode = remember { mutableStateOf(sharedPreferences.getBoolean("off_listening_mode", true)) } @@ -113,13 +116,27 @@ fun NoiseControlSettings(service: AirPodsService) { val d3a = remember { mutableFloatStateOf(0f) } fun onModeSelected(mode: NoiseControlMode, received: Boolean = false) { - if (!received && !offListeningMode.value && mode == NoiseControlMode.OFF) { - noiseControlMode.value = NoiseControlMode.ADAPTIVE + val previousMode = noiseControlMode.value // Store previous mode + + // Ensure the mode is valid if 'Off' is disabled + val targetMode = if (!offListeningMode.value && mode == NoiseControlMode.OFF) { + // If trying to select OFF but it's disabled, default to Transparency or Adaptive + NoiseControlMode.TRANSPARENCY // Or ADAPTIVE, based on preference } else { - noiseControlMode.value = mode + mode } - if (!received) service.setANCMode(mode.ordinal + 1) - when (noiseControlMode.value) { + + noiseControlMode.value = targetMode // Update internal state immediately + + // Only call service if the mode was manually selected (!received) + // and the target mode is actually different from the previous mode + if (!received && targetMode != previousMode) { + service.setANCMode(targetMode.ordinal + 1) + // onModeSelectedCallback() // REMOVE this call to keep dialog open + } + + // Update divider alphas based on the *new* mode + when (noiseControlMode.value) { // Use the updated noiseControlMode.value NoiseControlMode.NOISE_CANCELLATION -> { d1a.floatValue = 1f d2a.floatValue = 1f @@ -312,9 +329,10 @@ fun NoiseControlSettings(service: AirPodsService) { 1 -> if (offListeningMode.value) NoiseControlMode.TRANSPARENCY else NoiseControlMode.ADAPTIVE 2 -> if (offListeningMode.value) NoiseControlMode.ADAPTIVE else NoiseControlMode.NOISE_CANCELLATION 3 -> NoiseControlMode.NOISE_CANCELLATION - else -> null + else -> noiseControlMode.value // Keep current if index is invalid } - newMode?.let { onModeSelected(it) } + // Call onModeSelected which now handles service call but not callback + onModeSelected(newMode) } ) ) { @@ -429,5 +447,5 @@ fun NoiseControlSettings(service: AirPodsService) { @Preview() @Composable fun NoiseControlSettingsPreview() { - NoiseControlSettings(AirPodsService()) + NoiseControlSettings(AirPodsService()) {} } \ No newline at end of file diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/PersonalizedVolumeSwitch.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/PersonalizedVolumeSwitch.kt similarity index 94% rename from android/app/src/main/java/me/kavishdevar/aln/composables/PersonalizedVolumeSwitch.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/PersonalizedVolumeSwitch.kt index f631ca3..31379dc 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/PersonalizedVolumeSwitch.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/PersonalizedVolumeSwitch.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import android.content.SharedPreferences import androidx.compose.foundation.background @@ -45,7 +45,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import me.kavishdevar.aln.services.AirPodsService +import me.kavishdevar.librepods.services.AirPodsService @Composable fun PersonalizedVolumeSwitch(service: AirPodsService, sharedPreferences: SharedPreferences) { diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/PressAndHoldSettings.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/PressAndHoldSettings.kt similarity index 97% rename from android/app/src/main/java/me/kavishdevar/aln/composables/PressAndHoldSettings.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/PressAndHoldSettings.kt index 6a0f20e..e3a73ae 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/PressAndHoldSettings.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/PressAndHoldSettings.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.tween @@ -56,7 +56,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController -import me.kavishdevar.aln.R +import me.kavishdevar.librepods.R @Composable fun PressAndHoldSettings(navController: NavController) { diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/SinglePodANCSwitch.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/SinglePodANCSwitch.kt similarity index 94% rename from android/app/src/main/java/me/kavishdevar/aln/composables/SinglePodANCSwitch.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/SinglePodANCSwitch.kt index e572b62..14410c7 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/SinglePodANCSwitch.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/SinglePodANCSwitch.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import android.content.SharedPreferences import androidx.compose.foundation.background @@ -45,7 +45,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import me.kavishdevar.aln.services.AirPodsService +import me.kavishdevar.librepods.services.AirPodsService @Composable fun SinglePodANCSwitch(service: AirPodsService, sharedPreferences: SharedPreferences) { diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/StyledSwitch.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledSwitch.kt similarity index 93% rename from android/app/src/main/java/me/kavishdevar/aln/composables/StyledSwitch.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/StyledSwitch.kt index a631bcc..ba7a67c 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/StyledSwitch.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/StyledSwitch.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.background diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/ToneVolumeSlider.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/ToneVolumeSlider.kt similarity index 95% rename from android/app/src/main/java/me/kavishdevar/aln/composables/ToneVolumeSlider.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/ToneVolumeSlider.kt index f3fd318..69d5edb 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/ToneVolumeSlider.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/ToneVolumeSlider.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import android.content.SharedPreferences import androidx.compose.foundation.background @@ -50,8 +50,8 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import me.kavishdevar.aln.R -import me.kavishdevar.aln.services.AirPodsService +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.services.AirPodsService import kotlin.math.roundToInt @OptIn(ExperimentalMaterial3Api::class) diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/TransparencySettings.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/TransparencySettings.kt similarity index 98% rename from android/app/src/main/java/me/kavishdevar/aln/composables/TransparencySettings.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/TransparencySettings.kt index 4a1d7f4..3eece59 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/TransparencySettings.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/TransparencySettings.kt @@ -1,4 +1,4 @@ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import android.content.SharedPreferences import androidx.compose.foundation.background @@ -35,8 +35,8 @@ import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import me.kavishdevar.aln.R -import me.kavishdevar.aln.services.AirPodsService +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.services.AirPodsService @OptIn(ExperimentalMaterial3Api::class) @Composable diff --git a/android/app/src/main/java/me/kavishdevar/librepods/composables/VerticalVolumeSlider.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/VerticalVolumeSlider.kt new file mode 100644 index 0000000..3c9bd41 --- /dev/null +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/VerticalVolumeSlider.kt @@ -0,0 +1,190 @@ +/* + * LibrePods - AirPods liberated from Apple’s ecosystem + * + * Copyright (C) 2025 Kavish Devar + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package me.kavishdevar.librepods.composables + +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.spring +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.gestures.draggable +import androidx.compose.foundation.gestures.rememberDraggableState +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import kotlin.math.abs +import kotlin.math.max +import kotlin.math.min +import kotlin.math.roundToInt +import kotlin.math.sign + +@Composable +fun VerticalVolumeSlider( + displayFraction: Float, + maxVolume: Int, + onVolumeChange: (Int) -> Unit, + initialFraction: Float, + onDragStateChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + baseSliderHeight: Dp = 400.dp, + baseSliderWidth: Dp = 145.dp, + baseCornerRadius: Dp = 45.dp, + maxStretchFactor: Float = 1.15f, + minCompressionFactor: Float = 0.875f, + stretchSensitivity: Float = 1.0f, + compressionSensitivity: Float = 1.0f, + cornerRadiusChangeFactor: Float = 0.2f, + directionalStretchRatio: Float = 0.75f +) { + val trackColor = Color(0x593C3C3E) + val progressColor = Color.White + + var dragFraction by remember { mutableFloatStateOf(initialFraction) } + var isDragging by remember { mutableStateOf(false) } + + var rawDragPosition by remember { mutableFloatStateOf(initialFraction) } + var overscrollAmount by remember { mutableFloatStateOf(0f) } + + val baseHeightPx = with(LocalDensity.current) { baseSliderHeight.toPx() } + + val animatedProgress by animateFloatAsState( + targetValue = dragFraction.coerceIn(0f, 1f), + animationSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = Spring.StiffnessMedium + ), + label = "ProgressAnimation" + ) + + val animatedOverscroll by animateFloatAsState( + targetValue = overscrollAmount, + animationSpec = spring( + dampingRatio = Spring.DampingRatioMediumBouncy, + stiffness = Spring.StiffnessMediumLow + ), + label = "OverscrollAnimation" + ) + + val maxOverscrollEffect = (maxStretchFactor - 1f).coerceAtLeast(0f) + + val stretchMultiplier = stretchSensitivity + val compressionMultiplier = compressionSensitivity + + val overscrollDirection = sign(animatedOverscroll) + + val totalStretchAmount = (min(maxOverscrollEffect, abs(animatedOverscroll) * stretchMultiplier) * baseSliderHeight.value).dp + + val offsetY = if (abs(animatedOverscroll) > 0.001f) { + val asymmetricOffset = totalStretchAmount * (directionalStretchRatio - 0.5f) + (-overscrollDirection * asymmetricOffset.value).dp + } else { + 0.dp + } + + val heightStretch = baseSliderHeight + totalStretchAmount + + val widthCompression = baseSliderWidth * max( + minCompressionFactor, + 1f - min(1f - minCompressionFactor, abs(animatedOverscroll) * compressionMultiplier) + ) + + val dynamicCornerRadius = baseCornerRadius * (1f - min(cornerRadiusChangeFactor, abs(animatedOverscroll) * cornerRadiusChangeFactor * 2f)) + + Box( + modifier = modifier, + contentAlignment = Alignment.Center + ) { + Box( + modifier = Modifier + .height(heightStretch) + .width(widthCompression) + .offset(y = offsetY) + .clip(RoundedCornerShape(dynamicCornerRadius)) + .background(trackColor) + .pointerInput(Unit) { + detectTapGestures { offset -> + val newFraction = 1f - (offset.y / size.height).coerceIn(0f, 1f) + dragFraction = newFraction + rawDragPosition = newFraction + overscrollAmount = 0f + + val newVolume = (newFraction * maxVolume).roundToInt() + onVolumeChange(newVolume) + } + } + .draggable( + orientation = Orientation.Vertical, + state = rememberDraggableState { delta -> + rawDragPosition -= (delta / baseHeightPx) + + dragFraction = rawDragPosition.coerceIn(0f, 1f) + + overscrollAmount = when { + rawDragPosition > 1f -> min(1.0f, (rawDragPosition - 1f) * 2.0f) + rawDragPosition < 0f -> max(-1.0f, rawDragPosition * 2.0f) + else -> 0f + } + + val newVolume = (dragFraction * maxVolume).roundToInt() + onVolumeChange(newVolume) + }, + onDragStarted = { + isDragging = true + dragFraction = displayFraction + rawDragPosition = displayFraction + overscrollAmount = 0f + onDragStateChange(true) + }, + onDragStopped = { + isDragging = false + overscrollAmount = 0f + rawDragPosition = dragFraction + onDragStateChange(false) + } + ), + contentAlignment = Alignment.BottomCenter + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(animatedProgress) + .background(progressColor) + ) + } + } +} diff --git a/android/app/src/main/java/me/kavishdevar/aln/composables/VolumeControlSwitch.kt b/android/app/src/main/java/me/kavishdevar/librepods/composables/VolumeControlSwitch.kt similarity index 94% rename from android/app/src/main/java/me/kavishdevar/aln/composables/VolumeControlSwitch.kt rename to android/app/src/main/java/me/kavishdevar/librepods/composables/VolumeControlSwitch.kt index f668c0a..1acbef4 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/composables/VolumeControlSwitch.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/composables/VolumeControlSwitch.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.composables +package me.kavishdevar.librepods.composables import android.content.SharedPreferences import androidx.compose.foundation.background @@ -45,7 +45,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import me.kavishdevar.aln.services.AirPodsService +import me.kavishdevar.librepods.services.AirPodsService @Composable fun VolumeControlSwitch(service: AirPodsService, sharedPreferences: SharedPreferences) { diff --git a/android/app/src/main/java/me/kavishdevar/aln/receivers/BootReceiver.kt b/android/app/src/main/java/me/kavishdevar/librepods/receivers/BootReceiver.kt similarity index 85% rename from android/app/src/main/java/me/kavishdevar/aln/receivers/BootReceiver.kt rename to android/app/src/main/java/me/kavishdevar/librepods/receivers/BootReceiver.kt index cd2a20e..d7a1a23 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/receivers/BootReceiver.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/receivers/BootReceiver.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,12 +16,12 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.receivers +package me.kavishdevar.librepods.receivers import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import me.kavishdevar.aln.services.AirPodsService +import me.kavishdevar.librepods.services.AirPodsService class BootReceiver: BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { diff --git a/android/app/src/main/java/me/kavishdevar/aln/screens/AirPodsSettingsScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/AirPodsSettingsScreen.kt similarity index 92% rename from android/app/src/main/java/me/kavishdevar/aln/screens/AirPodsSettingsScreen.kt rename to android/app/src/main/java/me/kavishdevar/librepods/screens/AirPodsSettingsScreen.kt index 427acfa..cc6391a 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/screens/AirPodsSettingsScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/AirPodsSettingsScreen.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.screens +package me.kavishdevar.librepods.screens import android.annotation.SuppressLint import android.bluetooth.BluetoothDevice @@ -84,18 +84,18 @@ import dev.chrisbanes.haze.hazeChild import dev.chrisbanes.haze.materials.CupertinoMaterials import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi import kotlinx.coroutines.launch -import me.kavishdevar.aln.R -import me.kavishdevar.aln.composables.AccessibilitySettings -import me.kavishdevar.aln.composables.AudioSettings -import me.kavishdevar.aln.composables.BatteryView -import me.kavishdevar.aln.composables.IndependentToggle -import me.kavishdevar.aln.composables.NameField -import me.kavishdevar.aln.composables.NavigationButton -import me.kavishdevar.aln.composables.NoiseControlSettings -import me.kavishdevar.aln.composables.PressAndHoldSettings -import me.kavishdevar.aln.services.AirPodsService -import me.kavishdevar.aln.ui.theme.ALNTheme -import me.kavishdevar.aln.utils.AirPodsNotifications +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.composables.AccessibilitySettings +import me.kavishdevar.librepods.composables.AudioSettings +import me.kavishdevar.librepods.composables.BatteryView +import me.kavishdevar.librepods.composables.IndependentToggle +import me.kavishdevar.librepods.composables.NameField +import me.kavishdevar.librepods.composables.NavigationButton +import me.kavishdevar.librepods.composables.NoiseControlSettings +import me.kavishdevar.librepods.composables.PressAndHoldSettings +import me.kavishdevar.librepods.services.AirPodsService +import me.kavishdevar.librepods.ui.theme.LibrePodsTheme +import me.kavishdevar.librepods.utils.AirPodsNotifications @OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class) @SuppressLint("MissingPermission", "UnspecifiedRegisterReceiverFlag") @@ -147,11 +147,11 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService, val bluetoothReceiver = remember { object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { - if (intent?.action == "me.kavishdevar.aln.AIRPODS_CONNECTED_REMOTELY") { + if (intent?.action == "me.kavishdevar.librepods.AIRPODS_CONNECTED_REMOTELY") { coroutineScope.launch { handleRemoteConnection(true) } - } else if (intent?.action == "me.kavishdevar.aln.AIRPODS_DISCONNECTED_REMOTELY") { + } else if (intent?.action == "me.kavishdevar.librepods.AIRPODS_DISCONNECTED_REMOTELY") { coroutineScope.launch { handleRemoteConnection(false) } @@ -168,8 +168,8 @@ fun AirPodsSettingsScreen(dev: BluetoothDevice?, service: AirPodsService, DisposableEffect(Unit) { val filter = IntentFilter().apply { - addAction("me.kavishdevar.aln.AIRPODS_CONNECTED_REMOTELY") - addAction("me.kavishdevar.aln.AIRPODS_DISCONNECTED_REMOTELY") + addAction("me.kavishdevar.librepods.AIRPODS_CONNECTED_REMOTELY") + addAction("me.kavishdevar.librepods.AIRPODS_DISCONNECTED_REMOTELY") addAction(AirPodsNotifications.DISCONNECT_RECEIVERS) } @@ -399,7 +399,7 @@ fun AirPodsSettingsScreenPreview() { Column ( modifier = Modifier.height(2000.dp) ) { - ALNTheme ( + LibrePodsTheme ( darkTheme = true ) { AirPodsSettingsScreen(dev = null, service = AirPodsService(), navController = rememberNavController(), isConnected = true, isRemotelyConnected = false) diff --git a/android/app/src/main/java/me/kavishdevar/aln/screens/AppSettingsScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/AppSettingsScreen.kt similarity index 97% rename from android/app/src/main/java/me/kavishdevar/aln/screens/AppSettingsScreen.kt rename to android/app/src/main/java/me/kavishdevar/librepods/screens/AppSettingsScreen.kt index 82c6db8..67289af 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/screens/AppSettingsScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/AppSettingsScreen.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.screens +package me.kavishdevar.librepods.screens import android.content.Context import android.widget.Toast @@ -74,11 +74,11 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController -import me.kavishdevar.aln.R -import me.kavishdevar.aln.composables.IndependentToggle -import me.kavishdevar.aln.composables.StyledSwitch -import me.kavishdevar.aln.services.ServiceManager -import me.kavishdevar.aln.utils.RadareOffsetFinder +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.composables.IndependentToggle +import me.kavishdevar.librepods.composables.StyledSwitch +import me.kavishdevar.librepods.services.ServiceManager +import me.kavishdevar.librepods.utils.RadareOffsetFinder import kotlin.math.roundToInt @OptIn(ExperimentalMaterial3Api::class) @@ -419,7 +419,7 @@ fun AppSettingsScreen(navController: NavController) { confirmButton = { TextButton( onClick = { - if (RadareOffsetFinder.clearHookOffset()) { + if (RadareOffsetFinder.clearHookOffsets()) { Toast.makeText( context, "Hook offset has been reset. Redirecting to setup...", diff --git a/android/app/src/main/java/me/kavishdevar/aln/screens/DebugScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/DebugScreen.kt similarity index 98% rename from android/app/src/main/java/me/kavishdevar/aln/screens/DebugScreen.kt rename to android/app/src/main/java/me/kavishdevar/librepods/screens/DebugScreen.kt index 1f07802..c41fe84 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/screens/DebugScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/DebugScreen.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -18,7 +18,7 @@ @file:OptIn(ExperimentalHazeMaterialsApi::class) -package me.kavishdevar.aln.screens +package me.kavishdevar.librepods.screens import android.annotation.SuppressLint import android.content.ClipData @@ -99,11 +99,11 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.CoroutineScope -import me.kavishdevar.aln.R -import me.kavishdevar.aln.services.ServiceManager -import me.kavishdevar.aln.utils.BatteryStatus -import me.kavishdevar.aln.utils.isHeadTrackingData -import me.kavishdevar.aln.composables.StyledSwitch +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.services.ServiceManager +import me.kavishdevar.librepods.utils.BatteryStatus +import me.kavishdevar.librepods.utils.isHeadTrackingData +import me.kavishdevar.librepods.composables.StyledSwitch import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.imePadding import androidx.compose.ui.geometry.Offset diff --git a/android/app/src/main/java/me/kavishdevar/aln/screens/HeadTrackingScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/HeadTrackingScreen.kt similarity index 98% rename from android/app/src/main/java/me/kavishdevar/aln/screens/HeadTrackingScreen.kt rename to android/app/src/main/java/me/kavishdevar/librepods/screens/HeadTrackingScreen.kt index aedf267..b60666b 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/screens/HeadTrackingScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/HeadTrackingScreen.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.screens +package me.kavishdevar.librepods.screens import android.content.Context import android.os.Build @@ -99,10 +99,10 @@ import androidx.navigation.NavController import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import me.kavishdevar.aln.R -import me.kavishdevar.aln.composables.IndependentToggle -import me.kavishdevar.aln.services.ServiceManager -import me.kavishdevar.aln.utils.HeadTracking +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.composables.IndependentToggle +import me.kavishdevar.librepods.services.ServiceManager +import me.kavishdevar.librepods.utils.HeadTracking import kotlin.math.abs import kotlin.math.cos import kotlin.math.sin diff --git a/android/app/src/main/java/me/kavishdevar/aln/screens/Onboarding.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/Onboarding.kt similarity index 88% rename from android/app/src/main/java/me/kavishdevar/aln/screens/Onboarding.kt rename to android/app/src/main/java/me/kavishdevar/librepods/screens/Onboarding.kt index 28acc34..dc7a540 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/screens/Onboarding.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/Onboarding.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.screens +package me.kavishdevar.librepods.screens import android.content.Context import android.util.Log @@ -39,18 +39,24 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Clear +import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.Settings +import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -76,8 +82,8 @@ import androidx.navigation.NavController import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import me.kavishdevar.aln.R -import me.kavishdevar.aln.utils.RadareOffsetFinder +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.utils.RadareOffsetFinder @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -97,6 +103,9 @@ fun Onboarding(navController: NavController, activityContext: Context) { var moduleEnabled by remember { mutableStateOf(false) } var bluetoothToggled by remember { mutableStateOf(false) } + var showMenu by remember { mutableStateOf(false) } + var showSkipDialog by remember { mutableStateOf(false) } + fun checkRootAccess() { checkingRoot = true rootCheckFailed = false @@ -158,7 +167,29 @@ fun Onboarding(navController: NavController, activityContext: Context) { }, colors = TopAppBarDefaults.topAppBarColors( containerColor = Color.Transparent - ) + ), + actions = { + Box { + IconButton(onClick = { showMenu = true }) { + Icon( + imageVector = Icons.Default.MoreVert, + contentDescription = "More Options" + ) + } + DropdownMenu( + expanded = showMenu, + onDismissRequest = { showMenu = false } + ) { + DropdownMenuItem( + text = { Text("Skip Setup") }, + onClick = { + showMenu = false + showSkipDialog = true + } + ) + } + } + } ) }, containerColor = if (isDarkTheme) Color(0xFF000000) else Color(0xFFF2F2F7) @@ -477,6 +508,51 @@ fun Onboarding(navController: NavController, activityContext: Context) { } } } + + if (showSkipDialog) { + AlertDialog( + onDismissRequest = { showSkipDialog = false }, + title = { Text("Skip Setup") }, + text = { + Text( + "Have you installed the root module that patches the Bluetooth library directly? This option is for users who have manually patched their system instead of using the dynamic hook.", + style = TextStyle( + fontSize = 16.sp, + fontFamily = FontFamily(Font(R.font.sf_pro)) + ) + ) + }, + confirmButton = { + val sharedPreferences = activityContext.getSharedPreferences("settings", Context.MODE_PRIVATE) + TextButton( + onClick = { + showSkipDialog = false + RadareOffsetFinder.clearHookOffsets() + sharedPreferences.edit().putBoolean("skip_setup", true).apply() + navController.navigate("settings") { + popUpTo("onboarding") { inclusive = true } + } + } + ) { + Text( + "Yes, Skip Setup", + color = accentColor, + fontWeight = FontWeight.Bold + ) + } + }, + dismissButton = { + TextButton( + onClick = { showSkipDialog = false } + ) { + Text("Cancel") + } + }, + containerColor = backgroundColor, + textContentColor = textColor, + titleContentColor = textColor + ) + } } } @@ -565,7 +641,7 @@ private fun getStatusDescription( return when (state) { is RadareOffsetFinder.ProgressState.Success -> { when { - !moduleEnabled -> "Please enable the ALN Xposed module in your Xposed manager (e.g. LSPosed). If already enabled, disable and re-enable it." + !moduleEnabled -> "Please enable the LibrePods Xposed module in your Xposed manager (e.g. LSPosed). If already enabled, disable and re-enable it." !bluetoothToggled -> "Please turn off and then turn on Bluetooth to apply the changes." else -> "All set! You can now use your AirPods with enhanced functionality." } diff --git a/android/app/src/main/java/me/kavishdevar/aln/screens/PressAndHoldSettingsScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/PressAndHoldSettingsScreen.kt similarity index 98% rename from android/app/src/main/java/me/kavishdevar/aln/screens/PressAndHoldSettingsScreen.kt rename to android/app/src/main/java/me/kavishdevar/librepods/screens/PressAndHoldSettingsScreen.kt index 1f52113..78f92f1 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/screens/PressAndHoldSettingsScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/PressAndHoldSettingsScreen.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.screens +package me.kavishdevar.librepods.screens import android.content.Context import android.util.Log @@ -67,8 +67,8 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController -import me.kavishdevar.aln.R -import me.kavishdevar.aln.services.ServiceManager +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.services.ServiceManager @Composable() fun RightDivider() { diff --git a/android/app/src/main/java/me/kavishdevar/aln/screens/RenameScreen.kt b/android/app/src/main/java/me/kavishdevar/librepods/screens/RenameScreen.kt similarity index 96% rename from android/app/src/main/java/me/kavishdevar/aln/screens/RenameScreen.kt rename to android/app/src/main/java/me/kavishdevar/librepods/screens/RenameScreen.kt index def94bf..a5adb02 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/screens/RenameScreen.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/screens/RenameScreen.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.screens +package me.kavishdevar.librepods.screens import android.content.Context import androidx.compose.foundation.background @@ -64,8 +64,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController -import me.kavishdevar.aln.R -import me.kavishdevar.aln.services.ServiceManager +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.services.ServiceManager @OptIn(ExperimentalMaterial3Api::class) diff --git a/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsQSService.kt b/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsQSService.kt new file mode 100644 index 0000000..277a221 --- /dev/null +++ b/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsQSService.kt @@ -0,0 +1,263 @@ +/* + * LibrePods - AirPods liberated from Apple’s ecosystem + * + * Copyright (C) 2025 Kavish Devar + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package me.kavishdevar.librepods.services + +import android.annotation.SuppressLint +import android.app.PendingIntent +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.SharedPreferences +import android.graphics.drawable.Icon +import android.os.Build +import android.service.quicksettings.Tile +import android.service.quicksettings.TileService +import android.util.Log +import androidx.annotation.RequiresApi +import me.kavishdevar.librepods.QuickSettingsDialogActivity +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.utils.AirPodsNotifications +import me.kavishdevar.librepods.utils.NoiseControlMode + +@RequiresApi(Build.VERSION_CODES.Q) +class AirPodsQSService : TileService() { + + private lateinit var sharedPreferences: SharedPreferences + private var currentAncMode: Int = NoiseControlMode.OFF.ordinal + 1 + private var isAirPodsConnected: Boolean = false + + private val ancStatusReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action == AirPodsNotifications.ANC_DATA) { + val newMode = intent.getIntExtra("data", NoiseControlMode.OFF.ordinal + 1) + Log.d("AirPodsQSService", "Received ANC update: $newMode") + currentAncMode = newMode + updateTile() + } + } + } + + private val availabilityReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + when (intent.action) { + AirPodsNotifications.AIRPODS_CONNECTED -> { + Log.d("AirPodsQSService", "Received AIRPODS_CONNECTED") + isAirPodsConnected = true + currentAncMode = + ServiceManager.getService()?.getANC() ?: (NoiseControlMode.OFF.ordinal + 1) + updateTile() + } + AirPodsNotifications.AIRPODS_DISCONNECTED -> { + Log.d("AirPodsQSService", "Received AIRPODS_DISCONNECTED") + isAirPodsConnected = false + updateTile() + } + } + } + } + + private val preferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { _, key -> + if (key == "off_listening_mode") { + Log.d("AirPodsQSService", "Preference changed: $key") + if (currentAncMode == NoiseControlMode.OFF.ordinal + 1 && !isOffModeEnabled()) { + currentAncMode = NoiseControlMode.TRANSPARENCY.ordinal + 1 + } + updateTile() + } + } + + override fun onCreate() { + super.onCreate() + sharedPreferences = getSharedPreferences("settings", MODE_PRIVATE) + } + + @SuppressLint("InlinedApi", "UnspecifiedRegisterReceiverFlag") + override fun onStartListening() { + super.onStartListening() + Log.d("AirPodsQSService", "onStartListening") + + val service = ServiceManager.getService() + isAirPodsConnected = service?.isConnectedLocally == true + currentAncMode = service?.getANC() ?: (NoiseControlMode.OFF.ordinal + 1) + + if (currentAncMode == NoiseControlMode.OFF.ordinal + 1 && !isOffModeEnabled()) { + currentAncMode = NoiseControlMode.TRANSPARENCY.ordinal + 1 + } + + val ancIntentFilter = IntentFilter(AirPodsNotifications.ANC_DATA) + val availabilityIntentFilter = IntentFilter().apply { + addAction(AirPodsNotifications.AIRPODS_CONNECTED) + addAction(AirPodsNotifications.AIRPODS_DISCONNECTED) + } + + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + registerReceiver(ancStatusReceiver, ancIntentFilter, RECEIVER_EXPORTED) + registerReceiver(availabilityReceiver, availabilityIntentFilter, RECEIVER_EXPORTED) + } else { + registerReceiver(ancStatusReceiver, ancIntentFilter) + registerReceiver(availabilityReceiver, availabilityIntentFilter) + } + sharedPreferences.registerOnSharedPreferenceChangeListener(preferenceChangeListener) + Log.d("AirPodsQSService", "Receivers registered") + } catch (e: Exception) { + Log.e("AirPodsQSService", "Error registering receivers: $e") + } + + updateTile() + } + + override fun onStopListening() { + super.onStopListening() + Log.d("AirPodsQSService", "onStopListening") + try { + unregisterReceiver(ancStatusReceiver) + unregisterReceiver(availabilityReceiver) + sharedPreferences.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener) + Log.d("AirPodsQSService", "Receivers unregistered") + } catch (e: IllegalArgumentException) { + Log.e("AirPodsQSService", "Receiver not registered or already unregistered: $e") + } catch (e: Exception) { + Log.e("AirPodsQSService", "Error unregistering receivers: $e") + } + } + + override fun onClick() { + super.onClick() + Log.d("AirPodsQSService", "onClick - Current state: $isAirPodsConnected, Current mode: $currentAncMode") + if (!isAirPodsConnected) { + Log.d("AirPodsQSService", "Tile clicked but AirPods not connected.") + return + } + + val clickBehavior = sharedPreferences.getString("qs_click_behavior", "dialog") ?: "dialog" + + if (clickBehavior == "dialog") { + launchDialogActivity() + } else { + cycleAncMode() + } + } + + private fun launchDialogActivity() { + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + val pendingIntent = PendingIntent.getActivity( + this, + 0, + Intent(this, QuickSettingsDialogActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + }, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + ) + startActivityAndCollapse(pendingIntent) + } else { + @Suppress("DEPRECATION") + val intent = Intent(this, QuickSettingsDialogActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + } + startActivityAndCollapse(intent) + } + Log.d("AirPodsQSService", "Called startActivityAndCollapse for QuickSettingsDialogActivity") + } catch (e: Exception) { + Log.e("AirPodsQSService", "Error launching QuickSettingsDialogActivity: $e") + } + } + + private fun cycleAncMode() { + val service = ServiceManager.getService() + if (service == null) { + Log.d("AirPodsQSService", "Tile clicked (cycle mode) but service is null.") + return + } + val nextMode = getNextAncMode() + Log.d("AirPodsQSService", "Cycling ANC mode to: $nextMode") + service.setANCMode(nextMode) + } + + private fun updateTile() { + val tile = qsTile ?: return + Log.d("AirPodsQSService", "updateTile - Connected: $isAirPodsConnected, Mode: $currentAncMode") + + val deviceName = sharedPreferences.getString("name", "AirPods") ?: "AirPods" + + if (isAirPodsConnected) { + tile.state = Tile.STATE_ACTIVE + tile.label = getModeLabel(currentAncMode) + tile.subtitle = deviceName + tile.icon = Icon.createWithResource(this, getModeIcon(currentAncMode)) + } else { + tile.state = Tile.STATE_UNAVAILABLE + tile.label = "AirPods" + tile.subtitle = "Disconnected" + tile.icon = Icon.createWithResource(this, R.drawable.airpods) + } + + try { + tile.updateTile() + Log.d("AirPodsQSService", "Tile updated successfully") + } catch (e: Exception) { + Log.e("AirPodsQSService", "Error updating tile: $e") + } + } + + private fun isOffModeEnabled(): Boolean { + return sharedPreferences.getBoolean("off_listening_mode", true) + } + + private fun getAvailableModes(): List { + val modes = mutableListOf( + NoiseControlMode.TRANSPARENCY.ordinal + 1, + NoiseControlMode.ADAPTIVE.ordinal + 1, + NoiseControlMode.NOISE_CANCELLATION.ordinal + 1 + ) + if (isOffModeEnabled()) { + modes.add(0, NoiseControlMode.OFF.ordinal + 1) + } + return modes + } + + private fun getNextAncMode(): Int { + val availableModes = getAvailableModes() + val currentIndex = availableModes.indexOf(currentAncMode) + val nextIndex = (currentIndex + 1) % availableModes.size + return availableModes[nextIndex] + } + + private fun getModeLabel(mode: Int): String { + return when (mode) { + NoiseControlMode.OFF.ordinal + 1 -> "Off" + NoiseControlMode.TRANSPARENCY.ordinal + 1 -> "Transparency" + NoiseControlMode.ADAPTIVE.ordinal + 1 -> "Adaptive" + NoiseControlMode.NOISE_CANCELLATION.ordinal + 1 -> "Noise Cancellation" + else -> "Unknown" + } + } + + private fun getModeIcon(mode: Int): Int { + return when (mode) { + NoiseControlMode.OFF.ordinal + 1 -> R.drawable.noise_cancellation + NoiseControlMode.TRANSPARENCY.ordinal + 1 -> R.drawable.transparency + NoiseControlMode.ADAPTIVE.ordinal + 1 -> R.drawable.adaptive + NoiseControlMode.NOISE_CANCELLATION.ordinal + 1 -> R.drawable.noise_cancellation + else -> R.drawable.airpods + } + } +} diff --git a/android/app/src/main/java/me/kavishdevar/aln/services/AirPodsService.kt b/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt similarity index 97% rename from android/app/src/main/java/me/kavishdevar/aln/services/AirPodsService.kt rename to android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt index 171d3d0..6102a6e 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/services/AirPodsService.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/services/AirPodsService.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 Kavish Devar * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.services +package me.kavishdevar.librepods.services import android.Manifest import android.annotation.SuppressLint @@ -46,6 +46,7 @@ import android.os.Handler import android.os.IBinder import android.os.Looper import android.os.ParcelUuid +import android.provider.Settings import android.telecom.TelecomManager import android.telephony.PhoneStateListener import android.telephony.TelephonyManager @@ -67,26 +68,26 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine -import me.kavishdevar.aln.MainActivity -import me.kavishdevar.aln.R -import me.kavishdevar.aln.utils.AirPodsNotifications -import me.kavishdevar.aln.utils.Battery -import me.kavishdevar.aln.utils.BatteryComponent -import me.kavishdevar.aln.utils.BatteryStatus -import me.kavishdevar.aln.utils.CrossDevice -import me.kavishdevar.aln.utils.CrossDevicePackets -import me.kavishdevar.aln.utils.Enums -import me.kavishdevar.aln.utils.GestureDetector -import me.kavishdevar.aln.utils.HeadTracking -import me.kavishdevar.aln.utils.IslandType -import me.kavishdevar.aln.utils.IslandWindow -import me.kavishdevar.aln.utils.LongPressPackets -import me.kavishdevar.aln.utils.MediaController -import me.kavishdevar.aln.utils.PopupWindow -import me.kavishdevar.aln.utils.RadareOffsetFinder -import me.kavishdevar.aln.utils.isHeadTrackingData -import me.kavishdevar.aln.widgets.BatteryWidget -import me.kavishdevar.aln.widgets.NoiseControlWidget +import me.kavishdevar.librepods.MainActivity +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.utils.AirPodsNotifications +import me.kavishdevar.librepods.utils.Battery +import me.kavishdevar.librepods.utils.BatteryComponent +import me.kavishdevar.librepods.utils.BatteryStatus +import me.kavishdevar.librepods.utils.CrossDevice +import me.kavishdevar.librepods.utils.CrossDevicePackets +import me.kavishdevar.librepods.utils.Enums +import me.kavishdevar.librepods.utils.GestureDetector +import me.kavishdevar.librepods.utils.HeadTracking +import me.kavishdevar.librepods.utils.IslandType +import me.kavishdevar.librepods.utils.IslandWindow +import me.kavishdevar.librepods.utils.LongPressPackets +import me.kavishdevar.librepods.utils.MediaController +import me.kavishdevar.librepods.utils.PopupWindow +import me.kavishdevar.librepods.utils.RadareOffsetFinder +import me.kavishdevar.librepods.utils.isHeadTrackingData +import me.kavishdevar.librepods.widgets.BatteryWidget +import me.kavishdevar.librepods.widgets.NoiseControlWidget import org.lsposed.hiddenapibypass.HiddenApiBypass import java.nio.ByteBuffer import java.nio.ByteOrder @@ -142,7 +143,7 @@ class AirPodsService : Service() { override fun onCreate() { super.onCreate() sharedPreferencesLogs = getSharedPreferences("packet_logs", MODE_PRIVATE) - + inMemoryLogs.addAll(sharedPreferencesLogs.getStringSet(packetLogKey, emptySet()) ?: emptySet()) _packetLogsFlow.value = inMemoryLogs.toSet() } @@ -150,7 +151,7 @@ class AirPodsService : Service() { private fun logPacket(packet: ByteArray, source: String) { val packetHex = packet.joinToString(" ") { "%02X".format(it) } val logEntry = "$source: $packetHex" - + synchronized(inMemoryLogs) { inMemoryLogs.add(logEntry) if (inMemoryLogs.size > maxLogEntries) { @@ -158,16 +159,15 @@ class AirPodsService : Service() { inMemoryLogs.remove(it) } } - + _packetLogsFlow.value = inMemoryLogs.toSet() } - - // Save to SharedPreferences less frequently - only needed for persistence between sessions + CoroutineScope(Dispatchers.IO).launch { val logs = sharedPreferencesLogs.getStringSet(packetLogKey, mutableSetOf())?.toMutableSet() ?: mutableSetOf() logs.add(logEntry) - // Limit SharedPreferences size + if (logs.size > maxLogEntries) { val toKeep = logs.toList().takeLast(maxLogEntries).toSet() sharedPreferencesLogs.edit { putStringSet(packetLogKey, toKeep) } @@ -213,6 +213,10 @@ class AirPodsService : Service() { var popupShown = false fun showPopup(service: Service, name: String) { + if (!Settings.canDrawOverlays(service)) { + Log.d("AirPodsService", "No permission for SYSTEM_ALERT_WINDOW") + return + } if (popupShown) { return } @@ -225,6 +229,10 @@ class AirPodsService : Service() { @SuppressLint("MissingPermission") fun showIsland(service: Service, batteryPercentage: Int, type: IslandType = IslandType.CONNECTED) { Log.d("AirPodsService", "Showing island window") + if (!Settings.canDrawOverlays(service)) { + Log.d("AirPodsService", "No permission for SYSTEM_ALERT_WINDOW") + return + } CoroutineScope(Dispatchers.Main).launch { islandWindow = IslandWindow(service.applicationContext) islandWindow!!.show(sharedPreferences.getString("name", "AirPods Pro").toString(), batteryPercentage, this@AirPodsService, type) @@ -791,7 +799,7 @@ class AirPodsService : Service() { } val showIslandReceiver = object: BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { - if (intent?.action == "me.kavishdevar.aln.cross_device_island") { + if (intent?.action == "me.kavishdevar.librepods.cross_device_island") { showIsland(this@AirPodsService, batteryNotification.getBattery().find { it.component == BatteryComponent.LEFT}?.level!!.coerceAtMost(batteryNotification.getBattery().find { it.component == BatteryComponent.RIGHT}?.level!!)) } else if (intent?.action == AirPodsNotifications.Companion.DISCONNECT_RECEIVERS) { try { @@ -804,7 +812,7 @@ class AirPodsService : Service() { } val showIslandIntentFilter = IntentFilter().apply { - addAction("me.kavishdevar.aln.cross_device_island") + addAction("me.kavishdevar.librepods.cross_device_island") addAction(AirPodsNotifications.DISCONNECT_RECEIVERS) } @@ -1266,7 +1274,7 @@ class AirPodsService : Service() { val fromHex = packet.split(" ").map { it.toInt(16).toByte() } try { logPacket(fromHex.toByteArray(), "Sent") - + if (!isConnectedLocally && CrossDevice.isAvailable) { CrossDevice.sendRemotePacket(CrossDevicePackets.AIRPODS_DATA_HEADER.packet + fromHex.toByteArray()) return @@ -1285,9 +1293,8 @@ class AirPodsService : Service() { fun sendPacket(packet: ByteArray) { try { - // Always log the packet logPacket(packet, "Sent") - + if (!isConnectedLocally && CrossDevice.isAvailable) { CrossDevice.sendRemotePacket(CrossDevicePackets.AIRPODS_DATA_HEADER.packet + packet) return diff --git a/android/app/src/main/java/me/kavishdevar/aln/ui/theme/Color.kt b/android/app/src/main/java/me/kavishdevar/librepods/ui/theme/Color.kt similarity index 82% rename from android/app/src/main/java/me/kavishdevar/aln/ui/theme/Color.kt rename to android/app/src/main/java/me/kavishdevar/librepods/ui/theme/Color.kt index bfe3985..662186e 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/ui/theme/Color.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/ui/theme/Color.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -17,7 +17,7 @@ */ -package me.kavishdevar.aln.ui.theme +package me.kavishdevar.librepods.ui.theme import androidx.compose.ui.graphics.Color diff --git a/android/app/src/main/java/me/kavishdevar/aln/ui/theme/Theme.kt b/android/app/src/main/java/me/kavishdevar/librepods/ui/theme/Theme.kt similarity index 78% rename from android/app/src/main/java/me/kavishdevar/aln/ui/theme/Theme.kt rename to android/app/src/main/java/me/kavishdevar/librepods/ui/theme/Theme.kt index b0d66da..31e4f12 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/ui/theme/Theme.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/ui/theme/Theme.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.ui.theme +package me.kavishdevar.librepods.ui.theme import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme @@ -38,21 +38,11 @@ private val LightColorScheme = lightColorScheme( primary = Purple40, secondary = PurpleGrey40, tertiary = Pink40 - /* Other default colors to override - background = Color(0xFFFFFBFE), - surface = Color(0xFFFFFBFE), - onPrimary = Color.White, - onSecondary = Color.White, - onTertiary = Color.White, - onBackground = Color(0xFF1C1B1F), - onSurface = Color(0xFF1C1B1F), - */ ) @Composable -fun ALNTheme( +fun LibrePodsTheme( darkTheme: Boolean = isSystemInDarkTheme(), - // Dynamic color is available on Android 12+ dynamicColor: Boolean = true, content: @Composable () -> Unit ) { diff --git a/android/app/src/main/java/me/kavishdevar/aln/ui/theme/Type.kt b/android/app/src/main/java/me/kavishdevar/librepods/ui/theme/Type.kt similarity index 89% rename from android/app/src/main/java/me/kavishdevar/aln/ui/theme/Type.kt rename to android/app/src/main/java/me/kavishdevar/librepods/ui/theme/Type.kt index 5d17c6e..79a5218 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/ui/theme/Type.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/ui/theme/Type.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.ui.theme +package me.kavishdevar.librepods.ui.theme import androidx.compose.material3.Typography import androidx.compose.ui.text.TextStyle diff --git a/android/app/src/main/java/me/kavishdevar/aln/utils/CrossDevice.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/CrossDevice.kt similarity index 96% rename from android/app/src/main/java/me/kavishdevar/aln/utils/CrossDevice.kt rename to android/app/src/main/java/me/kavishdevar/librepods/utils/CrossDevice.kt index 1a7a421..bfbc611 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/utils/CrossDevice.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/utils/CrossDevice.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -17,7 +17,7 @@ */ -package me.kavishdevar.aln.utils +package me.kavishdevar.librepods.utils import android.annotation.SuppressLint import android.bluetooth.BluetoothAdapter @@ -37,7 +37,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import me.kavishdevar.aln.services.ServiceManager +import me.kavishdevar.librepods.services.ServiceManager import java.io.IOException import java.util.UUID @@ -77,7 +77,7 @@ object CrossDevice { sharedPreferences.edit().putBoolean("CrossDeviceIsAvailable", false).apply() this@CrossDevice.bluetoothAdapter = context.getSystemService(BluetoothManager::class.java).adapter this@CrossDevice.bluetoothLeAdvertiser = bluetoothAdapter.bluetoothLeAdvertiser - startAdvertising() + // startAdvertising() startServer() initialized = true } @@ -255,7 +255,7 @@ object CrossDevice { ) if (earDetectionStatus == listOf(false, false) && newEarDetectionStatus.contains(true)) { ServiceManager.getService()?.applicationContext?.sendBroadcast( - Intent("me.kavishdevar.aln.cross_device_island") + Intent("me.kavishdevar.librepods.cross_device_island") ) } earDetectionStatus = newEarDetectionStatus @@ -276,11 +276,11 @@ object CrossDevice { } fun notifyAirPodsConnectedRemotely(context: Context) { - val intent = Intent("me.kavishdevar.aln.AIRPODS_CONNECTED_REMOTELY") + val intent = Intent("me.kavishdevar.librepods.AIRPODS_CONNECTED_REMOTELY") context.sendBroadcast(intent) } fun notifyAirPodsDisconnectedRemotely(context: Context) { - val intent = Intent("me.kavishdevar.aln.AIRPODS_DISCONNECTED_REMOTELY") + val intent = Intent("me.kavishdevar.librepods.AIRPODS_DISCONNECTED_REMOTELY") context.sendBroadcast(intent) } } diff --git a/android/app/src/main/java/me/kavishdevar/aln/utils/GestureDetector.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/GestureDetector.kt similarity index 98% rename from android/app/src/main/java/me/kavishdevar/aln/utils/GestureDetector.kt rename to android/app/src/main/java/me/kavishdevar/librepods/utils/GestureDetector.kt index 4bb340f..d0b5dc4 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/utils/GestureDetector.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/utils/GestureDetector.kt @@ -1,4 +1,4 @@ -package me.kavishdevar.aln.utils +package me.kavishdevar.librepods.utils import android.os.Build import android.util.Log @@ -9,8 +9,8 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import me.kavishdevar.aln.services.AirPodsService -import me.kavishdevar.aln.services.ServiceManager +import me.kavishdevar.librepods.services.AirPodsService +import me.kavishdevar.librepods.services.ServiceManager import java.util.Collections import java.util.concurrent.CopyOnWriteArrayList import kotlin.math.abs diff --git a/android/app/src/main/java/me/kavishdevar/aln/utils/GestureFeedback.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/GestureFeedback.kt similarity index 99% rename from android/app/src/main/java/me/kavishdevar/aln/utils/GestureFeedback.kt rename to android/app/src/main/java/me/kavishdevar/librepods/utils/GestureFeedback.kt index 0a32d7d..a6f39ba 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/utils/GestureFeedback.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/utils/GestureFeedback.kt @@ -1,6 +1,6 @@ @file:Suppress("PrivatePropertyName") -package me.kavishdevar.aln.utils +package me.kavishdevar.librepods.utils import android.content.Context import android.media.AudioAttributes @@ -12,7 +12,7 @@ import android.os.Build import android.os.SystemClock import android.util.Log import androidx.annotation.RequiresApi -import me.kavishdevar.aln.R +import me.kavishdevar.librepods.R import java.util.concurrent.atomic.AtomicBoolean @RequiresApi(Build.VERSION_CODES.Q) diff --git a/android/app/src/main/java/me/kavishdevar/aln/utils/HeadOrientation.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/HeadOrientation.kt similarity index 98% rename from android/app/src/main/java/me/kavishdevar/aln/utils/HeadOrientation.kt rename to android/app/src/main/java/me/kavishdevar/librepods/utils/HeadOrientation.kt index b266777..d2a9e87 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/utils/HeadOrientation.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/utils/HeadOrientation.kt @@ -1,4 +1,4 @@ -package me.kavishdevar.aln.utils +package me.kavishdevar.librepods.utils import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow diff --git a/android/app/src/main/java/me/kavishdevar/aln/utils/IslandWindow.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/IslandWindow.kt similarity index 95% rename from android/app/src/main/java/me/kavishdevar/aln/utils/IslandWindow.kt rename to android/app/src/main/java/me/kavishdevar/librepods/utils/IslandWindow.kt index e696277..78b1272 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/utils/IslandWindow.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/utils/IslandWindow.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.utils +package me.kavishdevar.librepods.utils import android.animation.Animator import android.animation.AnimatorListenerAdapter @@ -39,8 +39,8 @@ import android.widget.ProgressBar import android.widget.TextView import android.widget.VideoView import androidx.core.content.ContextCompat.getString -import me.kavishdevar.aln.R -import me.kavishdevar.aln.services.ServiceManager +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.services.ServiceManager enum class IslandType { CONNECTED, @@ -106,7 +106,7 @@ class IslandWindow(context: Context) { batteryProgressBar.isIndeterminate = false val videoView = islandView.findViewById(R.id.island_video_view) - val videoUri = Uri.parse("android.resource://me.kavishdevar.aln/${R.raw.island}") + val videoUri = Uri.parse("android.resource://me.kavishdevar.librepods/${R.raw.island}") videoView.setVideoURI(videoUri) videoView.setOnPreparedListener { mediaPlayer -> mediaPlayer.isLooping = true diff --git a/android/app/src/main/java/me/kavishdevar/librepods/utils/KotlinModule.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/KotlinModule.kt new file mode 100644 index 0000000..78221c3 --- /dev/null +++ b/android/app/src/main/java/me/kavishdevar/librepods/utils/KotlinModule.kt @@ -0,0 +1,793 @@ +package me.kavishdevar.librepods.utils + +import android.content.Context +import android.content.Intent +import android.content.pm.ApplicationInfo +import android.content.res.ColorStateList +import android.graphics.Color +import android.graphics.drawable.GradientDrawable +import android.os.ParcelUuid +import android.util.Log +import android.view.Gravity +import android.view.View +import android.view.ViewGroup +import android.view.animation.AccelerateInterpolator +import android.view.animation.DecelerateInterpolator +import android.widget.FrameLayout +import android.widget.ImageButton +import android.widget.ImageView +import android.widget.LinearLayout +import io.github.libxposed.api.XposedInterface +import io.github.libxposed.api.XposedInterface.AfterHookCallback +import io.github.libxposed.api.XposedModule +import io.github.libxposed.api.XposedModuleInterface +import io.github.libxposed.api.XposedModuleInterface.ModuleLoadedParam +import io.github.libxposed.api.annotations.AfterInvocation +import io.github.libxposed.api.annotations.XposedHooker + +private const val TAG = "AirPodsHook" +private lateinit var module: KotlinModule + +class KotlinModule(base: XposedInterface, param: ModuleLoadedParam): XposedModule(base, param) { + init { + Log.i(TAG, "AirPodsHook module initialized at :: ${param.processName}") + module = this + } + + override fun onPackageLoaded(param: XposedModuleInterface.PackageLoadedParam) { + super.onPackageLoaded(param) + Log.i(TAG, "onPackageLoaded :: ${param.packageName}") + + if (param.packageName == "com.google.android.bluetooth" || param.packageName == "com.android.bluetooth") { + Log.i(TAG, "Bluetooth app detected, hooking l2c_fcr_chk_chan_modes") + + try { + if (param.isFirstPackage) { + Log.i(TAG, "Loading native library for Bluetooth hook") + System.loadLibrary("l2c_fcr_hook") + Log.i(TAG, "Native library loaded successfully") + } + } catch (e: Exception) { + Log.e(TAG, "Failed to load native library: ${e.message}", e) + } + } + + if (param.packageName == "com.android.settings") { + Log.i(TAG, "Settings app detected, hooking Bluetooth icon handling") + try { + val headerControllerClass = param.classLoader.loadClass( + "com.android.settings.bluetooth.AdvancedBluetoothDetailsHeaderController") + + val updateIconMethod = headerControllerClass.getDeclaredMethod( + "updateIcon", + android.widget.ImageView::class.java, + String::class.java) + + hook(updateIconMethod, BluetoothIconHooker::class.java) + Log.i(TAG, "Successfully hooked updateIcon method in Bluetooth settings") + + try { + val displayPreferenceMethod = headerControllerClass.getDeclaredMethod( + "displayPreference", + param.classLoader.loadClass("androidx.preference.PreferenceScreen")) + + hook(displayPreferenceMethod, BluetoothSettingsAirPodsHooker::class.java) + Log.i(TAG, "Successfully hooked displayPreference for AirPods button injection") + } catch (e: Exception) { + Log.e(TAG, "Failed to hook displayPreference: ${e.message}", e) + } + } catch (e: Exception) { + Log.e(TAG, "Failed to hook Bluetooth icon handler: ${e.message}", e) + } + } + + if (param.packageName == "com.android.systemui") { + Log.i(TAG, "SystemUI detected, hooking volume panel") + try { + val volumePanelViewClass = param.classLoader.loadClass("com.android.systemui.volume.VolumeDialogImpl") + + try { + val initDialogMethod = volumePanelViewClass.getDeclaredMethod("initDialog", Int::class.java) + hook(initDialogMethod, VolumeDialogInitHooker::class.java) + Log.i(TAG, "Hooked initDialog method successfully") + } catch (e: Exception) { + Log.e(TAG, "Failed to hook initDialog method: ${e.message}") + } + + try { + val showHMethod = volumePanelViewClass.getDeclaredMethod("showH", Int::class.java, Boolean::class.java, Int::class.java) + hook(showHMethod, VolumeDialogShowHooker::class.java) + Log.i(TAG, "Hooked showH method successfully") + } catch (e: Exception) { + Log.e(TAG, "Failed to hook showH method: ${e.message}") + } + + Log.i(TAG, "Volume panel hook setup attempted on multiple methods") + } catch (e: Exception) { + Log.e(TAG, "Failed to hook volume panel: ${e.message}", e) + } + } + } + + @XposedHooker + class VolumeDialogInitHooker : XposedInterface.Hooker { + companion object { + @JvmStatic + @AfterInvocation + fun afterInitDialog(callback: AfterHookCallback) { + try { + val volumeDialog = callback.thisObject + Log.i(TAG, "Volume dialog initialized, adding AirPods controls") + addAirPodsControlsToDialog(volumeDialog!!) + } catch (e: Exception) { + Log.e(TAG, "Error in initDialog hook: ${e.message}", e) + } + } + } + } + + @XposedHooker + class VolumeDialogShowHooker : XposedInterface.Hooker { + companion object { + @JvmStatic + @AfterInvocation + fun afterShowH(callback: AfterHookCallback) { + try { + val volumeDialog = callback.thisObject + Log.i(TAG, "Volume dialog shown, ensuring AirPods controls are added") + addAirPodsControlsToDialog(volumeDialog!!) + } catch (e: Exception) { + Log.e(TAG, "Error in showH hook: ${e.message}", e) + } + } + } + } + + @XposedHooker + class BluetoothSettingsAirPodsHooker : XposedInterface.Hooker { + companion object { + private const val AIRPODS_UUID = "74ec2172-0bad-4d01-8f77-997b2be0722a" + private const val LIBREPODS_PREFERENCE_KEY = "librepods_open_preference" + private const val ACTION_SET_ANC_MODE = "me.kavishdevar.librepods.SET_ANC_MODE" + private const val EXTRA_ANC_MODE = "anc_mode" + + private const val ANC_MODE_OFF = 1 + private const val ANC_MODE_NOISE_CANCELLATION = 2 + private const val ANC_MODE_TRANSPARENCY = 3 + private const val ANC_MODE_ADAPTIVE = 4 + + private var currentAncMode = ANC_MODE_NOISE_CANCELLATION + + @JvmStatic + @AfterInvocation + fun afterDisplayPreference(callback: AfterHookCallback) { + try { + val controller = callback.thisObject!! + val preferenceScreen = callback.args[0]!! + + val context = preferenceScreen.javaClass.getMethod("getContext").invoke(preferenceScreen) as Context + + val deviceField = controller.javaClass.getDeclaredField("mCachedDevice") + deviceField.isAccessible = true + val cachedDevice = deviceField.get(controller) ?: return + + val getDeviceMethod = cachedDevice.javaClass.getMethod("getDevice") + val bluetoothDevice = getDeviceMethod.invoke(cachedDevice) ?: return + + val uuidsMethod = bluetoothDevice.javaClass.getMethod("getUuids") + val uuids = uuidsMethod.invoke(bluetoothDevice) as? Array + + if (uuids != null) { + val isAirPods = uuids.any { it.uuid.toString() == AIRPODS_UUID } + + if (isAirPods) { + Log.i(TAG, "AirPods device detected in settings, injecting controls") + + val findPreferenceMethod = preferenceScreen.javaClass.getMethod("findPreference", CharSequence::class.java) + val existingPref = findPreferenceMethod.invoke(preferenceScreen, LIBREPODS_PREFERENCE_KEY) + + if (existingPref != null) { + Log.i(TAG, "LIBREPODS button already exists, skipping") + return + } + + val preferenceClass = preferenceScreen.javaClass.classLoader.loadClass("androidx.preference.Preference") + val preference = preferenceClass.getConstructor(Context::class.java).newInstance(context) + + val setKeyMethod = preferenceClass.getMethod("setKey", String::class.java) + setKeyMethod.invoke(preference, LIBREPODS_PREFERENCE_KEY) + + val setTitleMethod = preferenceClass.getMethod("setTitle", CharSequence::class.java) + setTitleMethod.invoke(preference, "Open LibrePods") + + val setSummaryMethod = preferenceClass.getMethod("setSummary", CharSequence::class.java) + setSummaryMethod.invoke(preference, "Control AirPods features") + + val setIconMethod = preferenceClass.getMethod("setIcon", Int::class.java) + setIconMethod.invoke(preference, android.R.drawable.ic_menu_manage) + + val setOrderMethod = preferenceClass.getMethod("setOrder", Int::class.java) + setOrderMethod.invoke(preference, 1000) + + val intent = Intent().apply { + setClassName("me.kavishdevar.librepods", "me.kavishdevar.librepods.MainActivity") + flags = Intent.FLAG_ACTIVITY_NEW_TASK + } + val setIntentMethod = preferenceClass.getMethod("setIntent", Intent::class.java) + setIntentMethod.invoke(preference, intent) + + val addPreferenceMethod = preferenceScreen.javaClass.getMethod("addPreference", preferenceClass) + addPreferenceMethod.invoke(preferenceScreen, preference) + + Log.i(TAG, "Successfully added Open LIBREPODS button to AirPods settings") + } + } + } catch (e: Exception) { + Log.e(TAG, "Error in BluetoothSettingsAirPodsHooker: ${e.message}", e) + e.printStackTrace() + } + } + } + } + + @XposedHooker + class BluetoothIconHooker : XposedInterface.Hooker { + companion object { + @JvmStatic + @AfterInvocation + fun afterUpdateIcon(callback: AfterHookCallback) { + Log.i(TAG, "BluetoothIconHooker called with args: ${callback.args.joinToString(", ")}") + try { + val imageView = callback.args[0] as ImageView + val iconUri = callback.args[1] as String + + val uri = android.net.Uri.parse(iconUri) + if (uri.toString().startsWith("android.resource://me.kavishdevar.librepods")) { + Log.i(TAG, "Handling AirPods icon URI: $uri") + + try { + val context = imageView.context + + android.os.Handler(android.os.Looper.getMainLooper()).post { + try { + val packageName = uri.authority + val packageContext = context.createPackageContext( + packageName, + Context.CONTEXT_IGNORE_SECURITY + ) + + val resPath = uri.pathSegments + if (resPath.size >= 2 && resPath[0] == "drawable") { + val resourceName = resPath[1] + val resourceId = packageContext.resources.getIdentifier( + resourceName, "drawable", packageName + ) + + if (resourceId != 0) { + val drawable = packageContext.resources.getDrawable( + resourceId, packageContext.theme + ) + + imageView.setImageDrawable(drawable) + imageView.alpha = 1.0f + + callback.result = null + + Log.i(TAG, "Successfully loaded icon from resource: $resourceName") + } else { + Log.e(TAG, "Resource not found: $resourceName") + } + } + } catch (e: Exception) { + Log.e(TAG, "Error loading resource from URI $uri: ${e.message}") + } + } + } catch (e: Exception) { + Log.e(TAG, "Error accessing context: ${e.message}") + } + } + } catch (e: Exception) { + Log.e(TAG, "Error in BluetoothIconHooker: ${e.message}") + e.printStackTrace() + } + } + } + } + + override fun getApplicationInfo(): ApplicationInfo { + return super.applicationInfo + } + + companion object { + private const val ANC_MODE_OFF = 1 + private const val ANC_MODE_NOISE_CANCELLATION = 2 + private const val ANC_MODE_TRANSPARENCY = 3 + private const val ANC_MODE_ADAPTIVE = 4 + + private var currentANCMode = ANC_MODE_NOISE_CANCELLATION + + private const val ACTION_SET_ANC_MODE = "me.kavishdevar.librepods.SET_ANC_MODE" + private const val EXTRA_ANC_MODE = "anc_mode" + private const val ANIMATION_DURATION = 250L + + private fun addAirPodsControlsToDialog(volumeDialog: Any) { + try { + val contextField = volumeDialog.javaClass.getDeclaredField("mContext") + contextField.isAccessible = true + val context = contextField.get(volumeDialog) as Context + + val dialogViewField = volumeDialog.javaClass.getDeclaredField("mDialogView") + dialogViewField.isAccessible = true + val dialogView = dialogViewField.get(volumeDialog) as ViewGroup + + val dialogRowsViewField = volumeDialog.javaClass.getDeclaredField("mDialogRowsView") + dialogRowsViewField.isAccessible = true + val dialogRowsView = dialogRowsViewField.get(volumeDialog) as ViewGroup + + Log.d(TAG, "Found dialogRowsView: ${dialogRowsView.javaClass.name}") + + val existingContainer = dialogView.findViewWithTag("airpods_container") + if (existingContainer != null) { + Log.d(TAG, "AirPods container already exists, ensuring visibility state") + val drawer = existingContainer.findViewWithTag("airpods_drawer_container") + drawer?.visibility = View.GONE + drawer?.alpha = 0f + drawer?.translationY = 0f + val button = existingContainer.findViewWithTag("airpods_button") + button?.visibility = View.VISIBLE + button?.alpha = 1f + if (button != null) { + updateMainButtonIcon(context, button, currentANCMode) + } + return + } + + val newAirPodsButton = ImageButton(context).apply { + tag = "airpods_button" + + try { + val airPodsPackage = context.createPackageContext( + "me.kavishdevar.librepods", + Context.CONTEXT_IGNORE_SECURITY + ) + val airPodsIconRes = airPodsPackage.resources.getIdentifier( + "airpods", "drawable", "me.kavishdevar.librepods") + + if (airPodsIconRes != 0) { + val airPodsDrawable = airPodsPackage.resources.getDrawable( + airPodsIconRes, airPodsPackage.theme) + setImageDrawable(airPodsDrawable) + } else { + setImageResource(android.R.drawable.ic_media_play) + Log.d(TAG, "Using fallback icon because airpods icon resource not found") + } + } catch (e: Exception) { + setImageResource(android.R.drawable.ic_media_play) + Log.e(TAG, "Failed to load AirPods icon: ${e.message}") + } + + val shape = GradientDrawable() + shape.shape = GradientDrawable.RECTANGLE + shape.setColor(Color.BLACK) + background = shape + + imageTintList = ColorStateList.valueOf(Color.WHITE) + scaleType = ImageView.ScaleType.CENTER_INSIDE + + setPadding(24, 24, 24, 24) + + val params = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 90 + ) + params.gravity = Gravity.CENTER + params.setMargins(0, 0, 0, 0) + layoutParams = params + + setOnClickListener { + Log.d(TAG, "AirPods button clicked, toggling drawer") + val container = findAirPodsContainer(this) + val drawerContainer = container?.findViewWithTag("airpods_drawer_container") + if (drawerContainer != null && container != null) { + if (drawerContainer.visibility == View.VISIBLE) { + hideAirPodsDrawer(container, this, drawerContainer) + } else { + showAirPodsDrawer(container, this, drawerContainer) + } + } else { + Log.e(TAG, "Could not find container or drawer for toggle") + } + } + + contentDescription = "AirPods Settings" + } + + val airPodsContainer = FrameLayout(context).apply { + layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ) + tag = "airpods_container" + } + + newAirPodsButton.setOnLongClickListener { + Log.d(TAG, "AirPods button long-pressed, opening QuickSettingsDialogActivity") + val intent = Intent().apply { + setClassName("me.kavishdevar.librepods", "me.kavishdevar.librepods.QuickSettingsDialogActivity") + flags = Intent.FLAG_ACTIVITY_NEW_TASK + } + context.startActivity(intent) + try { + val dismissMethod = volumeDialog.javaClass.getMethod("dismissH") + dismissMethod.invoke(volumeDialog) + } catch (e: Exception) { + Log.w(TAG, "Could not dismiss volume dialog: ${e.message}") + } + true + } + + val airPodsDrawer = LinearLayout(context).apply { + orientation = LinearLayout.VERTICAL + layoutParams = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.WRAP_CONTENT + ).apply { + gravity = Gravity.TOP + } + tag = "airpods_drawer_container" + visibility = View.GONE + alpha = 0f + + val drawerShape = GradientDrawable() + drawerShape.shape = GradientDrawable.RECTANGLE + drawerShape.setColor(Color.BLACK) + background = drawerShape + + setPadding(16, 8, 16, 8) + } + + val buttonContainer = LinearLayout(context).apply { + orientation = LinearLayout.VERTICAL + layoutParams = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.WRAP_CONTENT + ).apply { + gravity = Gravity.TOP + } + tag = "airpods_button_container" + } + + val modes = listOf(ANC_MODE_OFF, ANC_MODE_TRANSPARENCY, ANC_MODE_ADAPTIVE, ANC_MODE_NOISE_CANCELLATION) + for (mode in modes) { + val modeOption = createAncModeOption(context, mode, mode == currentANCMode, newAirPodsButton) + airPodsDrawer.addView(modeOption) + } + + buttonContainer.addView(newAirPodsButton) + + airPodsContainer.addView(airPodsDrawer) + airPodsContainer.addView(buttonContainer) + + val settingsViewField = try { + val field = volumeDialog.javaClass.getDeclaredField("mSettingsView") + field.isAccessible = true + field.get(volumeDialog) as? View + } catch (e: Exception) { + Log.e(TAG, "Failed to get settings view field: ${e.message}") + null + } + + if (settingsViewField != null && settingsViewField.parent is ViewGroup) { + val settingsParent = settingsViewField.parent as ViewGroup + val settingsIndex = findViewIndexInParent(settingsParent, settingsViewField) + + if (settingsIndex >= 0) { + settingsParent.addView(airPodsContainer, settingsIndex) + Log.i(TAG, "Added AirPods controls before settings button") + } else { + settingsParent.addView(airPodsContainer) + Log.i(TAG, "Added AirPods controls to the end of settings parent") + } + } else { + dialogView.addView(airPodsContainer) + Log.i(TAG, "Fallback: Added AirPods controls to dialog view") + } + + updateMainButtonIcon(context, newAirPodsButton, currentANCMode) + + Log.i(TAG, "Successfully added AirPods button and drawer to volume dialog") + } catch (e: Exception) { + Log.e(TAG, "Error adding AirPods button to volume panel: ${e.message}") + e.printStackTrace() + } + } + + private fun findViewIndexInParent(parent: ViewGroup, view: View): Int { + for (i in 0 until parent.childCount) { + if (parent.getChildAt(i) == view) { + return i + } + } + return -1 + } + + private fun updateMainButtonIcon(context: Context, button: ImageButton, mode: Int) { + try { + val pkgContext = context.createPackageContext( + "me.kavishdevar.librepods", + Context.CONTEXT_IGNORE_SECURITY + ) + + val resName = when (mode) { + ANC_MODE_OFF -> "noise_cancellation" + ANC_MODE_TRANSPARENCY -> "transparency" + ANC_MODE_ADAPTIVE -> "adaptive" + ANC_MODE_NOISE_CANCELLATION -> "noise_cancellation" + else -> "noise_cancellation" + } + + val resId = pkgContext.resources.getIdentifier( + resName, "drawable", "me.kavishdevar.librepods" + ) + + if (resId != 0) { + val drawable = pkgContext.resources.getDrawable(resId, pkgContext.theme) + button.setImageDrawable(drawable) + button.setColorFilter(Color.WHITE) + } else { + button.setImageResource(getIconResourceForMode(mode)) + button.setColorFilter(Color.WHITE) + } + } catch (e: Exception) { + button.setImageResource(getIconResourceForMode(mode)) + button.setColorFilter(Color.WHITE) + } + } + + private fun createAncModeOption(context: Context, mode: Int, isSelected: Boolean, mainButton: ImageButton): LinearLayout { + return LinearLayout(context).apply { + orientation = LinearLayout.HORIZONTAL + layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ).apply { + setMargins(0, 6, 0, 6) + } + gravity = Gravity.CENTER + setPadding(24, 16, 24, 16) + tag = "anc_mode_${mode}" + + val icon = ImageView(context).apply { + layoutParams = LinearLayout.LayoutParams(60, 60).apply { + gravity = Gravity.CENTER + } + tag = "mode_icon_$mode" + + try { + val packageContext = context.createPackageContext( + "me.kavishdevar.librepods", + Context.CONTEXT_IGNORE_SECURITY + ) + + val resourceName = when (mode) { + ANC_MODE_OFF -> "noise_cancellation" + ANC_MODE_TRANSPARENCY -> "transparency" + ANC_MODE_ADAPTIVE -> "adaptive" + ANC_MODE_NOISE_CANCELLATION -> "noise_cancellation" + else -> "noise_cancellation" + } + + val resourceId = packageContext.resources.getIdentifier( + resourceName, "drawable", "me.kavishdevar.librepods" + ) + + if (resourceId != 0) { + val drawable = packageContext.resources.getDrawable( + resourceId, packageContext.theme + ) + setImageDrawable(drawable) + } else { + setImageResource(getIconResourceForMode(mode)) + } + } catch (e: Exception) { + setImageResource(getIconResourceForMode(mode)) + Log.e(TAG, "Failed to load custom drawable for mode $mode: ${e.message}") + } + + if (isSelected) { + setColorFilter(Color.BLACK) + } else { + setColorFilter(Color.WHITE) + } + } + + addView(icon) + + if (isSelected) { + background = createSelectedBackground(context) + } else { + background = null + } + + setOnClickListener { + Log.d(TAG, "ANC mode selected: $mode (was: $currentANCMode)") + val container = findAirPodsContainer(this) + val drawerContainer = container?.findViewWithTag("airpods_drawer_container") + + if (currentANCMode == mode) { + if (drawerContainer != null && container != null) { + hideAirPodsDrawer(container, mainButton, drawerContainer) + } + return@setOnClickListener + } + + currentANCMode = mode + + val parentDrawer = parent as? ViewGroup + if (parentDrawer != null) { + for (i in 0 until parentDrawer.childCount) { + val child = parentDrawer.getChildAt(i) as? LinearLayout + if (child != null && child.tag.toString().startsWith("anc_mode_")) { + val childModeStr = child.tag.toString().substringAfter("anc_mode_") + val childMode = childModeStr.toIntOrNull() ?: -1 + val childIcon = child.findViewWithTag("mode_icon_${childMode}") + + if (childMode == mode) { + child.background = createSelectedBackground(context) + childIcon?.setColorFilter(Color.BLACK) + } else { + child.background = null + childIcon?.setColorFilter(Color.WHITE) + } + } + } + } + + val intent = Intent(ACTION_SET_ANC_MODE).apply { + setPackage("me.kavishdevar.librepods") + putExtra(EXTRA_ANC_MODE, mode) + } + context.sendBroadcast(intent) + Log.d(TAG, "Sent broadcast to change ANC mode to: ${getLabelForMode(currentANCMode)}") + + + updateMainButtonIcon(context, mainButton, mode) + + if (drawerContainer != null && container != null) { + android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({ + hideAirPodsDrawer(container, mainButton, drawerContainer) + }, 50) + } + } + } + } + + private fun createSelectedBackground(context: Context): GradientDrawable { + return GradientDrawable().apply { + shape = GradientDrawable.RECTANGLE + setColor(Color.WHITE) + cornerRadius = 50f + } + } + + private fun findAirPodsContainer(view: View): ViewGroup? { + var current: View? = view + while (current != null) { + if (current is ViewGroup && current.tag == "airpods_container") { + return current + } + val parent = current.parent + if (parent is ViewGroup && parent.tag == "airpods_container") { + return parent + } + current = parent as? View + } + Log.w(TAG, "Could not find airpods_container ancestor") + return null + } + + private fun showAirPodsDrawer(container: ViewGroup, mainButton: ImageButton, drawerContainer: View) { + Log.d(TAG, "Showing AirPods drawer") + val selectedModeView = drawerContainer.findViewWithTag("anc_mode_$currentANCMode") + val selectedModeIcon = selectedModeView?.findViewWithTag("mode_icon_$currentANCMode") + val buttonContainer = container.findViewWithTag("airpods_button_container") + + if (selectedModeView == null || selectedModeIcon == null) { + Log.e(TAG, "Cannot find selected mode view or icon for show animation") + + drawerContainer.alpha = 0f + drawerContainer.visibility = View.VISIBLE + + drawerContainer.animate() + .alpha(1f) + .setDuration(ANIMATION_DURATION) + .start() + + buttonContainer?.animate() + ?.alpha(0f) + ?.setDuration(ANIMATION_DURATION / 2) + ?.setStartDelay(ANIMATION_DURATION / 2) + ?.withEndAction { + buttonContainer.visibility = View.GONE + } + ?.start() + + return + } + + drawerContainer.measure( + View.MeasureSpec.makeMeasureSpec(container.width, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + ) + + val drawerHeight = drawerContainer.measuredHeight + + drawerContainer.alpha = 0f + drawerContainer.visibility = View.VISIBLE + drawerContainer.translationY = -drawerHeight.toFloat() + + drawerContainer.animate() + .translationY(0f) + .alpha(1f) + .setDuration(ANIMATION_DURATION) + .setInterpolator(DecelerateInterpolator()) + .start() + + buttonContainer?.animate() + ?.alpha(0f) + ?.setDuration(ANIMATION_DURATION / 2) + ?.setStartDelay(ANIMATION_DURATION / 3) + ?.withEndAction { + buttonContainer.visibility = View.GONE + } + ?.start() + } + + private fun hideAirPodsDrawer(container: ViewGroup, mainButton: ImageButton, drawerContainer: View) { + Log.d(TAG, "Hiding AirPods drawer") + val buttonContainer = container.findViewWithTag("airpods_button_container") + + if (buttonContainer != null && buttonContainer.visibility != View.VISIBLE) { + buttonContainer.alpha = 0f + buttonContainer.visibility = View.VISIBLE + } + + buttonContainer?.animate() + ?.alpha(1f) + ?.setDuration(ANIMATION_DURATION / 2) + ?.start() + + drawerContainer.animate() + .translationY(-drawerContainer.height.toFloat()) + .alpha(0f) + .setDuration(ANIMATION_DURATION) + .setInterpolator(AccelerateInterpolator()) + .setStartDelay(ANIMATION_DURATION / 4) + .withEndAction { + drawerContainer.visibility = View.GONE + drawerContainer.translationY = 0f + } + .start() + } + + private fun getIconResourceForMode(mode: Int): Int { + return when (mode) { + ANC_MODE_OFF -> android.R.drawable.ic_lock_silent_mode + ANC_MODE_TRANSPARENCY -> android.R.drawable.ic_lock_silent_mode_off + ANC_MODE_ADAPTIVE -> android.R.drawable.ic_menu_compass + ANC_MODE_NOISE_CANCELLATION -> android.R.drawable.ic_lock_idle_charging + else -> android.R.drawable.ic_lock_silent_mode_off + } + } + + private fun getLabelForMode(mode: Int): String { + return when (mode) { + ANC_MODE_OFF -> "Off" + ANC_MODE_TRANSPARENCY -> "Transparency" + ANC_MODE_ADAPTIVE -> "Adaptive" + ANC_MODE_NOISE_CANCELLATION -> "Noise Cancellation" + else -> "Unknown" + } + } + } +} diff --git a/android/app/src/main/java/me/kavishdevar/aln/utils/MediaController.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/MediaController.kt similarity index 97% rename from android/app/src/main/java/me/kavishdevar/aln/utils/MediaController.kt rename to android/app/src/main/java/me/kavishdevar/librepods/utils/MediaController.kt index 0709c04..c888bf6 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/utils/MediaController.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/utils/MediaController.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.utils +package me.kavishdevar.librepods.utils import android.content.SharedPreferences import android.media.AudioManager @@ -25,7 +25,7 @@ import android.os.Handler import android.os.Looper import android.util.Log import android.view.KeyEvent -import me.kavishdevar.aln.services.ServiceManager +import me.kavishdevar.librepods.services.ServiceManager object MediaController { private var initialVolume: Int? = null diff --git a/android/app/src/main/java/me/kavishdevar/aln/utils/Packets.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/Packets.kt similarity index 94% rename from android/app/src/main/java/me/kavishdevar/aln/utils/Packets.kt rename to android/app/src/main/java/me/kavishdevar/librepods/utils/Packets.kt index ffd662e..2f1a41e 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/utils/Packets.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/utils/Packets.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -19,7 +19,7 @@ @file:Suppress("unused") -package me.kavishdevar.aln.utils +package me.kavishdevar.librepods.utils import android.os.Parcelable import android.util.Log @@ -89,15 +89,15 @@ enum class NoiseControlMode { class AirPodsNotifications { companion object { - const val AIRPODS_CONNECTED = "me.kavishdevar.aln.AIRPODS_CONNECTED" - const val AIRPODS_DATA = "me.kavishdevar.aln.AIRPODS_DATA" - const val EAR_DETECTION_DATA = "me.kavishdevar.aln.EAR_DETECTION_DATA" - const val ANC_DATA = "me.kavishdevar.aln.ANC_DATA" - const val BATTERY_DATA = "me.kavishdevar.aln.BATTERY_DATA" - const val CA_DATA = "me.kavishdevar.aln.CA_DATA" - const val AIRPODS_DISCONNECTED = "me.kavishdevar.aln.AIRPODS_DISCONNECTED" - const val AIRPODS_CONNECTION_DETECTED = "me.kavishdevar.aln.AIRPODS_CONNECTION_DETECTED" - const val DISCONNECT_RECEIVERS = "me.kavishdevar.aln.DISCONNECT_RECEIVERS" + const val AIRPODS_CONNECTED = "me.kavishdevar.librepods.AIRPODS_CONNECTED" + const val AIRPODS_DATA = "me.kavishdevar.librepods.AIRPODS_DATA" + const val EAR_DETECTION_DATA = "me.kavishdevar.librepods.EAR_DETECTION_DATA" + const val ANC_DATA = "me.kavishdevar.librepods.ANC_DATA" + const val BATTERY_DATA = "me.kavishdevar.librepods.BATTERY_DATA" + const val CA_DATA = "me.kavishdevar.librepods.CA_DATA" + const val AIRPODS_DISCONNECTED = "me.kavishdevar.librepods.AIRPODS_DISCONNECTED" + const val AIRPODS_CONNECTION_DETECTED = "me.kavishdevar.librepods.AIRPODS_CONNECTION_DETECTED" + const val DISCONNECT_RECEIVERS = "me.kavishdevar.librepods.DISCONNECT_RECEIVERS" } class EarDetection { diff --git a/android/app/src/main/java/me/kavishdevar/aln/utils/PopupWindow.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/PopupWindow.kt similarity index 51% rename from android/app/src/main/java/me/kavishdevar/aln/utils/PopupWindow.kt rename to android/app/src/main/java/me/kavishdevar/librepods/utils/PopupWindow.kt index 714625e..81ca790 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/utils/PopupWindow.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/utils/PopupWindow.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -17,14 +17,17 @@ */ -package me.kavishdevar.aln.utils +package me.kavishdevar.librepods.utils import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.ObjectAnimator +import android.animation.PropertyValuesHolder import android.annotation.SuppressLint import android.content.Context import android.graphics.PixelFormat +import android.os.Handler +import android.os.Looper import android.util.Log import android.view.Gravity import android.view.LayoutInflater @@ -37,15 +40,17 @@ import android.widget.ImageButton import android.widget.LinearLayout import android.widget.TextView import android.widget.VideoView -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.MainScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import me.kavishdevar.aln.R +import me.kavishdevar.librepods.R @SuppressLint("InflateParams", "ClickableViewAccessibility") -class PopupWindow(context: Context) { +class PopupWindow( + private val context: Context, + private val onCloseCallback: () -> Unit = {} +) { private val mView: View + private var isClosing = false + private var autoCloseHandler = Handler(Looper.getMainLooper()) + private var autoCloseRunnable: Runnable? = null @Suppress("DEPRECATION") private val mParams: WindowManager.LayoutParams = WindowManager.LayoutParams().apply { @@ -109,76 +114,117 @@ class PopupWindow(context: Context) { mWindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager } - @SuppressLint("InlinedApi", "SetTextI18n") + @SuppressLint("InlinedApi", "SetTextI18s") fun open(name: String = "AirPods Pro", batteryNotification: AirPodsNotifications.BatteryNotification) { try { - if (mView.windowToken == null) { - if (mView.parent == null) { - mWindowManager.addView(mView, mParams) - mView.findViewById(R.id.name).text = name - val vid = mView.findViewById(R.id.video) - - vid.setVideoPath("android.resource://me.kavishdevar.aln/" + R.raw.connected) - vid.resolveAdjustedSize(vid.width, vid.height) + if (mView.windowToken == null && mView.parent == null && !isClosing) { + mView.findViewById(R.id.name).text = name + + updateBatteryStatus(batteryNotification) + + val vid = mView.findViewById(R.id.video) + vid.setVideoPath("android.resource://me.kavishdevar.librepods/" + R.raw.connected) + vid.resolveAdjustedSize(vid.width, vid.height) + vid.start() + vid.setOnCompletionListener { vid.start() - vid.setOnCompletionListener { - vid.start() - } - - val batteryStatus = batteryNotification.getBattery() - val batteryLeftText = mView.findViewById(R.id.left_battery) - val batteryRightText = mView.findViewById(R.id.right_battery) - val batteryCaseText = mView.findViewById(R.id.case_battery) - - batteryLeftText.text = batteryStatus.find { it.component == BatteryComponent.LEFT }?.let { - "\uDBC3\uDC8E ${it.level}%" - } ?: "" - batteryRightText.text = batteryStatus.find { it.component == BatteryComponent.RIGHT }?.let { - "\uDBC3\uDC8D ${it.level}%" - } ?: "" - batteryCaseText.text = batteryStatus.find { it.component == BatteryComponent.CASE }?.let { - "\uDBC3\uDE6C ${it.level}%" - } ?: "" - - val displayMetrics = mView.context.resources.displayMetrics - val screenHeight = displayMetrics.heightPixels - - mView.translationY = screenHeight.toFloat() - ObjectAnimator.ofFloat(mView, "translationY", 0f).apply { - duration = 500 - interpolator = DecelerateInterpolator() - start() - } - - CoroutineScope(MainScope().coroutineContext).launch { - delay(12000) - close() - } } + + mWindowManager.addView(mView, mParams) + + val displayMetrics = mView.context.resources.displayMetrics + val screenHeight = displayMetrics.heightPixels + + mView.translationY = screenHeight.toFloat() + mView.alpha = 1f + + val translationY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, screenHeight.toFloat(), 0f) + + ObjectAnimator.ofPropertyValuesHolder(mView, translationY).apply { + duration = 500 + interpolator = DecelerateInterpolator() + start() + } + + autoCloseRunnable = Runnable { close() } + autoCloseHandler.postDelayed(autoCloseRunnable!!, 12000) } } catch (e: Exception) { - Log.d("PopupService", e.toString()) + Log.e("PopupWindow", "Error opening popup: ${e.message}") + onCloseCallback() } } + @SuppressLint("SetTextI18n") + fun updateBatteryStatus(batteryNotification: AirPodsNotifications.BatteryNotification) { + val batteryStatus = batteryNotification.getBattery() + + val batteryLeftText = mView.findViewById(R.id.left_battery) + val batteryRightText = mView.findViewById(R.id.right_battery) + val batteryCaseText = mView.findViewById(R.id.case_battery) + + batteryLeftText.text = batteryStatus.find { it.component == BatteryComponent.LEFT }?.let { + if (it.status != BatteryStatus.DISCONNECTED) { + "\uDBC3\uDC8E ${it.level}%" + } else { + "" + } + } ?: "" + + batteryRightText.text = batteryStatus.find { it.component == BatteryComponent.RIGHT }?.let { + if (it.status != BatteryStatus.DISCONNECTED) { + "\uDBC3\uDC8D ${it.level}%" + } else { + "" + } + } ?: "" + + batteryCaseText.text = batteryStatus.find { it.component == BatteryComponent.CASE }?.let { + if (it.status != BatteryStatus.DISCONNECTED) { + "\uDBC3\uDE6C ${it.level}%" + } else { + "" + } + } ?: "" + } + fun close() { try { + if (isClosing) return + isClosing = true + + autoCloseRunnable?.let { autoCloseHandler.removeCallbacks(it) } + + val vid = mView.findViewById(R.id.video) + vid.stopPlayback() + ObjectAnimator.ofFloat(mView, "translationY", mView.height.toFloat()).apply { duration = 500 interpolator = AccelerateInterpolator() addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { try { - mWindowManager.removeView(mView) + mView.visibility = View.GONE + if (mView.parent != null) { + mWindowManager.removeView(mView) + } } catch (e: Exception) { - Log.d("PopupService", e.toString()) + Log.e("PopupWindow", "Error removing view: ${e.message}") + } finally { + isClosing = false + onCloseCallback() } } }) start() } } catch (e: Exception) { - Log.d("PopupService", e.toString()) + Log.e("PopupWindow", "Error closing popup: ${e.message}") + isClosing = false + onCloseCallback() } } + + val isShowing: Boolean + get() = mView.parent != null && !isClosing } diff --git a/android/app/src/main/java/me/kavishdevar/aln/utils/RadareOffsetFinder.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/RadareOffsetFinder.kt similarity index 70% rename from android/app/src/main/java/me/kavishdevar/aln/utils/RadareOffsetFinder.kt rename to android/app/src/main/java/me/kavishdevar/librepods/utils/RadareOffsetFinder.kt index ef1a9c2..2ab4159 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/utils/RadareOffsetFinder.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/utils/RadareOffsetFinder.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package me.kavishdevar.aln.utils +package me.kavishdevar.librepods.utils import android.content.Context import android.util.Log @@ -25,6 +25,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.withContext +import me.kavishdevar.librepods.services.ServiceManager import java.io.BufferedReader import java.io.File import java.io.FileOutputStream @@ -36,9 +37,11 @@ import java.net.URL class RadareOffsetFinder(context: Context) { companion object { private const val TAG = "RadareOffsetFinder" - // Custom static build of radare2 for Android that doesn't need Termux. See: https://github.com/devnoname120/radare2/releases/tag/5.9.8-android-aln - private const val RADARE2_URL = "https://github.com/devnoname120/radare2/releases/download/5.9.8-android-aln/radare2-5.9.9-android-aarch64-aln.tar.gz" - private const val HOOK_OFFSET_PROP = "persist.aln.hook_offset" + private const val RADARE2_URL = "https://hc-cdn.hel1.your-objectstorage.com/s/v3/c9898243c42c0d3d1387de9a37d57ce9df77f9c9_radare2-5.9.9-android-aarch64.tar.gz" + private const val HOOK_OFFSET_PROP = "persist.librepods.hook_offset" + private const val CFG_REQ_OFFSET_PROP = "persist.librepods.cfg_req_offset" + private const val CSM_CONFIG_OFFSET_PROP = "persist.librepods.csm_config_offset" + private const val PEER_INFO_REQ_OFFSET_PROP = "persist.librepods.peer_info_req_offset" private const val EXTRACT_DIR = "/" private const val RADARE2_BIN_PATH = "$EXTRACT_DIR/data/local/tmp/aln_unzip/org.radare.radare2installer/radare2/bin" @@ -64,21 +67,25 @@ class RadareOffsetFinder(context: Context) { return null } - fun clearHookOffset(): Boolean { + fun clearHookOffsets(): Boolean { try { val process = Runtime.getRuntime().exec(arrayOf( - "su", "-c", "setprop $HOOK_OFFSET_PROP ''" + "su", "-c", + "setprop $HOOK_OFFSET_PROP '' && " + + "setprop $CFG_REQ_OFFSET_PROP '' && " + + "setprop $CSM_CONFIG_OFFSET_PROP '' && " + + "setprop $PEER_INFO_REQ_OFFSET_PROP ''" )) val exitCode = process.waitFor() if (exitCode == 0) { - Log.d(TAG, "Successfully cleared hook offset property") + Log.d(TAG, "Successfully cleared hook offset properties") return true } else { - Log.e(TAG, "Failed to clear hook offset property, exit code: $exitCode") + Log.e(TAG, "Failed to clear hook offset properties, exit code: $exitCode") } } catch (e: Exception) { - Log.e(TAG, "Error clearing hook offset property", e) + Log.e(TAG, "Error clearing hook offset properties", e) } return false } @@ -105,6 +112,11 @@ class RadareOffsetFinder(context: Context) { fun isHookOffsetAvailable(): Boolean { + Log.d(TAG, "Setup Skipped? " + ServiceManager.getService()?.applicationContext?.getSharedPreferences("settings", Context.MODE_PRIVATE)?.getBoolean("skip_setup", false).toString()) + if (ServiceManager.getService()?.applicationContext?.getSharedPreferences("settings", Context.MODE_PRIVATE)?.getBoolean("skip_setup", false) == true) { + Log.d(TAG, "Setup skipped, returning true.") + return true + } _progressState.value = ProgressState.CheckingExisting try { val process = Runtime.getRuntime().exec(arrayOf("getprop", HOOK_OFFSET_PROP)) @@ -404,6 +416,10 @@ class RadareOffsetFinder(context: Context) { Log.e(TAG, "rabin2 command failed with exit code $exitCode") } + // findAndSaveL2cuProcessCfgReqOffset(libraryPath, envSetup) + // findAndSaveL2cCsmConfigOffset(libraryPath, envSetup) + findAndSaveL2cuSendPeerInfoReqOffset(libraryPath, envSetup) + } catch (e: Exception) { Log.e(TAG, "Failed to find function offset", e) return@withContext 0L @@ -418,6 +434,141 @@ class RadareOffsetFinder(context: Context) { return@withContext offset } + private suspend fun findAndSaveL2cuProcessCfgReqOffset(libraryPath: String, envSetup: String) = withContext(Dispatchers.IO) { + try { + val command = "$envSetup && $RADARE2_BIN_PATH/rabin2 -q -E $libraryPath | grep l2cu_process_our_cfg_req" + Log.d(TAG, "Running command: $command") + + val process = Runtime.getRuntime().exec(arrayOf("su", "-c", command)) + val reader = BufferedReader(InputStreamReader(process.inputStream)) + val errorReader = BufferedReader(InputStreamReader(process.errorStream)) + + var line: String? + var offset = 0L + + while (reader.readLine().also { line = it } != null) { + Log.d(TAG, "rabin2 output: $line") + if (line?.contains("l2cu_process_our_cfg_req") == true) { + val parts = line.split(" ") + if (parts.isNotEmpty() && parts[0].startsWith("0x")) { + offset = parts[0].substring(2).toLong(16) + Log.d(TAG, "Found l2cu_process_our_cfg_req offset at ${parts[0]}") + break + } + } + } + + while (errorReader.readLine().also { line = it } != null) { + Log.d(TAG, "rabin2 error: $line") + } + + val exitCode = process.waitFor() + if (exitCode != 0) { + Log.e(TAG, "rabin2 command failed with exit code $exitCode") + } + + if (offset > 0L) { + val hexString = "0x${offset.toString(16)}" + Runtime.getRuntime().exec(arrayOf( + "su", "-c", "setprop $CFG_REQ_OFFSET_PROP $hexString" + )).waitFor() + Log.d(TAG, "Saved l2cu_process_our_cfg_req offset: $hexString") + } + } catch (e: Exception) { + Log.e(TAG, "Failed to find or save l2cu_process_our_cfg_req offset", e) + } + } + + private suspend fun findAndSaveL2cCsmConfigOffset(libraryPath: String, envSetup: String) = withContext(Dispatchers.IO) { + try { + val command = "$envSetup && $RADARE2_BIN_PATH/rabin2 -q -E $libraryPath | grep l2c_csm_config" + Log.d(TAG, "Running command: $command") + + val process = Runtime.getRuntime().exec(arrayOf("su", "-c", command)) + val reader = BufferedReader(InputStreamReader(process.inputStream)) + val errorReader = BufferedReader(InputStreamReader(process.errorStream)) + + var line: String? + var offset = 0L + + while (reader.readLine().also { line = it } != null) { + Log.d(TAG, "rabin2 output: $line") + if (line?.contains("l2c_csm_config") == true) { + val parts = line.split(" ") + if (parts.isNotEmpty() && parts[0].startsWith("0x")) { + offset = parts[0].substring(2).toLong(16) + Log.d(TAG, "Found l2c_csm_config offset at ${parts[0]}") + break + } + } + } + + while (errorReader.readLine().also { line = it } != null) { + Log.d(TAG, "rabin2 error: $line") + } + + val exitCode = process.waitFor() + if (exitCode != 0) { + Log.e(TAG, "rabin2 command failed with exit code $exitCode") + } + + if (offset > 0L) { + val hexString = "0x${offset.toString(16)}" + Runtime.getRuntime().exec(arrayOf( + "su", "-c", "setprop $CSM_CONFIG_OFFSET_PROP $hexString" + )).waitFor() + Log.d(TAG, "Saved l2c_csm_config offset: $hexString") + } + } catch (e: Exception) { + Log.e(TAG, "Failed to find or save l2c_csm_config offset", e) + } + } + + private suspend fun findAndSaveL2cuSendPeerInfoReqOffset(libraryPath: String, envSetup: String) = withContext(Dispatchers.IO) { + try { + val command = "$envSetup && $RADARE2_BIN_PATH/rabin2 -q -E $libraryPath | grep l2cu_send_peer_info_req" + Log.d(TAG, "Running command: $command") + + val process = Runtime.getRuntime().exec(arrayOf("su", "-c", command)) + val reader = BufferedReader(InputStreamReader(process.inputStream)) + val errorReader = BufferedReader(InputStreamReader(process.errorStream)) + + var line: String? + var offset = 0L + + while (reader.readLine().also { line = it } != null) { + Log.d(TAG, "rabin2 output: $line") + if (line?.contains("l2cu_send_peer_info_req") == true) { + val parts = line.split(" ") + if (parts.isNotEmpty() && parts[0].startsWith("0x")) { + offset = parts[0].substring(2).toLong(16) + Log.d(TAG, "Found l2cu_send_peer_info_req offset at ${parts[0]}") + break + } + } + } + + while (errorReader.readLine().also { line = it } != null) { + Log.d(TAG, "rabin2 error: $line") + } + + val exitCode = process.waitFor() + if (exitCode != 0) { + Log.e(TAG, "rabin2 command failed with exit code $exitCode") + } + + if (offset > 0L) { + val hexString = "0x${offset.toString(16)}" + Runtime.getRuntime().exec(arrayOf( + "su", "-c", "setprop $PEER_INFO_REQ_OFFSET_PROP $hexString" + )).waitFor() + Log.d(TAG, "Saved l2cu_send_peer_info_req offset: $hexString") + } + } catch (e: Exception) { + Log.e(TAG, "Failed to find or save l2cu_send_peer_info_req offset", e) + } + } + private suspend fun saveOffset(offset: Long): Boolean = withContext(Dispatchers.IO) { try { val hexString = "0x${offset.toString(16)}" diff --git a/android/app/src/main/java/me/kavishdevar/librepods/utils/SystemAPIUtils.kt b/android/app/src/main/java/me/kavishdevar/librepods/utils/SystemAPIUtils.kt new file mode 100644 index 0000000..cb2a9b8 --- /dev/null +++ b/android/app/src/main/java/me/kavishdevar/librepods/utils/SystemAPIUtils.kt @@ -0,0 +1,285 @@ +package me.kavishdevar.librepods.utils + +import android.bluetooth.BluetoothDevice + +object SystemApisUtils { + + /** + * Device type which is used in METADATA_DEVICE_TYPE + * Indicates this Bluetooth device is an untethered headset. + * @hide + */ + val BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET: String + get() = "Untethered Headset" + + /** + * Maximum length of a metadata entry, this is to avoid exploding Bluetooth + * disk usage + * @hide + */ + val BluetoothDevice.METADATA_MAX_LENGTH: Int + get() = 2048 + + /** + * Manufacturer name of this Bluetooth device + * Data type should be {@String} as [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_MANUFACTURER_NAME: Int + get() = 0 + + /** + * Model name of this Bluetooth device + * Data type should be {@String} as [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_MODEL_NAME: Int + get() = 1 + + /** + * Software version of this Bluetooth device + * Data type should be {@String} as [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_SOFTWARE_VERSION: Int + get() = 2 + + /** + * Hardware version of this Bluetooth device + * Data type should be {@String} as [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_HARDWARE_VERSION: Int + get() = 3 + + /** + * Package name of the companion app, if any + * Data type should be {@String} as [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_COMPANION_APP: Int + get() = 4 + + /** + * URI to the main icon shown on the settings UI + * Data type should be [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_MAIN_ICON: Int + get() = 5 + + /** + * Whether this device is an untethered headset with left, right and case + * Data type should be {@String} as [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET: Int + get() = 6 + + /** + * URI to icon of the left headset + * Data type should be [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON: Int + get() = 7 + + /** + * URI to icon of the right headset + * Data type should be [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_UNTETHERED_RIGHT_ICON: Int + get() = 8 + + /** + * URI to icon of the headset charging case + * Data type should be [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_UNTETHERED_CASE_ICON: Int + get() = 9 + + /** + * Battery level of left headset + * Data type should be {@String} 0-100 as [Byte] array, otherwise + * as invalid. + * @hide + */ + val BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY: Int + get() = 10 + + /** + * Battery level of rigth headset + * Data type should be {@String} 0-100 as [Byte] array, otherwise + * as invalid. + * @hide + */ + val BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY: Int + get() = 11 + + /** + * Battery level of the headset charging case + * Data type should be {@String} 0-100 as [Byte] array, otherwise + * as invalid. + * @hide + */ + val BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY: Int + get() = 12 + + /** + * Whether the left headset is charging + * Data type should be {@String} as [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_UNTETHERED_LEFT_CHARGING: Int + get() = 13 + + /** + * Whether the right headset is charging + * Data type should be {@String} as [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_UNTETHERED_RIGHT_CHARGING: Int + get() = 14 + + /** + * Whether the headset charging case is charging + * Data type should be {@String} as [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_UNTETHERED_CASE_CHARGING: Int + get() = 15 + + /** + * URI to the enhanced settings UI slice + * Data type should be {@String} as [Byte] array, null means + * the UI does not exist. + * @hide + */ + val BluetoothDevice.METADATA_ENHANCED_SETTINGS_UI_URI: Int + get() = 16 + + /** + * @hide + */ + val BluetoothDevice.COMPANION_TYPE_PRIMARY: String + get() = "COMPANION_PRIMARY" + + /** + * @hide + */ + val BluetoothDevice.COMPANION_TYPE_SECONDARY: String + get() = "COMPANION_SECONDARY" + + /** + * @hide + */ + val BluetoothDevice.COMPANION_TYPE_NONE: String + get() = "COMPANION_NONE" + + /** + * Type of the Bluetooth device, must be within the list of + * BluetoothDevice.DEVICE_TYPE_* + * Data type should be {@String} as [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_DEVICE_TYPE: Int + get() = 17 + + /** + * Battery level of the Bluetooth device, use when the Bluetooth device + * does not support HFP battery indicator. + * Data type should be {@String} as [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_MAIN_BATTERY: Int + get() = 18 + + /** + * Whether the device is charging. + * Data type should be {@String} as [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_MAIN_CHARGING: Int + get() = 19 + + /** + * The battery threshold of the Bluetooth device to show low battery icon. + * Data type should be {@String} as [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_MAIN_LOW_BATTERY_THRESHOLD: Int + get() = 20 + + /** + * The battery threshold of the left headset to show low battery icon. + * Data type should be {@String} as [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD: Int + get() = 21 + + /** + * The battery threshold of the right headset to show low battery icon. + * Data type should be {@String} as [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD: Int + get() = 22 + + /** + * The battery threshold of the case to show low battery icon. + * Data type should be {@String} as [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD: Int + get() = 23 + + + /** + * The metadata of the audio spatial data. + * Data type should be [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_SPATIAL_AUDIO: Int + get() = 24 + + /** + * The metadata of the Fast Pair for any custmized feature. + * Data type should be [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS: Int + get() = 25 + + /** + * The metadata of the Fast Pair for LE Audio capable devices. + * Data type should be [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_LE_AUDIO: Int + get() = 26 + + /** + * The UUIDs (16-bit) of registered to CCC characteristics from Media Control services. + * Data type should be [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_GMCS_CCCD: Int + get() = 27 + + /** + * The UUIDs (16-bit) of registered to CCC characteristics from Telephony Bearer service. + * Data type should be [Byte] array. + * @hide + */ + val BluetoothDevice.METADATA_GTBS_CCCD: Int + get() = 28 + + const val BATTERY_LEVEL_UNKNOWN: Int = -1 + + const val ACTION_BLUETOOTH_HANDSFREE_BATTERY_CHANGED = "android.intent.action.BLUETOOTH_HANDSFREE_BATTERY_CHANGED" + const val EXTRA_SHOW_BT_HANDSFREE_BATTERY = "android.intent.extra.show_bluetooth_handsfree_battery" + const val EXTRA_BT_HANDSFREE_BATTERY_LEVEL = "android.intent.extra.bluetooth_handsfree_battery_level" +} diff --git a/android/app/src/main/java/me/kavishdevar/aln/widgets/BatteryWidget.kt b/android/app/src/main/java/me/kavishdevar/librepods/widgets/BatteryWidget.kt similarity index 79% rename from android/app/src/main/java/me/kavishdevar/aln/widgets/BatteryWidget.kt rename to android/app/src/main/java/me/kavishdevar/librepods/widgets/BatteryWidget.kt index fabd594..279bf46 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/widgets/BatteryWidget.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/widgets/BatteryWidget.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -17,7 +17,7 @@ */ -package me.kavishdevar.aln.widgets +package me.kavishdevar.librepods.widgets import android.app.PendingIntent import android.appwidget.AppWidgetManager @@ -26,9 +26,9 @@ import android.content.Context import android.content.Intent import android.widget.RemoteViews import androidx.compose.material3.ExperimentalMaterial3Api -import me.kavishdevar.aln.MainActivity -import me.kavishdevar.aln.R -import me.kavishdevar.aln.services.ServiceManager +import me.kavishdevar.librepods.MainActivity +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.services.ServiceManager class BatteryWidget : AppWidgetProvider() { override fun onUpdate( diff --git a/android/app/src/main/java/me/kavishdevar/aln/widgets/NoiseControlWidget.kt b/android/app/src/main/java/me/kavishdevar/librepods/widgets/NoiseControlWidget.kt similarity index 92% rename from android/app/src/main/java/me/kavishdevar/aln/widgets/NoiseControlWidget.kt rename to android/app/src/main/java/me/kavishdevar/librepods/widgets/NoiseControlWidget.kt index 918844f..b791f3c 100644 --- a/android/app/src/main/java/me/kavishdevar/aln/widgets/NoiseControlWidget.kt +++ b/android/app/src/main/java/me/kavishdevar/librepods/widgets/NoiseControlWidget.kt @@ -1,7 +1,7 @@ /* - * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! + * LibrePods - AirPods liberated from Apple’s ecosystem * - * Copyright (C) 2024 Kavish Devar + * Copyright (C) 2025 LibrePods contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -17,7 +17,7 @@ */ -package me.kavishdevar.aln.widgets +package me.kavishdevar.librepods.widgets import android.app.PendingIntent import android.appwidget.AppWidgetManager @@ -25,8 +25,8 @@ import android.appwidget.AppWidgetProvider import android.content.Context import android.content.Intent import android.widget.RemoteViews -import me.kavishdevar.aln.R -import me.kavishdevar.aln.services.ServiceManager +import me.kavishdevar.librepods.R +import me.kavishdevar.librepods.services.ServiceManager class NoiseControlWidget : AppWidgetProvider() { override fun onUpdate( diff --git a/android/app/src/main/res/drawable-v24/ic_launcher_background.xml b/android/app/src/main/res/drawable-v24/ic_launcher_background.xml new file mode 100644 index 0000000..ff1817c --- /dev/null +++ b/android/app/src/main/res/drawable-v24/ic_launcher_background.xml @@ -0,0 +1,10 @@ + + + + diff --git a/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml index 2b068d1..7148c08 100644 --- a/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ b/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -4,27 +4,36 @@ android:height="108dp" android:viewportWidth="108" android:viewportHeight="108"> - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + diff --git a/android/app/src/main/res/drawable-v24/ic_launcher_monochrome.xml b/android/app/src/main/res/drawable-v24/ic_launcher_monochrome.xml new file mode 100644 index 0000000..f1af758 --- /dev/null +++ b/android/app/src/main/res/drawable-v24/ic_launcher_monochrome.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/ic_launcher_background.xml b/android/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9..0000000 --- a/android/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/app/src/main/res/layout/battery_widget.xml b/android/app/src/main/res/layout/battery_widget.xml index a5110e0..df54f5d 100644 --- a/android/app/src/main/res/layout/battery_widget.xml +++ b/android/app/src/main/res/layout/battery_widget.xml @@ -1,12 +1,12 @@ + android:theme="@style/Theme.LibrePods.AppWidgetContainer"> - - - \ No newline at end of file + + + diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 6f3b755..8fde456 100644 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -2,5 +2,5 @@ - - \ No newline at end of file + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index c209e78ecd372343283f4157dcfd918ec5165bb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2898 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 diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 4f0f1d64e58ba64d180ce43ee13bf9a17835fbca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 948a3070fe34c611c42c0d3ad3013a0dce358be0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s diff --git a/android/app/src/main/res/values-night-v31/themes.xml b/android/app/src/main/res/values-night-v31/themes.xml index 827ca82..80e1388 100644 --- a/android/app/src/main/res/values-night-v31/themes.xml +++ b/android/app/src/main/res/values-night-v31/themes.xml @@ -3,7 +3,7 @@ - diff --git a/android/app/src/main/res/values-night/colors.xml b/android/app/src/main/res/values-night/colors.xml index 7a2a42e..952e4c9 100644 --- a/android/app/src/main/res/values-night/colors.xml +++ b/android/app/src/main/res/values-night/colors.xml @@ -4,4 +4,5 @@ @color/white #1C1B1E @color/white - \ No newline at end of file + #0B0B0B + diff --git a/android/app/src/main/res/values-v21/styles.xml b/android/app/src/main/res/values-v21/styles.xml index d1800ed..6d4e9a1 100644 --- a/android/app/src/main/res/values-v21/styles.xml +++ b/android/app/src/main/res/values-v21/styles.xml @@ -1,12 +1,12 @@ - - - diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml index 6e2a52b..5f4931d 100644 --- a/android/app/src/main/res/values/colors.xml +++ b/android/app/src/main/res/values/colors.xml @@ -1,13 +1,14 @@ - #FF000000 - #FFFFFFFF - #FFFFFF - @color/black - #87FFFFFF - @color/black - #FFE1F5FE - #FF81D4FA - #FF039BE5 - #FF01579B + #FF000000 + #FFFFFFFF + #FFFFFF + @color/black + #87FFFFFF + @color/black + #FFE1F5FE + #FF81D4FA + #FF039BE5 + #FF01579B + #FFFFFF diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 8969cae..cc67374 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - ALN + LibrePods GATT Testing See your AirPods battery status right from your home screen! Accessibility @@ -49,4 +49,14 @@ Moved to Linux Head Tracking Nod to answer calls, and shake your head to decline. + General + Quick Settings Tile Action + Show noise control dialog on tap. + Cycle through modes on tap. + Developer + Open AirPods Settings + Manage AirPods features and preferences + Automatic Ear Detection + Auto Play + Auto Pause diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 6cdb21d..30e8f43 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -1,11 +1,11 @@ - - diff --git a/android/app/src/main/res/values/themes.xml b/android/app/src/main/res/values/themes.xml index 548a11f..0f3f84e 100644 --- a/android/app/src/main/res/values/themes.xml +++ b/android/app/src/main/res/values/themes.xml @@ -1,14 +1,38 @@ - + - - + + - diff --git a/android/app/src/main/resources/META-INF/xposed/java_init.list b/android/app/src/main/resources/META-INF/xposed/java_init.list index 8968b1c..05a00eb 100644 --- a/android/app/src/main/resources/META-INF/xposed/java_init.list +++ b/android/app/src/main/resources/META-INF/xposed/java_init.list @@ -1 +1 @@ -me.kavishdevar.aln.utils.KotlinModule +me.kavishdevar.librepods.utils.KotlinModule diff --git a/android/app/src/main/resources/META-INF/xposed/scope.list b/android/app/src/main/resources/META-INF/xposed/scope.list index 18e0497..a2ac596 100644 --- a/android/app/src/main/resources/META-INF/xposed/scope.list +++ b/android/app/src/main/resources/META-INF/xposed/scope.list @@ -1,2 +1,6 @@ com.android.bluetooth -me.kavishdevar.aln \ No newline at end of file +me.kavishdevar.librepods +android +com.android.systemui +com.android.settings +com.google.android.bluetooth diff --git a/android/gradle.properties b/android/gradle.properties index 20e2a01..2c138d5 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -20,4 +20,6 @@ kotlin.code.style=official # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true \ No newline at end of file +android.nonTransitiveRClass=true + +android.javaCompile.suppressSourceTargetDeprecationWarning=true \ No newline at end of file diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 6d051a7..2a01f29 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -1,43 +1,40 @@ [versions] accompanistPermissions = "0.36.0" agp = "8.8.2" -hiddenapibypass = "4.3" -kotlin = "2.0.0" -coreKtx = "1.15.0" -junit = "4.13.2" -junitVersion = "1.2.1" -espressoCore = "3.6.1" +hiddenapibypass = "6.1" +kotlin = "2.1.10" +coreKtx = "1.16.0" lifecycleRuntimeKtx = "2.8.7" -activityCompose = "1.9.3" -composeBom = "2024.11.00" -annotations = "26.0.0" -navigationCompose = "2.8.4" -constraintlayout = "2.2.0" -haze = "1.1.1" -hazeMaterials = "1.1.1" +activityCompose = "1.10.1" +composeBom = "2025.04.00" +annotations = "26.0.2" +navigationCompose = "2.8.9" +constraintlayout = "2.2.1" +haze = "1.5.3" +hazeMaterials = "1.5.3" +sliceBuilders = "1.1.0-alpha02" +sliceCore = "1.1.0-alpha02" +sliceView = "1.1.0-alpha02" [libraries] accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } hiddenapibypass = { module = "org.lsposed.hiddenapibypass:hiddenapibypass", version.ref = "hiddenapibypass" } -junit = { group = "junit", name = "junit", version.ref = "junit" } -androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } -androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } androidx-ui = { group = "androidx.compose.ui", name = "ui" } androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } -androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } -androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } -androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" } annotations = { group = "org.jetbrains", name = "annotations", version.ref = "annotations" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" } androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } haze = { group = "dev.chrisbanes.haze", name = "haze", version.ref = "haze" } haze-materials = { group = "dev.chrisbanes.haze", name = "haze-materials", version.ref = "hazeMaterials" } +androidx-slice-builders = { group = "androidx.slice", name = "slice-builders", version.ref = "sliceBuilders" } +androidx-slice-core = { group = "androidx.slice", name = "slice-core", version.ref = "sliceCore" } +androidx-slice-view = { group = "androidx.slice", name = "slice-view", version.ref = "sliceView" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts index 8601bf4..7489fa0 100644 --- a/android/settings.gradle.kts +++ b/android/settings.gradle.kts @@ -20,5 +20,5 @@ dependencyResolutionManagement { } } -rootProject.name = "ALN" +rootProject.name = "LibrePods" include(":app") diff --git a/imgs/banner.png b/imgs/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..1406705bd6c3763c58a3733fb37b3b3325629cd2 GIT binary patch literal 272455 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

Q)T3<g>TvNh?37itg-$yk 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

mTga!kR@s}0bI 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
    6* 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 T72U!!A>f0sMQ&wSdmR4{NdW{B literal 0 HcmV?d00001 diff --git a/root-module/customize.sh b/root-module/customize.sh index a7234e4..9b01cd2 100644 --- a/root-module/customize.sh +++ b/root-module/customize.sh @@ -179,7 +179,7 @@ EOF ui_print "Created script for apex library handling." ui_print "You can now restart your device and test aln!" ui_print "Note: If your Bluetooth doesn't work anymore after restarting, then uninstall this module and report the issue at the link below." - ui_print "https://github.com/kavishdevar/aln/issues/new" + ui_print "https://github.com/kavishdevar/librepods/issues/new" fi else ui_print "Error: patched file missing." diff --git a/root-module/module.prop b/root-module/module.prop index 47ee2da..0193b14 100644 --- a/root-module/module.prop +++ b/root-module/module.prop @@ -4,4 +4,4 @@ version=v3 versionCode=3 author=@devnoname120 and @kavishdevar description=Fixes the Bluetooth L2CAP connection issue with AirPods -updateJson=https://raw.githubusercontent.com/kavishdevar/aln/main/update.json +updateJson=https://raw.githubusercontent.com/kavishdevar/librepods/main/update.json diff --git a/update.json b/update.json index 1fbd681..310ff94 100644 --- a/update.json +++ b/update.json @@ -1,6 +1,6 @@ { "version": "v0.0.3", "versionCode": 3, - "zipUrl": "https://github.com/kavishdevar/aln/releases/download/v0.0.3/btl2capfix-v0.0.3.zip", - "changelog": "https://raw.githubusercontent.com/kavishdevar/aln/main/CHANGELOG.md" + "zipUrl": "https://github.com/kavishdevar/librepods/releases/download/v0.0.3/btl2capfix-v0.0.3.zip", + "changelog": "https://raw.githubusercontent.com/kavishdevar/librepods/main/CHANGELOG.md" }