Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit ac59662

Browse files
authored
Merge pull request #89 from JSKitty/master
v0.1.7 - Android, Net Stability, Profile Improvements
2 parents 0ad6301 + 42def49 commit ac59662

89 files changed

Lines changed: 3023 additions & 309 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@ dist-ssr
2222
*.njsproj
2323
*.sln
2424
*.sw?
25+
26+
# Android Signing Keystore file
27+
src-tauri/gen/android/keystore.properties
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Android Build Setup for Tauri on Apple Silicon (M-series chip)
2+
3+
## Overview
4+
This guide documents how to set up a complete Android development environment for Vector on Apple Silicon (M1/M2/etc) Macs, focusing on resolving common cross-compilation issues with potential Clang linker issues.
5+
6+
## Prerequisites
7+
8+
It is ideal that you first attempt a native compile (`npm run tauri dev`), ensuring that Vector builds and runs successfully natively, first and foremost; prior to moving on to cross-compiling.
9+
10+
### 1. Install Android Studio
11+
Download and install from [developer.android.com](https://developer.android.com/studio)
12+
13+
### 2. Install Homebrew packages
14+
```bash
15+
# Install Java
16+
brew install openjdk@17
17+
sudo ln -sfn /opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-17.jdk
18+
19+
# Install Android NDK
20+
brew install --cask android-ndk
21+
```
22+
23+
### 3. Install Rust Android targets
24+
```bash
25+
rustup target add aarch64-linux-android
26+
rustup target add armv7-linux-androideabi
27+
rustup target add x86_64-linux-android
28+
rustup target add i686-linux-android
29+
```
30+
31+
## Environment Configuration
32+
33+
### 1. Set up environment variables
34+
Add to `~/.zshrc`:
35+
36+
```bash
37+
# Android Development Environment for Tauri
38+
export ANDROID_HOME="$HOME/Library/Android/sdk"
39+
export NDK_HOME="/opt/homebrew/Caskroom/android-ndk/28b/AndroidNDK13356709.app/Contents/NDK"
40+
export JAVA_HOME=$(/usr/libexec/java_home)
41+
export PATH="$NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin:$PATH"
42+
export PATH="$PATH:$ANDROID_HOME/platform-tools"
43+
export PATH="$PATH:$ANDROID_HOME/cmdline-tools/latest/bin"
44+
export PATH="$PATH:$ANDROID_HOME/emulator"
45+
```
46+
47+
Reload your environment:
48+
```bash
49+
source ~/.zshrc
50+
```
51+
52+
## Fixing Cross-Compilation Issues
53+
54+
### 1. Create missing NDK tool symlinks
55+
```bash
56+
cd $NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/
57+
sudo ln -sf llvm-ranlib aarch64-linux-android-ranlib
58+
sudo ln -sf llvm-ar aarch64-linux-android-ar
59+
```
60+
61+
### 2. Fix AAudio linking issue
62+
The linker may fail to find AAudio (used by Vector Voice) when using API level 24. Create wrapper scripts to force API level 26+ in the Tauri Build System:
63+
64+
```bash
65+
cd $NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64/bin/
66+
67+
# Create wrapper for clang
68+
sudo tee aarch64-linux-android24-clang << 'EOF'
69+
#!/bin/bash
70+
exec "$(dirname "$0")/aarch64-linux-android26-clang" "$@"
71+
EOF
72+
sudo chmod +x aarch64-linux-android24-clang
73+
74+
# Create wrapper for clang++
75+
sudo tee aarch64-linux-android24-clang++ << 'EOF'
76+
#!/bin/bash
77+
exec "$(dirname "$0")/aarch64-linux-android26-clang++" "$@"
78+
EOF
79+
sudo chmod +x aarch64-linux-android24-clang++
80+
```
81+
82+
## Building Your App
83+
84+
### Development build with Android Studio emulator
85+
86+
You'll need to be running an emulation device before performing the build, as such, please ensure **Android Studio** is open, click **More Actions** in the **Projects** tab, open **Virtual Device Manager**, and launch a device, once it's fully booted, you can continue building.
87+
88+
```bash
89+
npm run tauri android dev --open
90+
```
91+
92+
### Release build
93+
94+
To be able to install the APK, you'll need to Sign the APK, please follow the [Tauri v2 Android Signing documentation](https://tauri.app/distribute/sign/android/) to configure APK signing, it is possible to self-sign, making the process fairly straightforward.
95+
96+
All signing code and configuration edits have already been applied to the Vector Android files, as such, **you only need to add your Keystore file to complete the signing process.**
97+
98+
```bash
99+
# Build an APK for a specific architecture (aarch64 is the standard at >95% adoption, thus, the recommended APK arch)
100+
npm run tauri android build -- --apk --target aarch64
101+
102+
# Build an APK for all architectures (bulky binary, not recommended)
103+
npm run tauri android build -- --apk
104+
```
105+
106+
## Troubleshooting
107+
108+
### Verify environment setup
109+
```bash
110+
echo "ANDROID_HOME: $ANDROID_HOME"
111+
echo "NDK_HOME: $NDK_HOME"
112+
echo "JAVA_HOME: $JAVA_HOME"
113+
which aarch64-linux-android-ranlib
114+
```
115+
116+
### Clean build if issues persist
117+
```bash
118+
cd src-tauri && cargo clean
119+
```
120+
121+
## Key Takeaways
122+
123+
1. **OpenSSL vendoring**: Essential for cross-compilation - the `vendored` feature compiles OpenSSL from source for the target architecture
124+
2. **NDK toolchain path**: Must be in PATH for build tools to find `ranlib`, `ar`, etc.
125+
3. **API level consistency**: AAudio requires API 26+, so force this even when build system requests API 24 (due to Tauri bug?)
126+
4. **M1 compatibility**: Use `darwin-x86_64` NDK binaries - they work fine via Rosetta 2
127+
128+
## Success Indicators
129+
- ✅ No "archive member is neither ET_REL nor LLVM bitcode" warnings
130+
- ✅ No "unable to find library -laaudio" errors
131+
-`android dev` runs in Android Studio emulator
132+
-`android build` produces APKs easily installable on physical Android phones
133+
134+
---
135+
136+
*This setup was tested on MacBook Air M1 with Tauri v2.5.1, targeting Android API 26*

src-tauri/Cargo.lock

Lines changed: 15 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,22 @@ hound = "3.5.1"
5050
rubato = "0.16.2"
5151
symphonia = { version = "0.5.4", features = ["mp3", "wav", "flac", "pcm"] }
5252

53-
# Whisper (with cross-platform acceleration features)
54-
whisper-rs = "0.13.2"
53+
# Android-only dependencies
54+
[target.'cfg(target_os = "android")'.dependencies]
55+
jni = "0.21.1"
56+
ndk-context = "0.1.1"
57+
openssl = { version = "0.10", features = ["vendored"] }
5558

56-
[target.'cfg(target_os = "macos")'.dependencies]
59+
# Whisper (with cross-platform acceleration features - excluded on Android)
60+
[target.'cfg(all(not(target_os = "android"), target_os = "macos"))'.dependencies]
5761
whisper-rs = { version = "0.13.2", features = ["metal"] }
5862

59-
[target.'cfg(windows)'.dependencies]
63+
[target.'cfg(all(not(target_os = "android"), windows))'.dependencies]
6064
whisper-rs = { version = "0.13.2", features = ["vulkan"] }
6165

62-
[target.'cfg(target_os = "linux")'.dependencies]
66+
[target.'cfg(all(not(target_os = "android"), target_os = "linux"))'.dependencies]
6367
whisper-rs = { version = "0.13.2", features = ["vulkan"] }
68+
69+
# For other platforms (not Android, macOS, Windows, or Linux)
70+
[target.'cfg(all(not(target_os = "android"), not(target_os = "macos"), not(windows), not(target_os = "linux")))'.dependencies]
71+
whisper-rs = "0.13.2"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# EditorConfig is awesome: https://EditorConfig.org
2+
3+
# top-most EditorConfig file
4+
root = true
5+
6+
[*]
7+
indent_style = space
8+
indent_size = 2
9+
end_of_line = lf
10+
charset = utf-8
11+
trim_trailing_whitespace = false
12+
insert_final_newline = false

src-tauri/gen/android/.gitignore

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
*.iml
2+
.gradle
3+
/local.properties
4+
/.idea/caches
5+
/.idea/libraries
6+
/.idea/modules.xml
7+
/.idea/workspace.xml
8+
/.idea/navEditor.xml
9+
/.idea/assetWizardSettings.xml
10+
.DS_Store
11+
build
12+
/captures
13+
.externalNativeBuild
14+
.cxx
15+
local.properties
16+
key.properties
17+
18+
/.tauri
19+
/tauri.settings.gradle
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/src/main/java/io/vectorapp/generated
2+
/src/main/jniLibs/**/*.so
3+
/src/main/assets/tauri.conf.json
4+
/tauri.build.gradle.kts
5+
/proguard-tauri.pro
6+
/tauri.properties
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import java.util.Properties
2+
import java.io.FileInputStream
3+
4+
plugins {
5+
id("com.android.application")
6+
id("org.jetbrains.kotlin.android")
7+
id("rust")
8+
}
9+
10+
val tauriProperties = Properties().apply {
11+
val propFile = file("tauri.properties")
12+
if (propFile.exists()) {
13+
propFile.inputStream().use { load(it) }
14+
}
15+
}
16+
17+
android {
18+
compileSdk = 34
19+
namespace = "io.vectorapp"
20+
defaultConfig {
21+
manifestPlaceholders["usesCleartextTraffic"] = "false"
22+
applicationId = "io.vectorapp"
23+
minSdk = 26
24+
targetSdk = 34
25+
versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt()
26+
versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0")
27+
}
28+
signingConfigs {
29+
create("release") {
30+
val keystorePropertiesFile = rootProject.file("keystore.properties")
31+
val keystoreProperties = Properties()
32+
if (keystorePropertiesFile.exists()) {
33+
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
34+
}
35+
36+
keyAlias = keystoreProperties["keyAlias"] as String
37+
keyPassword = keystoreProperties["password"] as String
38+
storeFile = file(keystoreProperties["storeFile"] as String)
39+
storePassword = keystoreProperties["password"] as String
40+
}
41+
}
42+
buildTypes {
43+
getByName("debug") {
44+
manifestPlaceholders["usesCleartextTraffic"] = "true"
45+
isDebuggable = true
46+
isJniDebuggable = true
47+
isMinifyEnabled = false
48+
packaging { jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so")
49+
jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so")
50+
jniLibs.keepDebugSymbols.add("*/x86/*.so")
51+
jniLibs.keepDebugSymbols.add("*/x86_64/*.so")
52+
}
53+
}
54+
getByName("release") {
55+
signingConfig = signingConfigs.getByName("release")
56+
isMinifyEnabled = true
57+
proguardFiles(
58+
*fileTree(".") { include("**/*.pro") }
59+
.plus(getDefaultProguardFile("proguard-android-optimize.txt"))
60+
.toList().toTypedArray()
61+
)
62+
}
63+
}
64+
kotlinOptions {
65+
jvmTarget = "1.8"
66+
}
67+
buildFeatures {
68+
buildConfig = true
69+
}
70+
}
71+
72+
rust {
73+
rootDirRel = "../../../"
74+
}
75+
76+
dependencies {
77+
implementation("androidx.webkit:webkit:1.6.1")
78+
implementation("androidx.appcompat:appcompat:1.6.1")
79+
implementation("com.google.android.material:material:1.8.0")
80+
testImplementation("junit:junit:4.13.2")
81+
androidTestImplementation("androidx.test.ext:junit:1.1.4")
82+
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0")
83+
}
84+
85+
apply(from = "tauri.build.gradle.kts")
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile

0 commit comments

Comments
 (0)