feat: android settings

This commit is contained in:
neoapps-dev 2026-08-17 14:29:41 +03:00
parent 29bcfd8121
commit d5aaad83a8
10 changed files with 165 additions and 16 deletions

@ -1 +1 @@
Subproject commit bfe5ee43aa406049c7645dbe8891a445621bc02f
Subproject commit a7631bab90e8c2fe4322ffb96a50e38f15ef4f0e

View file

@ -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| {

View file

@ -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())
}

View file

@ -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,
}
}

View file

@ -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();

View file

@ -67,6 +67,8 @@ pub struct AppConfig {
pub launch_prefix: Option<String>,
pub launch_env_vars: Option<std::collections::HashMap<String, String>>,
pub instance_launch_args: Option<std::collections::HashMap<String, InstanceLaunchArgs>>,
pub android_runner: Option<String>,
pub android_audio_backend: Option<String>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]

View file

@ -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<number | null>(null);
const [currentSubMenu, setCurrentSubMenu] = useState<
"main" | "audio" | "video" | "launcher" | "game" | "plugins"
"main" | "audio" | "video" | "launcher" | "game" | "plugins" | "android"
>("main");
const [runners, setRunners] = useState<Runner[]>([]);
const [pluginsInfo, setPluginsInfo] = useState<PluginInfo[]>([]);
@ -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(() => {

View file

@ -109,6 +109,8 @@ export function LauncherProvider({ children }: { children: React.ReactNode }) {
configRaw.startFullscreen,
configRaw.skipIntro,
configRaw.instanceLaunchArgs,
configRaw.androidRunner,
configRaw.androidAudioBackend,
],
);

View file

@ -29,6 +29,8 @@ export function useAppConfig() {
const [instanceLaunchArgs, setInstanceLaunchArgs] = useState<
Record<string, { values: Record<string, unknown>; args: string[] }>
>({});
const [androidRunner, setAndroidRunner] = useLocalStorage<string | undefined>("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,
};
}

View file

@ -52,6 +52,8 @@ export interface AppConfig {
string,
{ values: Record<string, unknown>; args: string[] }
>;
androidRunner?: string;
androidAudioBackend?: "alsa" | "pulseaudio";
}
export interface ThemePalette {
@ -480,4 +482,16 @@ export class TauriService {
): Promise<string> {
return invoke("lce_to_java", { inputMsPath, javaWorldOutput });
}
static async installLatestDriver(): Promise<void> {
return invoke("install_latest_driver");
}
static async switchProton(version: string): Promise<void> {
return invoke("switch_proton", { version });
}
static async setAudioBackend(backend: string): Promise<void> {
return invoke("set_audio_backend", { backend });
}
}