Skip to content
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
fc31a5e
Patch manifest
haslinghuis Oct 29, 2025
ae221c4
Updte workflow
haslinghuis Oct 29, 2025
b6b31db
Use tauri-plugin-serialplugin
haslinghuis Oct 29, 2025
74a3266
Fix build
haslinghuis Oct 29, 2025
983a9cf
Use Kotlin DSL syntax, not Groovy
haslinghuis Oct 29, 2025
c2163bd
Replace sed with awk
haslinghuis Oct 29, 2025
0533869
Downgrade tauri-plugin-serialplugin to 2.10.0 in Cargo.lock
haslinghuis Oct 29, 2025
a801f59
Upgrade to tauri-plugin-serialplugin 2.16.0 for Android USB support
haslinghuis Oct 29, 2025
cadf39f
Add Android USB debugging tools and updated documentation
haslinghuis Oct 29, 2025
eb441f4
Fix manifest patching: replace sed with awk for cross-platform compat…
haslinghuis Oct 29, 2025
8c93e03
Add custom MainActivity with USB runtime permission handling
haslinghuis Oct 29, 2025
b1c3154
Update debugging docs with MainActivity testing instructions
haslinghuis Oct 29, 2025
0a60f3e
Fix MainActivity import: add missing TauriActivity import
haslinghuis Oct 29, 2025
2eb9aaa
Nitpicks
haslinghuis Oct 29, 2025
cd6aa1f
Fix JitPack repository injection: use settings.gradle.kts with Kotlin…
haslinghuis Oct 29, 2025
2f2e9ab
Move JitPack repository injection into patch script
haslinghuis Oct 29, 2025
e9c511e
Inject JitPack into dependencyResolutionManagement.repositories in se…
haslinghuis Oct 29, 2025
3d13b6e
Revert usb-serial-for-android to 3.8.0 and harden JitPack injection
haslinghuis Oct 29, 2025
d71a7c1
Fix grep alternation and duplicate -n in JitPack repo preview
haslinghuis Oct 29, 2025
04b8256
Force usb-serial-for-android to 3.8.0 and remove FAIL_ON_PROJECT_REPO…
haslinghuis Oct 29, 2025
99642e8
Fix Kotlin DSL syntax error - use resolutionStrategy instead of isForce
haslinghuis Oct 29, 2025
aa9ddc8
Add debug output to show settings.gradle.kts before and after JitPack…
haslinghuis Oct 29, 2025
8d87c20
Add JitPack repository directly to app build.gradle.kts as fallback
haslinghuis Oct 29, 2025
37fb1b6
Remove custom MainActivity - not needed for USB serial permissions
haslinghuis Oct 29, 2025
6d79317
Add extensive debug logging for Android USB enumeration
haslinghuis Oct 29, 2025
a826b93
Add ADB wireless debugging setup for Android USB testing
haslinghuis Oct 29, 2025
4a32b85
Fix ADB wireless setup script to support IPv6 addresses
haslinghuis Oct 29, 2025
b85212b
Add comprehensive next steps guide for Android USB debugging
haslinghuis Oct 29, 2025
792d918
Add local Android build script: init + patch + dev/release + signing …
haslinghuis Oct 29, 2025
735ac48
feat(android): enable USB serial support with debugging capabilities
haslinghuis Oct 29, 2025
1d19205
docs: add Android USB serial status and progress tracking
haslinghuis Oct 29, 2025
9416e32
Shell improvement
haslinghuis Oct 29, 2025
1331237
Default is not needed when using explicit permissions
haslinghuis Oct 29, 2025
4435a99
No need for HMR_HOST
haslinghuis Oct 29, 2025
bb84552
Forgot to include
haslinghuis Oct 29, 2025
90cecf7
Address 'Warning: src-tauri/gen/android/settings.gradle.kts not found…
haslinghuis Oct 30, 2025
83122fc
Address 'Warning: src-tauri/gen/android/settings.gradle.kts not found…
haslinghuis Oct 30, 2025
7e03458
little cleanup
haslinghuis Oct 30, 2025
b7f391a
Update scripts
haslinghuis Nov 3, 2025
0a0bf83
Fix android debugging
haslinghuis Nov 3, 2025
62f9c41
???
haslinghuis Nov 3, 2025
05bc006
Add dependency for usb-serial-for-android
haslinghuis Nov 3, 2025
5ec9332
Address CR nitpick
haslinghuis Nov 3, 2025
cb3680a
After running cargo update
haslinghuis Nov 3, 2025
5f31190
Fix working versions
haslinghuis Nov 3, 2025
a93186b
Dependency hell
haslinghuis Nov 3, 2025
8e779cd
Which version works ???
haslinghuis Nov 3, 2025
72173cf
Fix script portability
haslinghuis Nov 3, 2025
56f041f
modify the patch script to add the repository directly to build.gradl…
haslinghuis Nov 3, 2025
4dfd15e
refactor scripts for single responsibility
haslinghuis Nov 3, 2025
c19e84c
Using js API instead of Rust invoke
haslinghuis Nov 6, 2025
bf62596
Fix USB permission issue
haslinghuis Nov 6, 2025
220037c
Fix port enumeration
haslinghuis Nov 6, 2025
0e4c4c7
Some cleanup
haslinghuis Nov 6, 2025
6dea4b3
Read is not solved
haslinghuis Nov 6, 2025
7e5ce52
Apply custom MainActivity.kt
haslinghuis Nov 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,11 @@ jobs:
- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Setup Java 17
- name: Setup Java 21
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
java-version: '21'

- name: Setup Android SDK
uses: android-actions/setup-android@v3
Expand All @@ -151,6 +151,9 @@ jobs:
- name: Initialize Tauri Android project
run: yarn tauri android init --ci

- name: Patch Android manifest for USB support
run: bash scripts/patch-android-manifest.sh

- name: Build Tauri Android APK
uses: tauri-apps/tauri-action@v0
with:
Expand Down
103 changes: 103 additions & 0 deletions .github/workflows/test-android-patch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: Test Android Manifest Patch

on:
workflow_dispatch:

jobs:
test-patch:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5

- name: Setup Node
uses: actions/setup-node@v5
with:
node-version-file: '.nvmrc'
cache: 'yarn'

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Setup Java 21
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'

- name: Setup Android SDK
uses: android-actions/setup-android@v3

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

- name: Install Rust Android targets
run: |
rustup target add aarch64-linux-android
rustup target add armv7-linux-androideabi
rustup target add i686-linux-android
rustup target add x86_64-linux-android

- name: Build web assets (Vite)
run: yarn build

- name: Initialize Tauri Android project
run: yarn tauri android init --ci

- name: Show generated manifest BEFORE patch
run: |
echo "=== MANIFEST BEFORE PATCH ==="
cat src-tauri/gen/android/app/src/main/AndroidManifest.xml || echo "Manifest not found"
echo ""
echo "=== APP BUILD.GRADLE.KTS BEFORE PATCH ==="
cat src-tauri/gen/android/app/build.gradle.kts || echo "build.gradle.kts not found"

- name: Patch Android manifest for USB support
run: bash scripts/patch-android-manifest.sh

- name: Show files after patch
run: |
echo "=== AndroidManifest.xml AFTER patch ==="
cat src-tauri/gen/android/app/src/main/AndroidManifest.xml
echo ""
echo "=== Checking for USB permissions ==="
grep -i "usb" src-tauri/gen/android/app/src/main/AndroidManifest.xml || echo "No USB permissions found!"
echo ""
echo "=== device_filter.xml ==="
cat src-tauri/gen/android/app/src/main/res/xml/device_filter.xml || echo "device_filter.xml not found!"
echo ""
echo "=== MainActivity.kt ==="
cat src-tauri/gen/android/app/src/main/java/com/betaflight/configurator/MainActivity.kt || echo "MainActivity.kt not found!"
echo ""
echo "=== app/build.gradle.kts dependencies ==="
grep -A 10 "dependencies {" src-tauri/gen/android/app/build.gradle.kts || echo "No dependencies block found!"

- name: Ensure JitPack repository
shell: bash
run: |
set -euo pipefail
FILE="src-tauri/gen/android/build.gradle.kts"
if [ -f "$FILE" ]; then
echo "=== ROOT BUILD.GRADLE.KTS BEFORE JITPACK ==="
cat "$FILE"
echo ""
echo "Ensuring JitPack repository in $FILE..."
if ! grep -q "jitpack.io" "$FILE"; then
echo "Adding JitPack repository..."
printf '\nallprojects {\n repositories {\n maven(url = "https://jitpack.io")\n }\n}\n' >> "$FILE"
fi
echo ""
echo "=== ROOT BUILD.GRADLE.KTS AFTER JITPACK ==="
cat "$FILE"
fi
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Fix JitPack repository injection: target settings.gradle.kts and Kotlin DSL syntax

Appending an allprojects block to build.gradle.kts is discouraged in Gradle 8+ and often ignored when repositoriesMode FAIL_ON_PROJECT_REPOS is enabled. Also, maven(url = "...") is not idiomatic Kotlin DSL. Patch settings.gradle.kts under dependencyResolutionManagement instead.

Apply this diff to replace the step and use Kotlin DSL:

-      - name: Ensure JitPack repository
-        shell: bash
-        run: |
-          set -euo pipefail
-          FILE="src-tauri/gen/android/build.gradle.kts"
-          if [ -f "$FILE" ]; then
-            echo "=== ROOT BUILD.GRADLE.KTS BEFORE JITPACK ==="
-            cat "$FILE"
-            echo ""
-            echo "Ensuring JitPack repository in $FILE..."
-            if ! grep -q "jitpack.io" "$FILE"; then
-              echo "Adding JitPack repository..."
-              printf '\nallprojects {\n    repositories {\n        maven(url = "https://jitpack.io")\n    }\n}\n' >> "$FILE"
-            fi
-            echo ""
-            echo "=== ROOT BUILD.GRADLE.KTS AFTER JITPACK ==="
-            cat "$FILE"
-          fi
+      - name: Ensure JitPack repository (settings.gradle.kts)
+        shell: bash
+        run: |
+          set -euo pipefail
+          FILE="src-tauri/gen/android/settings.gradle.kts"
+          if [ -f "$FILE" ]; then
+            echo "=== settings.gradle.kts BEFORE JITPACK ==="
+            cat "$FILE"; echo ""
+            if grep -q 'dependencyResolutionManagement' "$FILE"; then
+              if ! grep -q 'https://jitpack.io' "$FILE"; then
+                echo "Adding JitPack to dependencyResolutionManagement.repositories..."
+                awk '
+                  /dependencyResolutionManagement/ {print; inBlock=1; next}
+                  inBlock && /repositories\s*\{/ && !added {print; print "        maven(\"https://jitpack.io\")"; added=1; next}
+                  {print}
+                ' "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"
+              fi
+            else
+              printf '\ndependencyResolutionManagement {\n    repositories {\n        mavenCentral()\n        google()\n        maven("https://jitpack.io")\n    }\n}\n' >> "$FILE"
+            fi
+            echo "=== settings.gradle.kts AFTER JITPACK ==="
+            cat "$FILE"
+          fi

Run to confirm where repositories are sourced:


🏁 Script executed:

#!/bin/bash
rg -n "dependencyResolutionManagement|jitpack|repositoriesMode" -S src-tauri/gen/android 2>/dev/null || true

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check if src-tauri/gen/android directory exists and list contents
if [ -d "src-tauri/gen/android" ]; then
  echo "=== src-tauri/gen/android exists ==="
  find "src-tauri/gen/android" -type f \( -name "*.gradle*" -o -name "*.kts" \) 2>/dev/null | head -20
else
  echo "src-tauri/gen/android does not exist"
fi

# Search for any gradle or kts files in src-tauri
echo ""
echo "=== Searching for gradle/kts files in src-tauri ==="
find src-tauri -type f \( -name "*.gradle.kts" -o -name "*.gradle" \) 2>/dev/null | head -20

# Check if settings.gradle.kts or build.gradle.kts exist elsewhere
echo ""
echo "=== Checking entire repo for build.gradle.kts or settings.gradle.kts ==="
find . -maxdepth 5 -type f \( -name "build.gradle.kts" -o -name "settings.gradle.kts" \) 2>/dev/null

Length of output: 163


🏁 Script executed:

#!/bin/bash
# View the entire test-android-patch.yml workflow to understand context
cat .github/workflows/test-android-patch.yml

Length of output: 3674


🌐 Web query:

Tauri android init gradle structure settings.gradle.kts

💡 Result:

Short answer — what tauri android init produces and what goes in settings.gradle.kts

  • The Android project is generated at src-tauri/gen/android as a Gradle multi-project (root + modules). settings.gradle.kts lives at that generated android root and declares the included modules and repository resolution rules. [1][2]

  • Typical responsibilities of settings.gradle.kts in a Tauri-generated project:

    • include the app module (usually ":app") and the Tauri Android library/project (names vary; older templates used :tauri-android / :tauri).
    • set dependencyResolutionManagement / repositoriesMode and repositories so subprojects don’t each declare their own repos.
    • (optionally) pluginManagement or version catalogs if the template uses them. [1][2]
  • Minimal example (Kotlin DSL) you can expect or copy into the generated android root:

    • include(":app")
    • include(":tauri-android") // or the module name your template created
    • dependencyResolutionManagement {
      repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
      repositories {
      google()
      mavenCentral()
      }
      }
      (Adapt module names to whatever src-tauri/gen/android contains.) [1][2]
  • Notes:

    • Tauri’s Android templates have been updated to recent Gradle / Android Gradle Plugin versions (Gradle 8.x / AGP 8.x in current templates), so you may need recent Android Studio / JDK versions. Check the Tauri CLI/release notes for the exact template versions you have. [3]

Sources

  • Gradle: Organizing multi-project builds / settings.gradle.kts (explains settings file role and dependencyResolutionManagement). [1]
  • Community Tauri Android setup guides showing the generated layout and where files live (src-tauri/gen/android, app/build.gradle.kts). [2]
  • Tauri CLI / template release notes mentioning Gradle/AGP updates. [3]

🌐 Web query:

Kotlin DSL maven repository syntax gradle

💡 Result:

Short examples for declaring Maven repositories in Gradle Kotlin DSL (build.gradle.kts or settings.gradle.kts):

  • common built-ins
    repositories {
    mavenCentral()
    mavenLocal()
    google()
    }
    [1]

  • custom Maven URL (build.gradle.kts)
    repositories {
    maven {
    url = uri("https://my.repo.example/maven")
    }
    }
    [1][2]

  • with name + credentials (username/password)
    repositories {
    maven {
    name = "MyRepo"
    url = uri("https://my.repo.example/maven")
    credentials(PasswordCredentials::class) {
    username = findProperty("repoUser") as String?
    password = findProperty("repoPassword") as String?
    }
    }
    }
    [2]

  • token/HTTP-header auth example (GitLab style)
    val gitLabToken: String by project
    repositories {
    maven {
    url = uri("https://gitlab.com/api/v4/projects/12345/packages/maven")
    name = "GitLab"
    credentials(org.gradle.plugin.auth.HttpHeaderCredentials::class) {
    name = "Private-Token"
    value = gitLabToken
    }
    authentication { create<org.gradle.plugin.auth.HttpHeaderAuthentication>("header") }
    }
    }
    [3]

  • centralizing in settings.gradle.kts (dependencyResolutionManagement)
    dependencyResolutionManagement {
    repositories {
    mavenCentral()
    maven { url = uri("https://my.repo.example/maven") }
    }
    }
    [1][3]

References:

  • Gradle — Declaring repositories (Kotlin DSL examples). [1]
  • MavenArtifactRepository / properties (credentials, url). [2]
  • Kotlin DSL examples including GitLab token / StackOverflow translation. [3]

Fix JitPack repository injection: target settings.gradle.kts with correct Kotlin DSL syntax

The original approach appends allprojects to build.gradle.kts using Groovy syntax, which is incompatible with Kotlin DSL (.kts) files and ignored in Gradle 8+ when repositoriesMode.FAIL_ON_PROJECT_REPOS is enabled. The correct target is settings.gradle.kts under dependencyResolutionManagement.

However, the proposed fix also contains incorrect Kotlin DSL syntax. In Kotlin DSL, the correct syntax is maven { url = uri("https://jitpack.io") }, not maven("url").

Replace the step with:

       - name: Ensure JitPack repository (settings.gradle.kts)
         shell: bash
         run: |
           set -euo pipefail
           FILE="src-tauri/gen/android/settings.gradle.kts"
           if [ -f "$FILE" ]; then
             echo "=== settings.gradle.kts BEFORE JITPACK ==="
             cat "$FILE"; echo ""
             if grep -q 'dependencyResolutionManagement' "$FILE"; then
               if ! grep -q 'https://jitpack.io' "$FILE"; then
                 echo "Adding JitPack to dependencyResolutionManagement.repositories..."
                 sed -i '/repositories\s*{/a\        maven { url = uri("https://jitpack.io") }' "$FILE"
               fi
             else
               printf '\ndependencyResolutionManagement {\n    repositories {\n        mavenCentral()\n        google()\n        maven { url = uri("https://jitpack.io") }\n    }\n}\n' >> "$FILE"
             fi
             echo "=== settings.gradle.kts AFTER JITPACK ==="
             cat "$FILE"
           fi
🤖 Prompt for AI Agents
.github/workflows/test-android-patch.yml around lines 75 to 92: the workflow
currently appends a Groovy-style allprojects block into
src-tauri/gen/android/build.gradle.kts which is incompatible with Kotlin DSL and
ignored under Gradle 8+ with repositoriesMode.FAIL_ON_PROJECT_REPOS; instead
modify the step to target settings.gradle.kts and inject a Kotlin DSL
dependencyResolutionManagement block (or merge into existing one) that adds the
JitPack repository using correct Kotlin DSL syntax: repositories { maven { url =
uri("https://jitpack.io") } }; ensure you check for an existing JitPack uri to
avoid duplication and preserve file integrity rather than appending Groovy code
to build.gradle.kts.


- name: Upload generated Android files
uses: actions/upload-artifact@v4
with:
name: android-generated-files
path: |
src-tauri/gen/android/app/src/main/AndroidManifest.xml
src-tauri/gen/android/app/src/main/res/xml/device_filter.xml
src-tauri/gen/android/app/build.gradle.kts
src-tauri/gen/android/build.gradle.kts
retention-days: 7
104 changes: 104 additions & 0 deletions ANDROID_USB.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Android USB Serial Support

## Current Status (v2.16.0)

The `tauri-plugin-serialplugin` version 2.16.0 includes Android support, but requires additional runtime permission handling to actually detect USB devices.

## What's Working

✅ **Build**: APK compiles successfully
✅ **Manifest Permissions**: USB permissions are patched into AndroidManifest.xml
✅ **Plugin**: Version 2.16.0 has Android Kotlin code
✅ **Dependencies**: `usb-serial-for-android` library is added via Gradle
✅ **Device Filter**: USB device VID/PID filter is created
✅ **Intent Filter**: App launches when USB device is attached

## Custom MainActivity

We've added a custom `MainActivity.kt` that handles USB permission requests properly:

### What it does:
1. **Registers USB broadcast receivers** for device attach/detach events
2. **Automatically requests permission** when a USB device is detected
3. **Checks for already connected devices** on app launch
4. **Handles the permission dialog** and logs the result
5. **Manages app lifecycle** (onCreate, onDestroy, onNewIntent)

### How it works:
```kotlin
// When USB device is attached:
1. Android broadcasts ACTION_USB_DEVICE_ATTACHED
2. MainActivity receives the broadcast
3. Checks if we have permission for the device
4. If not, shows permission dialog to user
5. User grants/denies permission
6. Result is broadcast back to MainActivity
7. Plugin can now enumerate and open the port
```

## The Problem We Solved

Even with manifest permissions, Android apps need to **request permission at runtime** for each USB device. The stock Tauri activity doesn't do this automatically, so we added:

- Custom `MainActivity.kt` that extends `TauriActivity`
- USB broadcast receivers for attach/detach events
- Runtime permission request handling
- Logging to help debug USB issues

## Usage

### Building for Android

1. Initialize the Android project (if not already done):
```bash
cd src-tauri
cargo tauri android init
```

2. **Run the patch script** before building:
```bash
./scripts/patch-android-manifest.sh
```

3. Build the Android APK:
```bash
cd src-tauri
cargo tauri android build
```

### Automated Workflow

You can combine these steps:

```bash
# From the project root
cd src-tauri
cargo tauri android init
cd ..
./scripts/patch-android-manifest.sh
cd src-tauri
cargo tauri android build
```

## What the Script Does

1. Checks if the Android manifest exists at `src-tauri/gen/android/app/src/main/AndroidManifest.xml`
2. Adds USB permissions if not already present
3. Adds USB device attach intent filter
4. Creates `device_filter.xml` with all Betaflight-compatible USB device IDs
5. Creates a backup of the original manifest

## Future Improvements

Ideally, `tauri-plugin-serialplugin` should handle Android permissions automatically. If this becomes available in a future version, this workaround will no longer be necessary.

## Troubleshooting

### Script fails with "manifest not found"
Run `cargo tauri android init` first to generate the Android project.

### Permissions not working after build
Make sure you run the patch script **after** `android init` but **before** `android build`.

### Need to rebuild
If you run `cargo tauri android init` again (which regenerates the manifest), you must run the patch script again before building.
Loading
Loading