feat: android build

This commit is contained in:
kieran 2024-10-17 20:06:37 +01:00
parent 6017ce18d4
commit 91f0baf75c
No known key found for this signature in database
GPG Key ID: DE71CEB3925BE941
11 changed files with 501 additions and 318 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
/data.mdb
/.idea
/cache
/ffmpeg-kit

713
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -19,32 +19,17 @@ pretty_env_logger = "0.5.0"
egui_inbox = "0.6.0"
bech32 = "0.11.0"
libc = "0.2.158"
egui-video = { git = "https://github.com/v0l/egui-video.git", rev = "4766d939ce4d34b5a3a57b2fbe750ea10f389f39" }
uuid = { version = "1.11.0", features = ["v4"] }
chrono = "0.4.38"
anyhow = "1.0.89"
async-trait = "0.1.83"
sha2 = "0.10.8"
reqwest = { version = "0.12.7", default-features = false, features = [ "rustls-tls-native-roots" ] }
reqwest = { version = "0.12.7", default-features = false, features = ["rustls-tls-native-roots"] }
itertools = "0.13.0"
egui-video = { git = "https://github.com/v0l/egui-video.git", rev = "7e9f8dbc2c7d69dd2f327b18f6dc3687d65129b4" }
[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.14.1"
android-activity = { version = "0.6.0", features = [ "native-activity" ] }
winit = { version = "0.30.5", features = [ "android-native-activity" ] }
[package.metadata.android]
package = "stream.zap.app"
build_targets = [ "aarch64-linux-android" ]
#build_targets = [ "armv7-linux-androideabi" ]
large_heap = true
[package.metadata.android.sdk]
min_sdk_version = 20
target_sdk_version = 32
[package.metadata.android.application]
extract_native_libs = true
[package.metadata.android.application.activity]
config_changes = "orientation"
android-activity = { version = "0.6.0", features = ["native-activity"] }
winit = { version = "0.30.5", features = ["android-native-activity"] }

28
android.sh Executable file
View File

@ -0,0 +1,28 @@
#!/bin/bash
git clone https://github.com/v0l/ffmpeg-kit.git
export ANDROID_SDK_ROOT=$ANDROID_HOME
#cd ffmpeg-kit && ./android.sh \
# --disable-x86 \
# --disable-x86-64 \
# --disable-arm-v7a \
# --disable-arm-v7a-neon \
# --no-ffmpeg-kit-protocols \
# --no-archive
if [[ $? -ne 0 ]]; then
exit 1;
fi
NDK_VER="28.0.12433566"
ARCH="arm64"
PLATFORM="android"
TRIPLET="aarch64-linux-android"
export PKG_CONFIG_SYSROOT_DIR="/"
export FFMPEG_DIR="$(pwd)/ffmpeg-kit/prebuilt/$PLATFORM-$ARCH/ffmpeg"
# DIRTY HACK !!
cp "$ANDROID_HOME/ndk/$NDK_VER/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/$TRIPLET/35/libcamera2ndk.so" \
./target/x/debug/android/$ARCH/cargo/$TRIPLET/debug/deps
x build --arch $ARCH --platform $PLATFORM --verbose

10
manifest.yaml Normal file
View File

@ -0,0 +1,10 @@
android:
manifest:
package: stream.zap.app
uses_permission:
- name: "android.permission.WRITE_EXTERNAL_STORAGE"
- name: "android.permission.READ_EXTERNAL_STORAGE"
- name: "android.permission.INTERNET"
uses_feature:
- name: "android.hardware.vulkan.level"
required: true

View File

@ -1,9 +1,10 @@
use crate::route::Router;
use eframe::{App, CreationContext, Frame};
use egui::{Color32, Context, Pos2, Rect, Rounding};
use egui::{Color32, Context};
use nostr_sdk::database::MemoryDatabase;
use nostr_sdk::{Client, RelayPoolNotification};
use nostrdb::{Config, Ndb};
use std::path::PathBuf;
use tokio::sync::broadcast;
pub struct ZapStreamApp {
@ -13,7 +14,7 @@ pub struct ZapStreamApp {
}
impl ZapStreamApp {
pub fn new(cc: &CreationContext) -> Self {
pub fn new(cc: &CreationContext, data_path: PathBuf) -> Self {
let client = Client::builder()
.database(MemoryDatabase::with_opts(Default::default()))
.build();
@ -34,12 +35,15 @@ impl ZapStreamApp {
});
egui_extras::install_image_loaders(&cc.egui_ctx);
let ndb = Ndb::new(".", &Config::default()).unwrap();
let ndb_path = data_path.join("ndb");
std::fs::create_dir_all(&ndb_path).expect("Failed to create ndb directory");
let ndb = Ndb::new(ndb_path.to_str().unwrap(), &Config::default()).unwrap();
Self {
client: client.clone(),
notifications,
router: Router::new(cc.egui_ctx.clone(), client.clone(), ndb.clone()),
router: Router::new(data_path, cc.egui_ctx.clone(), client.clone(), ndb.clone()),
}
}
}

View File

@ -1,5 +1,6 @@
use eframe::Renderer;
use egui::Vec2;
use std::path::PathBuf;
use zap_stream_app::app::ZapStreamApp;
#[tokio::main]
@ -12,9 +13,10 @@ async fn main() {
options.renderer = Renderer::Glow;
options.viewport = options.viewport.with_inner_size(Vec2::new(360., 720.));
let data_path = PathBuf::from(".");
let _res = eframe::run_native(
"zap.stream",
options,
Box::new(move |cc| Ok(Box::new(ZapStreamApp::new(cc)))),
Box::new(move |cc| Ok(Box::new(ZapStreamApp::new(cc, data_path)))),
);
}

View File

@ -21,7 +21,7 @@ use winit::platform::android::EventLoopBuilderExtAndroid;
#[tokio::main]
pub async fn android_main(app: AndroidApp) {
std::env::set_var("RUST_BACKTRACE", "full");
android_logger::init_once(android_logger::Config::default().with_min_level(log::Level::Info));
android_logger::init_once(android_logger::Config::default().with_max_level(log::LevelFilter::Info));
let mut options = eframe::NativeOptions::default();
options.renderer = Renderer::Glow;
@ -31,9 +31,14 @@ pub async fn android_main(app: AndroidApp) {
builder.with_android_app(app_clone_for_event_loop);
}));
let external_data_path = app
.external_data_path()
.expect("external data path")
.to_path_buf();
let _res = eframe::run_native(
"zap.stream",
options,
Box::new(move |cc| Ok(Box::new(ZapStreamApp::new(cc)))),
Box::new(move |cc| Ok(Box::new(ZapStreamApp::new(cc, external_data_path)))),
);
}

View File

@ -14,6 +14,7 @@ use nostr_sdk::nips::nip01;
use nostr_sdk::{Client, Kind, PublicKey};
use nostrdb::{Filter, Ndb, Note, Transaction};
use std::borrow::Borrow;
use std::path::PathBuf;
mod home;
mod stream;
@ -52,7 +53,7 @@ pub struct Router {
}
impl Router {
pub fn new(ctx: Context, client: Client, ndb: Ndb) -> Self {
pub fn new(data_path: PathBuf, ctx: Context, client: Client, ndb: Ndb) -> Self {
Self {
current: Routes::HomePage,
current_widget: None,
@ -61,7 +62,7 @@ impl Router {
ndb: NDBWrapper::new(ctx.clone(), ndb.clone(), client.clone()),
client,
login: None,
image_cache: ImageCache::new(ctx.clone()),
image_cache: ImageCache::new(data_path, ctx.clone()),
}
}

View File

@ -17,8 +17,8 @@ pub struct ImageCache {
}
impl ImageCache {
pub fn new(ctx: egui::Context) -> Self {
let out = PathBuf::from("./cache/images");
pub fn new(data_path: PathBuf, ctx: egui::Context) -> Self {
let out = data_path.join("cache/images");
fs::create_dir_all(&out).unwrap();
Self {
ctx,

View File

@ -1,18 +1,21 @@
use crate::widgets::VideoPlaceholder;
use egui::{Context, Response, Ui, Vec2, Widget};
use egui_video::Player;
use egui_video::{AudioDevice, Player};
pub struct StreamPlayer {
player: Option<Player>,
audio: AudioDevice,
}
impl StreamPlayer {
pub fn new(ctx: &Context, url: &String) -> Self {
let mut audio = AudioDevice::new().unwrap();
Self {
player: Player::new(ctx, url).map_or(None, |mut f| {
f.start();
Some(f)
}),
audio,
}
}
}
@ -24,6 +27,7 @@ impl Widget for &mut StreamPlayer {
let size = Vec2::new(w, h);
if let Some(mut p) = self.player.as_mut() {
p.add_audio(&mut self.audio).expect("Failed to add audio to stream player");
p.ui(ui, size)
} else {
VideoPlaceholder.ui(ui)