跳转至

crates\nfarms-mobile\src\lib.rs

源码路径:crates\nfarms-mobile\src\lib.rs · 61 行

//! NFarms HUB 安卓版入口。
//!
//! 这一层很薄 —— 逻辑全在下面两层里复用:
//!
//! * `nfarms-core` 纯逻辑(路径 / 日志 / 设置 / 天气 / 拓扑 / libs 检测)
//! * `nfarms-gui`  egui 界面(侧栏 / 主题 / 图标 / 背景图 / logo / 天气页 …)
//!
//! 桌面版的入口是 `nfarms-app`(Windows 双模 exe:命令行诊断 + 界面);
//! 这边是 **cdylib**,由 Android 的 NativeActivity 通过 `android_main` 拉起来。
//!
//! ## 安卓和桌面版的差别,只有两处
//!
//! 1. **「程序根」从哪来**。桌面版按 exe 位置推(exe 在 `bin\` 下就上溯一层);
//!    安卓上 exe 在 APK 里、不可写,所以改用**应用私有数据目录**
//!    (`/data/data/<包名>/files`)。`Paths::from_exe_dir` 只在目录名恰好是
//!    `bin` 时才上溯,把数据目录传进去正好得到「数据目录 = 程序根」。
//! 2. **eframe 必须拿到 `AndroidApp`**(NativeActivity 的句柄),
//!    否则 `run_native` 会直接报 `NativeOptions is missing required android_app`。
//!
//! ## 哪些页面在安卓上有意义
//!
//! 能搬的:**天气**(wttr.in)、**拓扑验证器**(纯网络探测)、**设置**。
//! 搬不了的:Environment(winget / npm)、NF-Printer(Windows 打印 API)、
//! Harness(要拉桌面可执行文件)、Browser(WebView2)、NF-VPN(nfztna.exe)、
//! Pet(全局键鼠钩子 + 分层窗口)—— 这些页在安卓上仍是占位。

// 非安卓目标下这个 crate 是**空的**:它只是安卓的入口壳子。
// 这样 workspace 级的 `cargo build`(Windows)不会因为多了一个成员而失败。
#[cfg(target_os = "android")]
mod imp {
    use std::path::PathBuf;

    use nfarms_core::{settings, Log, Paths};

    /// 安卓入口。NativeActivity 会 dlopen 这个 .so 并调用它。
    ///
    /// 注意:这个签名要和 `android-activity` 的约定一致(它内部是按
    /// `extern "C" fn(*mut c_void)` 去 `dlsym` 的)。
    ///
    /// edition 2024 起 `no_mangle` 属于「不安全属性」,必须写成 `unsafe(no_mangle)`。
    #[unsafe(no_mangle)]
    pub fn android_main(app: winit::platform::android::activity::AndroidApp) {
        // 安卓上的「程序根」= 应用私有数据目录
        let root: PathBuf = app
            .internal_data_path()
            .unwrap_or_else(|| PathBuf::from("."));

        let paths = Paths::from_exe_dir(&root);
        let log = Log::detect(&paths);
        log.write(
            "gui",
            &format!("安卓版启动,程序根={}", paths.root().display()),
        );

        let settings_path = settings::default_path(&paths);
        if let Err(e) = nfarms_gui::run_android(app, &paths, &log, &settings_path) {
            log.write("gui", &format!("界面启动失败: {e}"));
        }
    }
}