From d5aaad83a8d3f5b9d49badf5db784e387f7878dc Mon Sep 17 00:00:00 2001 From: neoapps-dev Date: Mon, 17 Aug 2026 14:29:41 +0300 Subject: [PATCH] feat: android settings --- DiamondRuntime | 2 +- src-tauri/src/android_runtime.rs | 6 +++ src-tauri/src/commands/game.rs | 60 +++++++++++++++++++++ src-tauri/src/config.rs | 2 + src-tauri/src/lib.rs | 3 ++ src-tauri/src/types.rs | 2 + src/components/views/SettingsView.tsx | 78 ++++++++++++++++++++++----- src/context/LauncherContext.tsx | 2 + src/hooks/useAppConfig.ts | 12 ++++- src/services/TauriService.ts | 14 +++++ 10 files changed, 165 insertions(+), 16 deletions(-) diff --git a/DiamondRuntime b/DiamondRuntime index bfe5ee4..a7631ba 160000 --- a/DiamondRuntime +++ b/DiamondRuntime @@ -1 +1 @@ -Subproject commit bfe5ee43aa406049c7645dbe8891a445621bc02f +Subproject commit a7631bab90e8c2fe4322ffb96a50e38f15ef4f0e diff --git a/src-tauri/src/android_runtime.rs b/src-tauri/src/android_runtime.rs index 4e77094..ffe0ee9 100644 --- a/src-tauri/src/android_runtime.rs +++ b/src-tauri/src/android_runtime.rs @@ -2,6 +2,9 @@ pub enum BridgeAction { Play, OpenContainer, OpenSettings, + SwitchProton, + InstallDriver, + SetAudioBackend, } pub fn launch_bridge( @@ -80,6 +83,9 @@ pub fn launch_bridge( BridgeAction::Play => "play", BridgeAction::OpenContainer => "open", BridgeAction::OpenSettings => "settings", + BridgeAction::SwitchProton => "switch_proton", + BridgeAction::InstallDriver => "install_driver", + BridgeAction::SetAudioBackend => "set_audio_backend", } .to_string(); dispatch(move |env, activity, _webview| { diff --git a/src-tauri/src/commands/game.rs b/src-tauri/src/commands/game.rs index 89073c9..bb7dc56 100644 --- a/src-tauri/src/commands/game.rs +++ b/src-tauri/src/commands/game.rs @@ -783,3 +783,63 @@ async fn perform_instance_sync(app: &AppHandle, instance_id: &str) -> Result<(), perform_dlc_sync(app, &target_dir)?; Ok(()) } + +#[tauri::command] +#[cfg(target_os = "android")] +pub fn switch_proton(app: AppHandle, version: String) -> Result<(), String> { + let instance_id = { + let config_val = config::load_config_raw(app.clone()); + config_val.profile.unwrap_or_else(|| "legacy_evolved".into()) + }; + crate::android_runtime::launch_bridge( + instance_id, + crate::android_runtime::BridgeAction::SwitchProton, + vec![version], + ) +} + +#[tauri::command] +#[cfg(not(target_os = "android"))] +pub fn switch_proton(_app: AppHandle, _version: String) -> Result<(), String> { + Err("Only supported on Android".into()) +} + +#[tauri::command] +#[cfg(target_os = "android")] +pub fn install_latest_driver(app: AppHandle) -> Result<(), String> { + let instance_id = { + let config_val = config::load_config_raw(app.clone()); + config_val.profile.unwrap_or_else(|| "legacy_evolved".into()) + }; + crate::android_runtime::launch_bridge( + instance_id, + crate::android_runtime::BridgeAction::InstallDriver, + Vec::new(), + ) +} + +#[tauri::command] +#[cfg(not(target_os = "android"))] +pub fn install_latest_driver(_app: AppHandle) -> Result<(), String> { + Err("Only supported on Android".into()) +} + +#[tauri::command] +#[cfg(target_os = "android")] +pub fn set_audio_backend(app: AppHandle, backend: String) -> Result<(), String> { + let instance_id = { + let config_val = config::load_config_raw(app.clone()); + config_val.profile.unwrap_or_else(|| "legacy_evolved".into()) + }; + crate::android_runtime::launch_bridge( + instance_id, + crate::android_runtime::BridgeAction::SetAudioBackend, + vec![backend], + ) +} + +#[tauri::command] +#[cfg(not(target_os = "android"))] +pub fn set_audio_backend(_app: AppHandle, _backend: String) -> Result<(), String> { + Err("Only supported on Android".into()) +} diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index ecac205..28bd1fb 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -38,6 +38,8 @@ pub fn load_config_raw(app: AppHandle) -> AppConfig { launch_prefix: None, launch_env_vars: None, instance_launch_args: None, + android_runner: None, + android_audio_backend: None, } } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index bfe61d8..4008f46 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -144,6 +144,9 @@ pub fn run() { plugins::list_directory, plugins::create_plugin_dir, plugins::remove_plugin_dir, + game::switch_proton, + game::install_latest_driver, + game::set_audio_backend, ]) .setup(|app| { let app_handle = app.handle().clone(); diff --git a/src-tauri/src/types.rs b/src-tauri/src/types.rs index 621f4b3..e877357 100644 --- a/src-tauri/src/types.rs +++ b/src-tauri/src/types.rs @@ -67,6 +67,8 @@ pub struct AppConfig { pub launch_prefix: Option, pub launch_env_vars: Option>, pub instance_launch_args: Option>, + pub android_runner: Option, + pub android_audio_backend: Option, } #[derive(Serialize, Deserialize, Clone, Debug)] diff --git a/src/components/views/SettingsView.tsx b/src/components/views/SettingsView.tsx index 4935765..11601fc 100644 --- a/src/components/views/SettingsView.tsx +++ b/src/components/views/SettingsView.tsx @@ -44,6 +44,10 @@ const SettingsView = memo(function SettingsView() { skipIntro, setSkipIntro, profile, + androidRunner, + setAndroidRunner, + androidAudioBackend, + setAndroidAudioBackend, } = useConfig(); const { currentTrack, skipTrack, tracks, playPressSound, playBackSound } = useAudio(); @@ -57,7 +61,7 @@ const SettingsView = memo(function SettingsView() { const { isLinux, isMac, isAndroid } = usePlatform(); const [focusIndex, setFocusIndex] = useState(null); const [currentSubMenu, setCurrentSubMenu] = useState< - "main" | "audio" | "video" | "launcher" | "game" | "plugins" + "main" | "audio" | "video" | "launcher" | "game" | "plugins" | "android" >("main"); const [runners, setRunners] = useState([]); const [pluginsInfo, setPluginsInfo] = useState([]); @@ -324,23 +328,15 @@ const SettingsView = memo(function SettingsView() { setFocusIndex(0); }, }); - if (isAndroid && profile) { + if (isAndroid) { items.push({ - id: "container_settings", - label: "Container Settings", + id: "android_menu", + label: "Android", type: "button", onClick: () => { playPressSound(); - TauriService.openContainerSettings(profile).catch(console.error); - }, - }); - items.push({ - id: "open_container", - label: "Open Container", - type: "button", - onClick: () => { - playPressSound(); - TauriService.openInstanceFolder(profile).catch(console.error); + setCurrentSubMenu("android"); + setFocusIndex(0); }, }); } @@ -544,6 +540,58 @@ const SettingsView = memo(function SettingsView() { onClick: handleResetSetup, color: "orange", }); + } else if (currentSubMenu === "android") { + if (profile) { + items.push({ + id: "android_container_settings", + label: "Container Settings", + type: "button", + onClick: () => { + playPressSound(); + TauriService.openContainerSettings(profile).catch(console.error); + }, + }); + items.push({ + id: "android_open_container", + label: "Open Container", + type: "button", + onClick: () => { + playPressSound(); + TauriService.openInstanceFolder(profile).catch(console.error); + }, + }); + } + items.push({ + id: "android_proton", + label: `Proton: ${androidRunner === "proton10" ? "Proton 10" : "Proton 11 (Default)"}`, + type: "button", + onClick: () => { + playPressSound(); + const next = androidRunner === "proton10" ? "proton11" : "proton10"; + setAndroidRunner(next); + TauriService.switchProton(next).catch(console.error); + }, + }); + items.push({ + id: "android_install_driver", + label: "Install Latest Driver", + type: "button", + onClick: () => { + playPressSound(); + TauriService.installLatestDriver().catch(console.error); + }, + }); + items.push({ + id: "android_audio", + label: `Audio: ${androidAudioBackend === "alsa" ? "ALSA" : "PulseAudio (Default)"}`, + type: "button", + onClick: () => { + playPressSound(); + const next = androidAudioBackend === "alsa" ? "pulseaudio" : "alsa"; + setAndroidAudioBackend(next); + TauriService.setAudioBackend(next).catch(console.error); + }, + }); } if (isGameRunning) { @@ -613,6 +661,8 @@ const SettingsView = memo(function SettingsView() { launchEnvVars, skipIntro, profile, + androidRunner, + androidAudioBackend, ]); useEffect(() => { diff --git a/src/context/LauncherContext.tsx b/src/context/LauncherContext.tsx index db83551..7a13be5 100644 --- a/src/context/LauncherContext.tsx +++ b/src/context/LauncherContext.tsx @@ -109,6 +109,8 @@ export function LauncherProvider({ children }: { children: React.ReactNode }) { configRaw.startFullscreen, configRaw.skipIntro, configRaw.instanceLaunchArgs, + configRaw.androidRunner, + configRaw.androidAudioBackend, ], ); diff --git a/src/hooks/useAppConfig.ts b/src/hooks/useAppConfig.ts index 6f49959..0b44df7 100644 --- a/src/hooks/useAppConfig.ts +++ b/src/hooks/useAppConfig.ts @@ -29,6 +29,8 @@ export function useAppConfig() { const [instanceLaunchArgs, setInstanceLaunchArgs] = useState< Record; args: string[] }> >({}); + const [androidRunner, setAndroidRunner] = useLocalStorage("lce-android-runner", undefined); + const [androidAudioBackend, setAndroidAudioBackend] = useLocalStorage<"alsa" | "pulseaudio">("lce-android-audio", "pulseaudio"); useEffect(() => { TauriService.loadConfig().then((config) => { if (config.username) setUsername(config.username); @@ -53,6 +55,8 @@ export function useAppConfig() { if (config.launchEnvVars) setLaunchEnvVars(config.launchEnvVars); if (config.skipIntro !== undefined) setSkipIntro(config.skipIntro); if (config.instanceLaunchArgs) setInstanceLaunchArgs(config.instanceLaunchArgs); + if (config.androidRunner) setAndroidRunner(config.androidRunner); + if (config.androidAudioBackend) setAndroidAudioBackend(config.androidAudioBackend); setIsLoaded(true); }); }, []); @@ -81,9 +85,11 @@ export function useAppConfig() { launchEnvVars, skipIntro, instanceLaunchArgs, + androidRunner, + androidAudioBackend, }).catch(console.error); } - }, [username, theme, linuxRunner, perfBoost, profile, customEditions, customPaths, customizations, animationsEnabled, vfxEnabled, rpcEnabled, startFullscreen, musicVol, sfxVol, legacyMode, mangohudEnabled, extraLaunchArgs, launchPrefix, launchEnvVars, skipIntro, isLoaded, instanceLaunchArgs]); + }, [username, theme, linuxRunner, perfBoost, profile, customEditions, customPaths, customizations, animationsEnabled, vfxEnabled, rpcEnabled, startFullscreen, musicVol, sfxVol, legacyMode, mangohudEnabled, extraLaunchArgs, launchPrefix, launchEnvVars, skipIntro, isLoaded, instanceLaunchArgs, androidRunner, androidAudioBackend]); return { username, @@ -135,5 +141,9 @@ export function useAppConfig() { setSkipIntro, instanceLaunchArgs, setInstanceLaunchArgs, + androidRunner, + setAndroidRunner, + androidAudioBackend, + setAndroidAudioBackend, }; } diff --git a/src/services/TauriService.ts b/src/services/TauriService.ts index 48aa860..34150a6 100644 --- a/src/services/TauriService.ts +++ b/src/services/TauriService.ts @@ -52,6 +52,8 @@ export interface AppConfig { string, { values: Record; args: string[] } >; + androidRunner?: string; + androidAudioBackend?: "alsa" | "pulseaudio"; } export interface ThemePalette { @@ -480,4 +482,16 @@ export class TauriService { ): Promise { return invoke("lce_to_java", { inputMsPath, javaWorldOutput }); } + + static async installLatestDriver(): Promise { + return invoke("install_latest_driver"); + } + + static async switchProton(version: string): Promise { + return invoke("switch_proton", { version }); + } + + static async setAudioBackend(backend: string): Promise { + return invoke("set_audio_backend", { backend }); + } }