add android icon

This commit is contained in:
netadmin committed 2026-11-02 22:13:43 +03:00
1 parent f22d954105
commit 55a69c743a
521 files changed
+8146 -14086

No files matched your search

+2 -2
View File
@@ -4,7 +4,6 @@ version.workspace = true
edition.workspace = true
[lib]
# Имя выходной библиотеки libramshare_ui.so совпадает с System.loadLibrary("ramshare_ui") в Kotlin
name = "ramshare_ui"
path = "src/lib.rs"
crate-type = ["cdylib"]
@@ -12,7 +11,8 @@ crate-type = ["cdylib"]
[dependencies]
ramshare-core = { path = "../core" }
tokio.workspace = true
tokio-util.workspace = true
tokio-rustls.workspace = true
axum.workspace = true
parking_lot.workspace = true
serde_json.workspace = true
jni = "0.21"
+354 -47
View File
@@ -1,18 +1,18 @@
use axum::Router;
use jni::{
objects::{JClass, JString},
sys::{jboolean, jint, JNI_FALSE, JNI_TRUE},
sys::{jboolean, jint, jlong, jstring, JNI_FALSE, JNI_TRUE},
JNIEnv,
};
use parking_lot::Mutex as PlMutex;
use ramshare_core::{api, init_app_state, models::AppState, net, p2p, storage, tls};
use ramshare_core::{api, init_app_state, models::*, net, p2p, storage, tls, transfer};
use std::{
net::Ipv4Addr,
net::IpAddr,
path::Path,
sync::{atomic::Ordering, Arc, OnceLock},
time::Duration,
};
use tokio::sync::Notify;
use tokio::{sync::Notify, task::JoinHandle};
use tokio_util::io::ReaderStream;
struct RunningNode {
state: Arc<AppState>,
@@ -33,40 +33,42 @@ fn rt() -> &'static tokio::runtime::Runtime {
})
}
fn jstr<'a>(env: &JNIEnv<'a>, s: &str) -> jstring {
env.new_string(s)
.map(|v| v.into_raw())
.unwrap_or(std::ptr::null_mut())
}
fn get_state() -> Result<Arc<AppState>, String> {
NODE.lock()
.as_ref()
.map(|n| n.state.clone())
.ok_or_else(|| "Локальное ядро остановлено".to_string())
}
#[no_mangle]
pub extern "system" fn Java_com_ramshare_app_RamshareNative_startServer<'local>(
pub extern "system" fn Java_com_ramshare_app_RamshareNative_startNode<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
base_dir: JString<'local>,
device_name: JString<'local>,
p2p_port: jint,
ui_port: jint,
) -> jboolean {
) -> jstring {
if NODE.lock().is_some() {
return JNI_TRUE;
return jstr(&env, "");
}
let base_str: String = match env.get_string(&base_dir) {
Ok(s) => s.into(),
Err(_) => return JNI_FALSE,
Err(e) => return jstr(&env, &format!("base_dir: {}", e)),
};
let dev_name: String = match env.get_string(&device_name) {
Ok(s) => s.into(),
Err(_) => "Android-Device".to_string(),
};
let p2p_port = (p2p_port as u16).max(1024);
let ui_port = (ui_port as u16).max(1024);
let dirs = match storage::resolve_dirs(Some(Path::new(&base_str)), p2p_port) {
Ok(d) => d,
Err(e) => {
eprintln!("[ANDROID ERR] resolve_dirs: {}", e);
return JNI_FALSE;
}
};
let (state, bind_ip) = match init_app_state(
dirs.config,
dirs.media,
Some(Path::new(&base_str)),
None,
&dev_name,
p2p_port,
@@ -76,10 +78,7 @@ pub extern "system" fn Java_com_ramshare_app_RamshareNative_startServer<'local>(
false,
) {
Ok(v) => v,
Err(e) => {
eprintln!("[ANDROID ERR] init_app_state: {}", e);
return JNI_FALSE;
}
Err(e) => return jstr(&env, &e),
};
let stop = Arc::new(Notify::new());
@@ -89,17 +88,21 @@ pub extern "system" fn Java_com_ramshare_app_RamshareNative_startServer<'local>(
let started = rt().block_on(async move {
let server_cfg = tls::server_config(&st.cert_der, &st.key_der)?;
let acceptor = tokio_rustls::TlsAcceptor::from(server_cfg);
let p2p_listener = tokio::net::TcpListener::bind((bind_ip, p2p_port))
.await
.map_err(|e| e.to_string())?;
let ui_listener = tokio::net::TcpListener::bind((Ipv4Addr::LOCALHOST, ui_port))
.await
.map_err(|e| e.to_string())?;
let p2p_listener = net::bind_p2p_listener(bind_ip, p2p_port)
.map_err(|e| format!("Порт {} занят или недоступен: {}", p2p_port, e))?;
net::start_discovery(st.clone());
st.add_log(format!(
"Ядро запущено: '{}' ({}:{}, ID: {})",
st.device_name,
st.current_ip(),
p2p_port,
short_id(&st.my_fingerprint)
));
let mut tasks: Vec<JoinHandle<()>> = net::start_discovery(st.clone());
let st_ping = st.clone();
let ping_task = tokio::spawn(async move {
tasks.push(tokio::spawn(async move {
net::ping_all_peers(&st_ping, false).await;
st_ping.maintenance();
loop {
@@ -111,17 +114,19 @@ pub extern "system" fn Java_com_ramshare_app_RamshareNative_startServer<'local>(
net::ping_all_peers(&st_ping, false).await;
st_ping.maintenance();
}
});
}));
let ui_router = api::control_router(Router::new(), st.clone(), ui_port);
let ui_task = tokio::spawn(async move { axum::serve(ui_listener, ui_router).await });
let p2p_task = tokio::spawn(p2p::serve(p2p_listener, acceptor, p2p::router(st.clone())));
tasks.push(tokio::spawn(p2p::serve(
p2p_listener,
acceptor,
p2p::router(st.clone()),
)));
tokio::spawn(async move {
stop_wait.notified().await;
ping_task.abort();
ui_task.abort();
p2p_task.abort();
for t in tasks {
t.abort();
}
});
Ok::<(), String>(())
@@ -130,24 +135,326 @@ pub extern "system" fn Java_com_ramshare_app_RamshareNative_startServer<'local>(
match started {
Ok(()) => {
*NODE.lock() = Some(RunningNode { state, stop });
JNI_TRUE
}
Err(e) => {
eprintln!("[ANDROID ERR] bind/start: {}", e);
JNI_FALSE
jstr(&env, "")
}
Err(e) => jstr(&env, &e),
}
}
#[no_mangle]
pub extern "system" fn Java_com_ramshare_app_RamshareNative_stopServer<'local>(
pub extern "system" fn Java_com_ramshare_app_RamshareNative_stopNode<'local>(
_env: JNIEnv<'local>,
_class: JClass<'local>,
) {
if let Some(node) = NODE.lock().take() {
node.state.sync_trust_from_disk();
node.state.persist_trust();
storage::cleanup_partials(&node.state.media_dir);
storage::cleanup_partials(&node.state.current_media_dir());
node.stop.notify_waiters();
}
}
#[no_mangle]
pub extern "system" fn Java_com_ramshare_app_RamshareNative_isNodeRunning<'local>(
_env: JNIEnv<'local>,
_class: JClass<'local>,
) -> jboolean {
if NODE.lock().is_some() {
JNI_TRUE
} else {
JNI_FALSE
}
}
#[no_mangle]
pub extern "system" fn Java_com_ramshare_app_RamshareNative_getStateJson<'local>(
env: JNIEnv<'local>,
_class: JClass<'local>,
) -> jstring {
let Ok(state) = get_state() else {
return jstr(&env, "");
};
let val = api::state_snapshot_json(&state);
jstr(&env, &val.to_string())
}
#[no_mangle]
pub extern "system" fn Java_com_ramshare_app_RamshareNative_pingAll<'local>(
env: JNIEnv<'local>,
_class: JClass<'local>,
) -> jstring {
let state = match get_state() {
Ok(s) => s,
Err(e) => return jstr(&env, &e),
};
rt().block_on(async move {
net::ping_all_peers(&state, true).await;
});
jstr(&env, "")
}
#[no_mangle]
pub extern "system" fn Java_com_ramshare_app_RamshareNative_pingPeer<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
peer_id: JString<'local>,
) -> jboolean {
let Ok(state) = get_state() else {
return JNI_FALSE;
};
let Ok(id_str) = env.get_string(&peer_id) else {
return JNI_FALSE;
};
let id: String = id_str.into();
let online = rt()
.block_on(async move { net::ping_peer_by_id(&state, &id).await })
.unwrap_or(false);
if online {
JNI_TRUE
} else {
JNI_FALSE
}
}
#[no_mangle]
pub extern "system" fn Java_com_ramshare_app_RamshareNative_addManualPeer<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
ip: JString<'local>,
port: jint,
) -> jstring {
let state = match get_state() {
Ok(s) => s,
Err(e) => return jstr(&env, &e),
};
let ip_raw: String = match env.get_string(&ip) {
Ok(s) => s.into(),
Err(_) => return jstr(&env, "Некорректная строка IP"),
};
let parsed_ip: IpAddr = match ip_raw.trim().parse() {
Ok(a) => a,
Err(_) => return jstr(&env, "Некорректный IP-адрес"),
};
match parsed_ip {
IpAddr::V4(v4) if is_lan_v4(v4) => {}
_ => return jstr(&env, "Разрешены только локальные IPv4-адреса"),
}
let target_port = if port > 0 && port <= 65535 {
port as u16
} else {
state.p2p_port
};
let res = rt().block_on(async move { net::probe_and_register(&state, parsed_ip, target_port).await });
match res {
Ok(_) => jstr(&env, ""),
Err(e) => jstr(&env, &format!("Узел {}: {}", parsed_ip, e)),
}
}
#[no_mangle]
pub extern "system" fn Java_com_ramshare_app_RamshareNative_scanRange<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
range: JString<'local>,
) -> jstring {
let state = match get_state() {
Ok(s) => s,
Err(e) => return jstr(&env, &e),
};
let range_str: String = match env.get_string(&range) {
Ok(s) => s.into(),
Err(_) => "all".to_string(),
};
if let Some(ip) = net::detect_lan_ip() {
state.update_my_ip(ip);
}
let my_ip = state.current_ip();
let (label, ips) = match parse_ip_range(&range_str, &my_ip) {
Ok(v) => v,
Err(e) => return jstr(&env, &e),
};
let total = ips.len();
let port = state.p2p_port;
{
let mut s = state.scan_status.write();
if s.active {
return jstr(&env, "Сканирование уже выполняется");
}
*s = ScanProgress {
active: true,
range: label.clone(),
scanned: 0,
total,
found: 0,
};
}
state.add_log(format!("Запущено сканирование {} ({} адресов)", label, total));
let st = state.clone();
rt().spawn(async move {
let worker = tokio::spawn(net::run_scan(st.clone(), ips, port));
let _ = worker.await;
let found = {
let mut s = st.scan_status.write();
s.active = false;
s.found
};
st.add_log(format!("Сканирование {} завершено. Найдено: {}", label, found));
});
jstr(&env, "")
}
#[no_mangle]
pub extern "system" fn Java_com_ramshare_app_RamshareNative_setTrust<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
peer_id: JString<'local>,
trust: jboolean,
) -> jstring {
let state = match get_state() {
Ok(s) => s,
Err(e) => return jstr(&env, &e),
};
let id: String = match env.get_string(&peer_id) {
Ok(s) => s.into(),
Err(_) => return jstr(&env, "Некорректный ID"),
};
let trust_bool = trust == JNI_TRUE;
let msg = {
let mut peers = state.peers.write();
let Some(p) = peers.get_mut(&id) else {
return jstr(&env, "Устройство не найдено");
};
p.verified = trust_bool;
if trust_bool {
p.fail_count = 0;
format!("Устройство '{}' [{}] ЗАВЕРЕНО", p.name, p.pin_code)
} else {
format!("Доверие устройству '{}' [{}] ОТОЗВАНО", p.name, p.pin_code)
}
};
state.persist_trust();
state.add_log(msg);
let st = state.clone();
rt().spawn(async move {
net::push_trust(&st, &id).await;
});
jstr(&env, "")
}
#[no_mangle]
pub extern "system" fn Java_com_ramshare_app_RamshareNative_setPingInterval<'local>(
env: JNIEnv<'local>,
_class: JClass<'local>,
sec: jint,
) -> jstring {
let state = match get_state() {
Ok(s) => s,
Err(e) => return jstr(&env, &e),
};
match state.set_ping_interval(sec.max(0) as u64) {
Ok(()) => jstr(&env, ""),
Err(e) => jstr(&env, &e),
}
}
#[no_mangle]
pub extern "system" fn Java_com_ramshare_app_RamshareNative_setMediaDir<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
path: JString<'local>,
) -> jstring {
let state = match get_state() {
Ok(s) => s,
Err(e) => return jstr(&env, &e),
};
let raw: String = match env.get_string(&path) {
Ok(s) => s.into(),
Err(_) => String::new(),
};
match state.set_media_dir(Some(&raw)) {
Ok(_) => jstr(&env, ""),
Err(e) => jstr(&env, &e),
}
}
#[no_mangle]
pub extern "system" fn Java_com_ramshare_app_RamshareNative_clearRam<'local>(
_env: JNIEnv<'local>,
_class: JClass<'local>,
) {
if let Ok(state) = get_state() {
state.ram_texts.write().clear();
state.add_log("Текстовый буфер в ОЗУ очищен");
}
}
#[no_mangle]
pub extern "system" fn Java_com_ramshare_app_RamshareNative_sendText<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
peer_id: JString<'local>,
text: JString<'local>,
) -> jstring {
let state = match get_state() {
Ok(s) => s,
Err(e) => return jstr(&env, &e),
};
let id: String = match env.get_string(&peer_id) {
Ok(s) => s.into(),
Err(_) => return jstr(&env, "Не указан получатель"),
};
let txt: String = match env.get_string(&text) {
Ok(s) => s.into(),
Err(_) => return jstr(&env, "Некорректный текст"),
};
let res = rt().block_on(async move { transfer::send_text(&state, &id, txt).await });
match res {
Ok(_) => jstr(&env, ""),
Err(e) => jstr(&env, &e.1),
}
}
#[no_mangle]
pub extern "system" fn Java_com_ramshare_app_RamshareNative_sendFileFd<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
peer_id: JString<'local>,
file_name: JString<'local>,
fd: jint,
total_size: jlong,
) -> jstring {
use std::os::unix::io::FromRawFd;
if fd < 0 {
return jstr(&env, "Некорректный файловый дескриптор");
}
let std_file = unsafe { std::fs::File::from_raw_fd(fd) };
let state = match get_state() {
Ok(s) => s,
Err(e) => return jstr(&env, &e),
};
let id: String = match env.get_string(&peer_id) {
Ok(s) => s.into(),
Err(_) => return jstr(&env, "Не указан получатель"),
};
let name: String = match env.get_string(&file_name) {
Ok(s) => s.into(),
Err(_) => "file.bin".to_string(),
};
let total = total_size.max(0) as u64;
let res = rt().block_on(async move {
let tokio_file = tokio::fs::File::from_std(std_file);
let stream = ReaderStream::with_capacity(tokio_file, 256 * 1024);
transfer::send_stream(&state, &id, &name, total, stream).await
});
match res {
Ok(_) => jstr(&env, ""),
Err(e) => jstr(&env, &e.1),
}
}