diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..fc84c97 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,5 @@ +[target.aarch64-linux-android] +linker = "aarch64-linux-android21-clang" + +[target.armv7-linux-androideabi] +linker = "armv7a-linux-androideabi21-clang" diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..95e6516 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,6 @@ +.gradle/ +build/ +app/build/ +app/src/main/jniLibs/ +local.properties +keystore/*.jks diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index c06aaf8..0e65563 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -13,11 +13,31 @@ android { targetSdk = 34 versionCode = 1 versionName = "0.1.0" - ndk { abiFilters += listOf("arm64-v8a", "armeabi-v7a") } + ndk { abiFilters += listOf("arm64-v8a") } + } + + signingConfigs { + create("release") { + storeFile = file("${project.rootDir}/keystore/wzp-release.jks") + storePassword = "wzphone2024" + keyAlias = "wzp-release" + keyPassword = "wzphone2024" + } + getByName("debug") { + storeFile = file("${project.rootDir}/keystore/wzp-debug.jks") + storePassword = "android" + keyAlias = "wzp-debug" + keyPassword = "android" + } } buildTypes { + debug { + signingConfig = signingConfigs.getByName("debug") + isDebuggable = true + } release { + signingConfig = signingConfigs.getByName("release") isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), @@ -46,7 +66,7 @@ tasks.register("cargoNdkBuild") { workingDir = file("${project.rootDir}/..") commandLine( "cargo", "ndk", - "-t", "arm64-v8a", "-t", "armeabi-v7a", + "-t", "arm64-v8a", "-o", "${project.projectDir}/src/main/jniLibs", "build", "--release", "-p", "wzp-android" ) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 367de2d..28644f5 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -13,7 +13,7 @@ android:name=".WzpApplication" android:label="WZ Phone" android:supportsRtl="true" - android:theme="@style/Theme.Material3.DayNight"> + android:theme="@android:style/Theme.Material.Light.NoActionBar"> { + println!("cargo:warning=Building with Oboe from {:?}", oboe_path); + cc::Build::new() + .cpp(true) + .std("c++17") + .file("cpp/oboe_bridge.cpp") + .include("cpp") + .include(oboe_path.join("include")) + .define("WZP_HAS_OBOE", None) + .compile("oboe_bridge"); + } + None => { + println!("cargo:warning=Oboe not found, building with stub"); + cc::Build::new() + .cpp(true) + .std("c++17") + .file("cpp/oboe_stub.cpp") + .include("cpp") + .compile("oboe_bridge"); + } + } } else { - // Stub for host builds / testing + // Non-Android: always use stub cc::Build::new() .cpp(true) .std("c++17") @@ -18,3 +39,37 @@ fn main() { .compile("oboe_bridge"); } } + +/// Try to find or fetch Oboe headers. +/// Returns the path to the Oboe source root (containing include/ directory). +fn fetch_oboe() -> Option { + let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); + let oboe_dir = out_dir.join("oboe"); + + // Check if already fetched + if oboe_dir.join("include").join("oboe").join("Oboe.h").exists() { + return Some(oboe_dir); + } + + // Try to clone Oboe from GitHub + let status = std::process::Command::new("git") + .args([ + "clone", + "--depth=1", + "--branch=1.8.1", + "https://github.com/google/oboe.git", + oboe_dir.to_str().unwrap(), + ]) + .status(); + + match status { + Ok(s) if s.success() => { + if oboe_dir.join("include").join("oboe").join("Oboe.h").exists() { + Some(oboe_dir) + } else { + None + } + } + _ => None, + } +} diff --git a/crates/wzp-android/cpp/oboe_bridge.cpp b/crates/wzp-android/cpp/oboe_bridge.cpp index 2cb6afd..066bb61 100644 --- a/crates/wzp-android/cpp/oboe_bridge.cpp +++ b/crates/wzp-android/cpp/oboe_bridge.cpp @@ -79,7 +79,7 @@ public: oboe::AudioStream* stream, void* audioData, int32_t numFrames) override { - if (!g_running.load(std::std::memory_order_relaxed) || !g_rings) { + if (!g_running.load(std::memory_order_relaxed) || !g_rings) { return oboe::DataCallbackResult::Stop; } @@ -98,7 +98,7 @@ public: auto result = stream->calculateLatencyMillis(); if (result) { g_capture_latency_ms.store(static_cast(result.value()), - std::std::memory_order_relaxed); + std::memory_order_relaxed); } return oboe::DataCallbackResult::Continue; @@ -115,7 +115,7 @@ public: oboe::AudioStream* stream, void* audioData, int32_t numFrames) override { - if (!g_running.load(std::std::memory_order_relaxed) || !g_rings) { + if (!g_running.load(std::memory_order_relaxed) || !g_rings) { memset(audioData, 0, numFrames * sizeof(int16_t)); return oboe::DataCallbackResult::Stop; } @@ -140,7 +140,7 @@ public: auto result = stream->calculateLatencyMillis(); if (result) { g_playout_latency_ms.store(static_cast(result.value()), - std::std::memory_order_relaxed); + std::memory_order_relaxed); } return oboe::DataCallbackResult::Continue; @@ -155,7 +155,7 @@ static PlayoutCallback g_playout_cb; // --------------------------------------------------------------------------- int wzp_oboe_start(const WzpOboeConfig* config, const WzpOboeRings* rings) { - if (g_running.load(std::std::memory_order_relaxed)) { + if (g_running.load(std::memory_order_relaxed)) { LOGW("wzp_oboe_start: already running"); return -1; } @@ -200,13 +200,13 @@ int wzp_oboe_start(const WzpOboeConfig* config, const WzpOboeRings* rings) { return -3; } - g_running.store(true, std::std::memory_order_release); + g_running.store(true, std::memory_order_release); // Start both streams result = g_capture_stream->requestStart(); if (result != oboe::Result::OK) { LOGE("Failed to start capture: %s", oboe::convertToText(result)); - g_running.store(false, std::std::memory_order_release); + g_running.store(false, std::memory_order_release); g_capture_stream->close(); g_playout_stream->close(); g_capture_stream.reset(); @@ -217,7 +217,7 @@ int wzp_oboe_start(const WzpOboeConfig* config, const WzpOboeRings* rings) { result = g_playout_stream->requestStart(); if (result != oboe::Result::OK) { LOGE("Failed to start playout: %s", oboe::convertToText(result)); - g_running.store(false, std::std::memory_order_release); + g_running.store(false, std::memory_order_release); g_capture_stream->requestStop(); g_capture_stream->close(); g_playout_stream->close(); @@ -232,7 +232,7 @@ int wzp_oboe_start(const WzpOboeConfig* config, const WzpOboeRings* rings) { } void wzp_oboe_stop(void) { - g_running.store(false, std::std::memory_order_release); + g_running.store(false, std::memory_order_release); if (g_capture_stream) { g_capture_stream->requestStop(); @@ -250,15 +250,15 @@ void wzp_oboe_stop(void) { } float wzp_oboe_capture_latency_ms(void) { - return g_capture_latency_ms.load(std::std::memory_order_relaxed); + return g_capture_latency_ms.load(std::memory_order_relaxed); } float wzp_oboe_playout_latency_ms(void) { - return g_playout_latency_ms.load(std::std::memory_order_relaxed); + return g_playout_latency_ms.load(std::memory_order_relaxed); } int wzp_oboe_is_running(void) { - return g_running.load(std::std::memory_order_relaxed) ? 1 : 0; + return g_running.load(std::memory_order_relaxed) ? 1 : 0; } #else