using System.Text.Json; using System.Text.Json.Nodes; using LanMountainDesktop.Shared.Contracts.Launcher; namespace LanMountainDesktop.Launcher.Services; /// /// 在 OOBE 中向 Host 的 settings.json 写入启动与展示相关字段,属性名与 Host /// AppSettingsSnapshot 的 JSON 序列化一致(PascalCase)。 /// public static class HostAppSettingsOobeMerger { public const string ShowInTaskbarKey = "ShowInTaskbar"; public const string EnableFadeTransitionKey = "EnableFadeTransition"; public const string EnableSlideTransitionKey = "EnableSlideTransition"; public const string EnableFusedDesktopKey = "EnableFusedDesktop"; public const string EnableThreeFingerSwipeKey = "EnableThreeFingerSwipe"; public const string AutoStartWithWindowsKey = "AutoStartWithWindows"; public const string MultiInstanceLaunchBehaviorKey = "MultiInstanceLaunchBehavior"; public static string GetSettingsFilePath(string dataRoot) => Path.Combine(Path.GetFullPath(dataRoot.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)), "settings.json"); public static HostAppSettingsStartupDefaults LoadStartupDefaults(string settingsPath) { if (!File.Exists(settingsPath)) { return HostAppSettingsStartupDefaults.Fallback; } try { var root = JsonNode.Parse(File.ReadAllText(settingsPath))?.AsObject(); if (root is null) { return HostAppSettingsStartupDefaults.Fallback; } var fade = ReadBool(root, EnableFadeTransitionKey, defaultValue: true); var slide = ReadBool(root, EnableSlideTransitionKey, defaultValue: false); var normalized = StartupVisualPreferencesResolver.FromFlags(fade, slide); return new HostAppSettingsStartupDefaults( ShowInTaskbar: ReadBool(root, ShowInTaskbarKey, defaultValue: false), EnableFadeTransition: normalized.EnableFadeTransition, EnableSlideTransition: normalized.EnableSlideTransition, FusedPopupExperience: ReadBool(root, EnableFusedDesktopKey, defaultValue: false) && ReadBool(root, EnableThreeFingerSwipeKey, defaultValue: false), AutoStartWithWindows: ReadBool(root, AutoStartWithWindowsKey, defaultValue: false)); } catch (Exception ex) { Logger.Warn($"HostAppSettingsOobeMerger: failed to read '{settingsPath}'. {ex.Message}"); return HostAppSettingsStartupDefaults.Fallback; } } public static MultiInstanceLaunchBehavior LoadMultiInstanceLaunchBehavior(string settingsPath) { if (!File.Exists(settingsPath)) { return MultiInstanceLaunchBehavior.NotifyAndOpenDesktop; } try { var root = JsonNode.Parse(File.ReadAllText(settingsPath))?.AsObject(); if (root is null) { return MultiInstanceLaunchBehavior.NotifyAndOpenDesktop; } return ReadMultiInstanceLaunchBehavior(root); } catch (Exception ex) { Logger.Warn($"HostAppSettingsOobeMerger: failed to read multi-instance behavior from '{settingsPath}'. {ex.Message}"); return MultiInstanceLaunchBehavior.NotifyAndOpenDesktop; } } public static void MergeStartupPresentation(string settingsPath, HostAppSettingsStartupChoices choices) { var directory = Path.GetDirectoryName(settingsPath); if (!string.IsNullOrWhiteSpace(directory)) { Directory.CreateDirectory(directory); } JsonObject root; if (File.Exists(settingsPath)) { try { root = JsonNode.Parse(File.ReadAllText(settingsPath))?.AsObject() ?? new JsonObject(); } catch (Exception ex) { Logger.Warn($"HostAppSettingsOobeMerger: replacing invalid JSON at '{settingsPath}'. {ex.Message}"); root = new JsonObject(); } } else { root = new JsonObject(); } var normalized = StartupVisualPreferencesResolver.FromFlags( choices.EnableFadeTransition, choices.EnableSlideTransition); root[ShowInTaskbarKey] = choices.ShowInTaskbar; root[EnableFadeTransitionKey] = normalized.EnableFadeTransition; root[EnableSlideTransitionKey] = normalized.EnableSlideTransition; root[EnableFusedDesktopKey] = choices.FusedPopupExperience; root[EnableThreeFingerSwipeKey] = choices.FusedPopupExperience; root[AutoStartWithWindowsKey] = choices.AutoStartWithWindows; var options = new JsonSerializerOptions { WriteIndented = true }; File.WriteAllText(settingsPath, root.ToJsonString(options)); } private static bool ReadBool(JsonObject root, string key, bool defaultValue) { if (!root.TryGetPropertyValue(key, out var node) || node is null) { return defaultValue; } return node switch { JsonValue v when v.TryGetValue(out var b) => b, JsonValue v when v.TryGetValue(out var s) => bool.TryParse(s, out var p) && p, _ => defaultValue }; } private static MultiInstanceLaunchBehavior ReadMultiInstanceLaunchBehavior(JsonObject root) { if (!root.TryGetPropertyValue(MultiInstanceLaunchBehaviorKey, out var node) || node is null) { return MultiInstanceLaunchBehavior.NotifyAndOpenDesktop; } if (node is JsonValue value) { if (value.TryGetValue(out var text) && Enum.TryParse(text, ignoreCase: true, out var parsed)) { return parsed; } if (value.TryGetValue(out var numeric) && Enum.IsDefined(typeof(MultiInstanceLaunchBehavior), numeric)) { return (MultiInstanceLaunchBehavior)numeric; } } return MultiInstanceLaunchBehavior.NotifyAndOpenDesktop; } } public readonly record struct HostAppSettingsStartupDefaults( bool ShowInTaskbar, bool EnableFadeTransition, bool EnableSlideTransition, bool FusedPopupExperience, bool AutoStartWithWindows) { public static HostAppSettingsStartupDefaults Fallback { get; } = new( ShowInTaskbar: false, EnableFadeTransition: true, EnableSlideTransition: false, FusedPopupExperience: false, AutoStartWithWindows: false); } public readonly record struct HostAppSettingsStartupChoices( bool ShowInTaskbar, bool EnableFadeTransition, bool EnableSlideTransition, bool FusedPopupExperience, bool AutoStartWithWindows);