feat: android build
This commit is contained in:
parent
6017ce18d4
commit
91f0baf75c
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,4 +2,5 @@
|
|||||||
/lock.mdb
|
/lock.mdb
|
||||||
/data.mdb
|
/data.mdb
|
||||||
/.idea
|
/.idea
|
||||||
/cache
|
/cache
|
||||||
|
/ffmpeg-kit
|
713
Cargo.lock
generated
713
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
25
Cargo.toml
25
Cargo.toml
@ -19,32 +19,17 @@ pretty_env_logger = "0.5.0"
|
|||||||
egui_inbox = "0.6.0"
|
egui_inbox = "0.6.0"
|
||||||
bech32 = "0.11.0"
|
bech32 = "0.11.0"
|
||||||
libc = "0.2.158"
|
libc = "0.2.158"
|
||||||
egui-video = { git = "https://github.com/v0l/egui-video.git", rev = "4766d939ce4d34b5a3a57b2fbe750ea10f389f39" }
|
|
||||||
uuid = { version = "1.11.0", features = ["v4"] }
|
uuid = { version = "1.11.0", features = ["v4"] }
|
||||||
chrono = "0.4.38"
|
chrono = "0.4.38"
|
||||||
anyhow = "1.0.89"
|
anyhow = "1.0.89"
|
||||||
async-trait = "0.1.83"
|
async-trait = "0.1.83"
|
||||||
sha2 = "0.10.8"
|
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"
|
itertools = "0.13.0"
|
||||||
|
|
||||||
|
egui-video = { git = "https://github.com/v0l/egui-video.git", rev = "7e9f8dbc2c7d69dd2f327b18f6dc3687d65129b4" }
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
android_logger = "0.14.1"
|
android_logger = "0.14.1"
|
||||||
android-activity = { version = "0.6.0", features = [ "native-activity" ] }
|
android-activity = { version = "0.6.0", features = ["native-activity"] }
|
||||||
winit = { version = "0.30.5", features = [ "android-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"
|
|
28
android.sh
Executable file
28
android.sh
Executable 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
10
manifest.yaml
Normal 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
|
12
src/app.rs
12
src/app.rs
@ -1,9 +1,10 @@
|
|||||||
use crate::route::Router;
|
use crate::route::Router;
|
||||||
use eframe::{App, CreationContext, Frame};
|
use eframe::{App, CreationContext, Frame};
|
||||||
use egui::{Color32, Context, Pos2, Rect, Rounding};
|
use egui::{Color32, Context};
|
||||||
use nostr_sdk::database::MemoryDatabase;
|
use nostr_sdk::database::MemoryDatabase;
|
||||||
use nostr_sdk::{Client, RelayPoolNotification};
|
use nostr_sdk::{Client, RelayPoolNotification};
|
||||||
use nostrdb::{Config, Ndb};
|
use nostrdb::{Config, Ndb};
|
||||||
|
use std::path::PathBuf;
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
pub struct ZapStreamApp {
|
pub struct ZapStreamApp {
|
||||||
@ -13,7 +14,7 @@ pub struct ZapStreamApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ZapStreamApp {
|
impl ZapStreamApp {
|
||||||
pub fn new(cc: &CreationContext) -> Self {
|
pub fn new(cc: &CreationContext, data_path: PathBuf) -> Self {
|
||||||
let client = Client::builder()
|
let client = Client::builder()
|
||||||
.database(MemoryDatabase::with_opts(Default::default()))
|
.database(MemoryDatabase::with_opts(Default::default()))
|
||||||
.build();
|
.build();
|
||||||
@ -34,12 +35,15 @@ impl ZapStreamApp {
|
|||||||
});
|
});
|
||||||
egui_extras::install_image_loaders(&cc.egui_ctx);
|
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 {
|
Self {
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
notifications,
|
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()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use eframe::Renderer;
|
use eframe::Renderer;
|
||||||
use egui::Vec2;
|
use egui::Vec2;
|
||||||
|
use std::path::PathBuf;
|
||||||
use zap_stream_app::app::ZapStreamApp;
|
use zap_stream_app::app::ZapStreamApp;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@ -12,9 +13,10 @@ async fn main() {
|
|||||||
options.renderer = Renderer::Glow;
|
options.renderer = Renderer::Glow;
|
||||||
options.viewport = options.viewport.with_inner_size(Vec2::new(360., 720.));
|
options.viewport = options.viewport.with_inner_size(Vec2::new(360., 720.));
|
||||||
|
|
||||||
|
let data_path = PathBuf::from(".");
|
||||||
let _res = eframe::run_native(
|
let _res = eframe::run_native(
|
||||||
"zap.stream",
|
"zap.stream",
|
||||||
options,
|
options,
|
||||||
Box::new(move |cc| Ok(Box::new(ZapStreamApp::new(cc)))),
|
Box::new(move |cc| Ok(Box::new(ZapStreamApp::new(cc, data_path)))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ use winit::platform::android::EventLoopBuilderExtAndroid;
|
|||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
pub async fn android_main(app: AndroidApp) {
|
pub async fn android_main(app: AndroidApp) {
|
||||||
std::env::set_var("RUST_BACKTRACE", "full");
|
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();
|
let mut options = eframe::NativeOptions::default();
|
||||||
options.renderer = Renderer::Glow;
|
options.renderer = Renderer::Glow;
|
||||||
@ -31,9 +31,14 @@ pub async fn android_main(app: AndroidApp) {
|
|||||||
builder.with_android_app(app_clone_for_event_loop);
|
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(
|
let _res = eframe::run_native(
|
||||||
"zap.stream",
|
"zap.stream",
|
||||||
options,
|
options,
|
||||||
Box::new(move |cc| Ok(Box::new(ZapStreamApp::new(cc)))),
|
Box::new(move |cc| Ok(Box::new(ZapStreamApp::new(cc, external_data_path)))),
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -14,6 +14,7 @@ use nostr_sdk::nips::nip01;
|
|||||||
use nostr_sdk::{Client, Kind, PublicKey};
|
use nostr_sdk::{Client, Kind, PublicKey};
|
||||||
use nostrdb::{Filter, Ndb, Note, Transaction};
|
use nostrdb::{Filter, Ndb, Note, Transaction};
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
mod home;
|
mod home;
|
||||||
mod stream;
|
mod stream;
|
||||||
@ -52,7 +53,7 @@ pub struct Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
Self {
|
||||||
current: Routes::HomePage,
|
current: Routes::HomePage,
|
||||||
current_widget: None,
|
current_widget: None,
|
||||||
@ -61,7 +62,7 @@ impl Router {
|
|||||||
ndb: NDBWrapper::new(ctx.clone(), ndb.clone(), client.clone()),
|
ndb: NDBWrapper::new(ctx.clone(), ndb.clone(), client.clone()),
|
||||||
client,
|
client,
|
||||||
login: None,
|
login: None,
|
||||||
image_cache: ImageCache::new(ctx.clone()),
|
image_cache: ImageCache::new(data_path, ctx.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,8 +17,8 @@ pub struct ImageCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ImageCache {
|
impl ImageCache {
|
||||||
pub fn new(ctx: egui::Context) -> Self {
|
pub fn new(data_path: PathBuf, ctx: egui::Context) -> Self {
|
||||||
let out = PathBuf::from("./cache/images");
|
let out = data_path.join("cache/images");
|
||||||
fs::create_dir_all(&out).unwrap();
|
fs::create_dir_all(&out).unwrap();
|
||||||
Self {
|
Self {
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
use crate::widgets::VideoPlaceholder;
|
use crate::widgets::VideoPlaceholder;
|
||||||
use egui::{Context, Response, Ui, Vec2, Widget};
|
use egui::{Context, Response, Ui, Vec2, Widget};
|
||||||
use egui_video::Player;
|
use egui_video::{AudioDevice, Player};
|
||||||
|
|
||||||
pub struct StreamPlayer {
|
pub struct StreamPlayer {
|
||||||
player: Option<Player>,
|
player: Option<Player>,
|
||||||
|
audio: AudioDevice,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StreamPlayer {
|
impl StreamPlayer {
|
||||||
pub fn new(ctx: &Context, url: &String) -> Self {
|
pub fn new(ctx: &Context, url: &String) -> Self {
|
||||||
|
let mut audio = AudioDevice::new().unwrap();
|
||||||
Self {
|
Self {
|
||||||
player: Player::new(ctx, url).map_or(None, |mut f| {
|
player: Player::new(ctx, url).map_or(None, |mut f| {
|
||||||
f.start();
|
f.start();
|
||||||
Some(f)
|
Some(f)
|
||||||
}),
|
}),
|
||||||
|
audio,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -24,6 +27,7 @@ impl Widget for &mut StreamPlayer {
|
|||||||
let size = Vec2::new(w, h);
|
let size = Vec2::new(w, h);
|
||||||
|
|
||||||
if let Some(mut p) = self.player.as_mut() {
|
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)
|
p.ui(ui, size)
|
||||||
} else {
|
} else {
|
||||||
VideoPlaceholder.ui(ui)
|
VideoPlaceholder.ui(ui)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user