More basic Android stuff (#910)

* enable tokio

* data dirs

* jni pointer

* more

* more

* more

* more

* more

* more

* more

* more
This commit is contained in:
Logan McNaughton
2026-05-18 17:08:01 +02:00
committed by GitHub
parent 8dde32cb21
commit a78393ca91
6 changed files with 50 additions and 5 deletions
+2 -2
View File
@@ -267,9 +267,9 @@ jobs:
cache: ${{ github.ref_type != 'tag' }}
- name: Build
run: |
cargo install cargo-apk cargo-ndk
cargo install cargo-apk2 cargo-ndk
source <(cargo ndk-env --target ${{ matrix.target }})
cargo apk build --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }} --lib
cargo apk2 build --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }} --lib
collect-artifacts:
name: Collect release artifacts
+13 -1
View File
@@ -12,7 +12,6 @@ gui = ["dep:rfd", "dep:slint", "dep:slint-build"]
crate-type = ["cdylib", "rlib"]
[dependencies]
dirs = "6.0"
zip = "8.0"
sevenz-rust2 = "0.21"
chrono = "0.4"
@@ -43,6 +42,7 @@ discord-rich-presence = "1.1"
[target.'cfg(not(target_os = "android"))'.dependencies]
slint = { version = "1.16", default-features = false, features = ["compat-1-2", "std", "unstable-winit-030", "backend-winit", "renderer-femtovg-wgpu", "accessibility"], optional = true }
rfd = {version = "0.17", optional = true }
dirs = "6.0"
[target.'cfg(target_os = "android")'.dependencies]
slint = { version = "1.16", default-features = false, features = ["compat-1-2", "std", "backend-android-activity-06", "accessibility"], optional = true }
@@ -73,10 +73,22 @@ osx_frameworks = ["/opt/homebrew/opt/molten-vk/lib/libMoltenVK.dylib"]
osx_minimum_system_version = "15.0"
[package.metadata.android]
use_aapt2 = true
package = "io.github.gopher64.gopher64"
build_targets = ["aarch64-linux-android"]
resources = "data/android/res"
[[package.metadata.android.application.activity]]
name = "android.app.NativeActivity"
[[package.metadata.android.application.activity.intent_filter]]
actions = ["android.intent.action.MAIN"]
categories = ["android.intent.category.LAUNCHER"]
[[package.metadata.android.application.activity.meta_data]]
name = "android.app.lib_name"
value = "gopher64"
[package.metadata.android.application]
icon = "@mipmap/ic_launcher"
label = "Gopher64"
+12 -2
View File
@@ -317,8 +317,18 @@ pub async fn run() -> std::io::Result<()> {
#[cfg(target_os = "android")]
#[unsafe(no_mangle)]
fn android_main(app: slint::android::AndroidApp) {
slint::android::init(app).unwrap();
#[tokio::main(worker_threads = 4)]
async fn android_main(app: slint::android::AndroidApp) {
slint::android::init(app.clone()).unwrap();
ui::ANDROID_APP.set(app).unwrap();
let dirs = ui::get_dirs();
std::fs::create_dir_all(dirs.config_dir).unwrap();
std::fs::create_dir_all(dirs.cache_dir).unwrap();
std::fs::create_dir_all(dirs.data_dir.join("saves")).unwrap();
std::fs::create_dir_all(dirs.data_dir.join("states")).unwrap();
ui::gui::app_window();
}
+18
View File
@@ -15,6 +15,10 @@ pub mod video;
#[cfg(feature = "gui")]
pub mod vru;
#[cfg(target_os = "android")]
pub static ANDROID_APP: std::sync::OnceLock<slint::android::AndroidApp> =
std::sync::OnceLock::new();
pub static WEB_CLIENT: std::sync::LazyLock<reqwest::Client> = std::sync::LazyLock::new(|| {
reqwest::Client::builder()
.user_agent(format!(
@@ -125,6 +129,7 @@ pub fn sdl_close() {
}
}
#[cfg(not(target_os = "android"))]
pub fn get_dirs() -> Dirs {
let exe_path = std::env::current_exe().unwrap();
let portable_dir = exe_path.parent();
@@ -144,6 +149,19 @@ pub fn get_dirs() -> Dirs {
}
}
#[cfg(target_os = "android")]
pub fn get_dirs() -> Dirs {
if let Some(android_app) = ANDROID_APP.get() {
Dirs {
config_dir: android_app.internal_data_path().unwrap().join("config"),
data_dir: android_app.external_data_path().unwrap().join("data"),
cache_dir: android_app.internal_data_path().unwrap().join("cache"),
}
} else {
panic!("Android app not initialized");
}
}
impl Ui {
pub fn new() -> Ui {
let dirs = get_dirs();
+4
View File
@@ -152,6 +152,7 @@ fn local_game_window(app: &AppWindow, config: &ui::config::Config) {
file_dropped(app);
}
#[cfg(not(target_os = "android"))]
fn input_profiles(config: &ui::config::Config) -> Vec<String> {
let mut profiles = vec![];
for key in config.input.input_profiles.keys() {
@@ -190,6 +191,7 @@ fn settings_window(app: &AppWindow, config: &ui::config::Config) {
}
}
#[cfg(not(target_os = "android"))]
fn update_input_profiles(weak: &slint::Weak<AppWindow>, config: &ui::config::Config) {
let profiles = input_profiles(config);
let config_bindings = config.input.input_profile_binding.clone();
@@ -235,6 +237,7 @@ fn clear_gb_paths(weak: &slint::Weak<AppWindow>, player: i32) {
.unwrap();
}
#[cfg(not(target_os = "android"))]
fn controller_window(app: &AppWindow, config: &ui::config::Config) {
ui::sdl_init(sdl3_sys::init::SDL_INIT_GAMEPAD);
@@ -533,6 +536,7 @@ pub fn app_window() {
{
let config = ui::config::Config::new();
settings_window(&app, &config);
#[cfg(not(target_os = "android"))]
controller_window(&app, &config);
local_game_window(&app, &config);
}
+1
View File
@@ -359,6 +359,7 @@ pub fn get_controller_names() -> Vec<String> {
controllers
}
#[cfg(not(target_os = "android"))]
#[cfg(feature = "gui")]
pub fn get_controller_paths() -> Vec<String> {
let mut controller_paths: Vec<String> = vec![];