mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-21 08:04:26 +08:00
refactor(launcher): replace LauncherFlowCoordinator with LaunchPipeline and slim App shell
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Threading;
|
||||
using LanMountainDesktop.Launcher.Models;
|
||||
using LanMountainDesktop.Launcher.Views;
|
||||
|
||||
namespace LanMountainDesktop.Launcher.Shell.EntryHandlers;
|
||||
|
||||
internal static class LaunchEntryHandler
|
||||
{
|
||||
public static SplashWindow CreateSplashWindow()
|
||||
{
|
||||
var window = new SplashWindow();
|
||||
try
|
||||
{
|
||||
var appRoot = Commands.ResolveAppRoot(LauncherRuntimeContext.Current);
|
||||
var versionInfo = new DeploymentLocator(appRoot).GetVersionInfo();
|
||||
window.SetVersionInfo(versionInfo.Version, versionInfo.Codename);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Warn($"Failed to set splash version info: {ex.Message}");
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
public static Task RunAsync(
|
||||
IClassicDesktopStyleApplicationLifetime desktop,
|
||||
CommandContext context,
|
||||
SplashWindow splashWindow) =>
|
||||
LauncherCompositionRoot.RunOrchestratorWithSplashAsync(desktop, context, splashWindow);
|
||||
}
|
||||
|
||||
internal static class ApplyUpdateEntryHandler
|
||||
{
|
||||
public static Task RunAsync(
|
||||
IClassicDesktopStyleApplicationLifetime desktop,
|
||||
CommandContext context,
|
||||
UpdateWindow window) =>
|
||||
LauncherCompositionRoot.RunApplyUpdateWithWindowAsync(desktop, context, window);
|
||||
}
|
||||
|
||||
internal static class AirAppBrokerEntryHandler
|
||||
{
|
||||
public static async Task RunAsync(IClassicDesktopStyleApplicationLifetime desktop, CommandContext context)
|
||||
{
|
||||
var appRoot = Commands.ResolveAppRoot(context);
|
||||
var requesterPid = context.GetIntOption("requester-pid", 0);
|
||||
var dataLocationResolver = new DataLocationResolver(appRoot);
|
||||
Logger.Info($"Air APP broker starting. AppRoot='{appRoot}'; RequesterPid={requesterPid}.");
|
||||
|
||||
using var airAppIpcHost = new LauncherAirAppLifecycleIpcHost(
|
||||
new LauncherAirAppLifecycleService(
|
||||
new AirAppProcessStarter(
|
||||
new AirAppHostLocator(),
|
||||
() => appRoot,
|
||||
() => null,
|
||||
() => dataLocationResolver.ResolveDataRoot())));
|
||||
airAppIpcHost.Start();
|
||||
|
||||
while (ShouldKeepAlive(requesterPid, airAppIpcHost.LifecycleService))
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
Logger.Info("Air APP broker exiting.");
|
||||
await Dispatcher.UIThread.InvokeAsync(() => desktop.Shutdown(0), DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
internal static bool ShouldKeepAirAppBrokerAlive(int requesterPid, LauncherAirAppLifecycleService lifecycleService)
|
||||
{
|
||||
if (requesterPid <= 0)
|
||||
{
|
||||
return lifecycleService.HasLiveAirApps();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var process = System.Diagnostics.Process.GetProcessById(requesterPid);
|
||||
return !process.HasExited || lifecycleService.HasLiveAirApps();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return lifecycleService.HasLiveAirApps();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ShouldKeepAlive(int requesterPid, LauncherAirAppLifecycleService lifecycleService) =>
|
||||
ShouldKeepAirAppBrokerAlive(requesterPid, lifecycleService);
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Threading;
|
||||
using LanMountainDesktop.Launcher.Models;
|
||||
using LanMountainDesktop.Launcher.Resources;
|
||||
using LanMountainDesktop.Launcher.Views;
|
||||
|
||||
namespace LanMountainDesktop.Launcher.Shell.EntryHandlers;
|
||||
|
||||
internal static class PreviewEntryHandler
|
||||
{
|
||||
public static bool TryHandle(CommandContext context, IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
switch (context.Command.ToLowerInvariant())
|
||||
{
|
||||
case "preview-splash":
|
||||
RunSplashPreview(desktop);
|
||||
return true;
|
||||
case "preview-error":
|
||||
RunErrorPreview(desktop);
|
||||
return true;
|
||||
case "preview-multi-instance":
|
||||
RunMultiInstancePreview(desktop);
|
||||
return true;
|
||||
case "preview-update":
|
||||
RunUpdatePreview(desktop);
|
||||
return true;
|
||||
case "preview-oobe":
|
||||
RunOobePreview(desktop);
|
||||
return true;
|
||||
case "preview-debug":
|
||||
new DevDebugWindow().Show();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void RunSplashPreview(IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
var splashWindow = LaunchEntryHandler.CreateSplashWindow();
|
||||
splashWindow.SetDebugMode(true);
|
||||
splashWindow.Show();
|
||||
_ = SimulateSplashPreviewAsync(desktop, splashWindow);
|
||||
}
|
||||
|
||||
private static void RunErrorPreview(IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
var errorWindow = new ErrorWindow();
|
||||
errorWindow.SetErrorMessage(Strings.Preview_ErrorMessage);
|
||||
errorWindow.Show();
|
||||
_ = WaitForWindowCloseAsync(desktop, errorWindow);
|
||||
}
|
||||
|
||||
private static void RunMultiInstancePreview(IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
var promptWindow = new MultiInstancePromptWindow();
|
||||
promptWindow.SetDetails(Environment.ProcessId, "ForegroundDesktop");
|
||||
promptWindow.Show();
|
||||
_ = WaitForWindowCloseAsync(desktop, promptWindow);
|
||||
}
|
||||
|
||||
private static void RunUpdatePreview(IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
var updateWindow = new UpdateWindow();
|
||||
updateWindow.SetDebugMode(true);
|
||||
updateWindow.Show();
|
||||
_ = SimulateUpdatePreviewAsync(desktop, updateWindow);
|
||||
}
|
||||
|
||||
private static void RunOobePreview(IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
var oobeWindow = new OobeWindow();
|
||||
oobeWindow.Show();
|
||||
_ = SimulateOobePreviewAsync(desktop, oobeWindow);
|
||||
}
|
||||
|
||||
private static async Task SimulateSplashPreviewAsync(IClassicDesktopStyleApplicationLifetime desktop, SplashWindow window)
|
||||
{
|
||||
var stages = new[] { "initializing", "update", "plugins", "launch", "ready" };
|
||||
var messages = new[]
|
||||
{
|
||||
Strings.Preview_SplashInitializing,
|
||||
Strings.Preview_SplashCheckingUpdates,
|
||||
Strings.Preview_SplashCheckingPlugins,
|
||||
Strings.Preview_SplashLaunchingHost,
|
||||
Strings.Preview_SplashReady
|
||||
};
|
||||
var reporter = (ISplashStageReporter)window;
|
||||
|
||||
for (var i = 0; i < stages.Length; i++)
|
||||
{
|
||||
reporter.Report(stages[i], messages[i]);
|
||||
await Task.Delay(800).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await Task.Delay(5000).ConfigureAwait(false);
|
||||
await Dispatcher.UIThread.InvokeAsync(() => desktop.Shutdown(0));
|
||||
}
|
||||
|
||||
private static async Task SimulateUpdatePreviewAsync(IClassicDesktopStyleApplicationLifetime desktop, UpdateWindow window)
|
||||
{
|
||||
var stages = new[] { "verify", "extract", "apply", "plugins", "cleanup" };
|
||||
for (var i = 0; i < stages.Length; i++)
|
||||
{
|
||||
window.Report(stages[i], string.Format(Strings.Preview_UpdateProcessing, stages[i]), (i + 1) * 20);
|
||||
await Task.Delay(600).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
window.ReportComplete(true, null);
|
||||
await Task.Delay(3000).ConfigureAwait(false);
|
||||
await Dispatcher.UIThread.InvokeAsync(() => desktop.Shutdown(0));
|
||||
}
|
||||
|
||||
private static async Task SimulateOobePreviewAsync(IClassicDesktopStyleApplicationLifetime desktop, OobeWindow window)
|
||||
{
|
||||
try
|
||||
{
|
||||
await window.WaitForEnterAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error("OOBE preview failed.", ex);
|
||||
}
|
||||
|
||||
await Dispatcher.UIThread.InvokeAsync(() => desktop.Shutdown(0));
|
||||
}
|
||||
|
||||
private static async Task WaitForWindowCloseAsync(IClassicDesktopStyleApplicationLifetime desktop, Window window)
|
||||
{
|
||||
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
window.Closed += (_, _) => tcs.TrySetResult();
|
||||
await tcs.Task.ConfigureAwait(false);
|
||||
await Dispatcher.UIThread.InvokeAsync(() => desktop.Shutdown(0));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user