diff --git a/AutoPilot.App.csproj b/AutoPilot.App.csproj index 48f9af7be8..ded97d09b9 100644 --- a/AutoPilot.App.csproj +++ b/AutoPilot.App.csproj @@ -69,8 +69,8 @@ - - + + diff --git a/Components/Layout/SessionSidebar.razor b/Components/Layout/SessionSidebar.razor index 28a02f8e41..6f596fcb4d 100644 --- a/Components/Layout/SessionSidebar.razor +++ b/Components/Layout/SessionSidebar.razor @@ -384,6 +384,7 @@ else private async Task BrowseDirectory() { +#if MACCATALYST try { var dir = await FolderPickerService.PickFolderAsync(); @@ -394,6 +395,7 @@ else { Console.WriteLine($"Folder picker error: {ex.Message}"); } +#endif } private async Task ResumeSession(PersistedSessionInfo persisted) diff --git a/MainPage.xaml b/MainPage.xaml index 3f2a335e26..8b8be5f9c4 100644 --- a/MainPage.xaml +++ b/MainPage.xaml @@ -1,7 +1,8 @@ + x:Class="AutoPilot.App.MainPage" + SafeAreaEdges="Container"> diff --git a/Models/ConnectionSettings.cs b/Models/ConnectionSettings.cs index fe507ee693..02713bfcce 100644 --- a/Models/ConnectionSettings.cs +++ b/Models/ConnectionSettings.cs @@ -44,11 +44,26 @@ public static ConnectionSettings Load() if (File.Exists(SettingsPath)) { var json = File.ReadAllText(SettingsPath); - return JsonSerializer.Deserialize(json) ?? new(); + return JsonSerializer.Deserialize(json) ?? DefaultSettings(); } } catch { } + return DefaultSettings(); + } + + private static ConnectionSettings DefaultSettings() + { +#if ANDROID + // Android can't run Copilot locally — default to persistent mode with common LAN IP + return new ConnectionSettings + { + Mode = ConnectionMode.Persistent, + Host = "192.168.50.72", // Mac host on local network + Port = 4321 + }; +#else return new(); +#endif } public void Save() diff --git a/Platforms/Android/AndroidManifest.xml b/Platforms/Android/AndroidManifest.xml index 24166ca757..d707cbf142 100644 --- a/Platforms/Android/AndroidManifest.xml +++ b/Platforms/Android/AndroidManifest.xml @@ -1,6 +1,6 @@  - + diff --git a/Platforms/Android/MainActivity.cs b/Platforms/Android/MainActivity.cs index f3c8d1d547..da5fd67fe6 100644 --- a/Platforms/Android/MainActivity.cs +++ b/Platforms/Android/MainActivity.cs @@ -13,7 +13,7 @@ protected override void OnCreate(Bundle? savedInstanceState) { base.OnCreate(savedInstanceState); - // Draw behind system bars (edge-to-edge) + // Edge-to-edge with transparent system bars if (Window != null) { WindowCompat.SetDecorFitsSystemWindows(Window, false); diff --git a/Services/CopilotService.cs b/Services/CopilotService.cs index 60e3c71ab7..e6a8cce711 100644 --- a/Services/CopilotService.cs +++ b/Services/CopilotService.cs @@ -16,24 +16,46 @@ public class CopilotService : IAsyncDisposable private string? _activeSessionName; private SynchronizationContext? _syncContext; - private static readonly string AppDataDir = GetAppDataDir(); + private static string? _copilotBaseDir; + private static string CopilotBaseDir => _copilotBaseDir ??= GetCopilotBaseDir(); - private static string GetAppDataDir() + private static string GetCopilotBaseDir() { - // On mobile, UserProfile may be empty — use LocalApplicationData instead - var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - if (string.IsNullOrEmpty(home)) - home = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - return Path.Combine(home, ".copilot"); + try + { +#if ANDROID + var home = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + if (string.IsNullOrEmpty(home)) + home = Environment.GetFolderPath(Environment.SpecialFolder.Personal); + if (string.IsNullOrEmpty(home)) + home = Android.App.Application.Context.FilesDir?.AbsolutePath ?? Path.GetTempPath(); + return Path.Combine(home, ".copilot"); +#else + var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + if (string.IsNullOrEmpty(home)) + home = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + if (string.IsNullOrEmpty(home)) + home = Environment.GetFolderPath(Environment.SpecialFolder.Personal); + return Path.Combine(home, ".copilot"); +#endif + } + catch + { + return Path.Combine(Path.GetTempPath(), ".copilot"); + } } - private static readonly string SessionStatePath = Path.Combine(AppDataDir, "session-state"); + private static string? _sessionStatePath; + private static string SessionStatePath => _sessionStatePath ??= Path.Combine(CopilotBaseDir, "session-state"); - private static readonly string ActiveSessionsFile = Path.Combine(AppDataDir, "autopilot-active-sessions.json"); + private static string? _activeSessionsFile; + private static string ActiveSessionsFile => _activeSessionsFile ??= Path.Combine(CopilotBaseDir, "autopilot-active-sessions.json"); - private static readonly string UiStateFile = Path.Combine(AppDataDir, "autopilot-ui-state.json"); + private static string? _uiStateFile; + private static string UiStateFile => _uiStateFile ??= Path.Combine(CopilotBaseDir, "autopilot-ui-state.json"); - private static readonly string ProjectDir = FindProjectDir(); + private static string? _projectDir; + private static string ProjectDir => _projectDir ??= FindProjectDir(); private static string FindProjectDir() { @@ -41,9 +63,9 @@ private static string FindProjectDir() { // Walk up from the base directory to find the .csproj (works from bin/Debug/... at runtime) var dir = AppDomain.CurrentDomain.BaseDirectory; + if (string.IsNullOrEmpty(dir)) return CopilotBaseDir; for (int i = 0; i < 10; i++) { - if (string.IsNullOrEmpty(dir)) break; if (Directory.Exists(dir) && Directory.GetFiles(dir, "*.csproj").Length > 0) return dir; var parent = Directory.GetParent(dir); @@ -53,10 +75,7 @@ private static string FindProjectDir() } catch { } // Fallback - var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - return string.IsNullOrEmpty(home) - ? Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) - : home; + return CopilotBaseDir; } public string DefaultModel { get; set; } = "claude-opus-4.6"; @@ -147,6 +166,16 @@ public async Task InitializeAsync(CancellationToken cancellationToken = default) return; } +#if ANDROID + // Android can't run Copilot CLI locally — must connect to remote server + settings.Mode = ConnectionMode.Persistent; + CurrentMode = ConnectionMode.Persistent; + if (settings.Host == "localhost" || settings.Host == "127.0.0.1") + { + Debug("Android detected with localhost — update Host in settings to your Mac's IP"); + } + Debug($"Android: connecting to remote server at {settings.CliUrl}"); +#endif // In Persistent mode, auto-start the server if not already running if (settings.Mode == ConnectionMode.Persistent) { diff --git a/Services/ServerManager.cs b/Services/ServerManager.cs index 550b9a0271..126bc8aabc 100644 --- a/Services/ServerManager.cs +++ b/Services/ServerManager.cs @@ -14,6 +14,8 @@ private static string GetCopilotDir() var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); if (string.IsNullOrEmpty(home)) home = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + if (string.IsNullOrEmpty(home)) + home = Path.GetTempPath(); return Path.Combine(home, ".copilot"); }