diff --git a/src/PostIt/PostIt/Services/ContactService.Desktop.cs b/src/PostIt/PostIt/Services/ContactService.Desktop.cs new file mode 100644 index 00000000..82746c49 --- /dev/null +++ b/src/PostIt/PostIt/Services/ContactService.Desktop.cs @@ -0,0 +1,27 @@ +#if !ANDROID && !IOS +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace PostIt.Services; + +/// +/// Desktop stub for IContactService. +/// +/// On desktop targets (Linux, macOS, Windows) MAUI Essentials +/// Contacts.Default throws NotImplementedInReferenceAssemblyException, +/// so we short-circuit with an empty list rather than trying to +/// call into the portable facade at runtime. +/// +/// Future provider plug-ins (Google Contacts API, Exchange EWS, +/// CardDAV) can either replace this stub on a per-OS basis or +/// live behind their own IContactService implementation that the +/// DI container selects by configuration. +/// +public sealed class ContactService : IContactService +{ + public Task> GetDeviceContactsAsync(CancellationToken ct = default) + => Task.FromResult>(Array.Empty()); +} +#endif diff --git a/src/PostIt/PostIt/Services/ContactService.Mobile.cs b/src/PostIt/PostIt/Services/ContactService.Mobile.cs new file mode 100644 index 00000000..744fb9a6 --- /dev/null +++ b/src/PostIt/PostIt/Services/ContactService.Mobile.cs @@ -0,0 +1,64 @@ +#if ANDROID || IOS +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Maui.ApplicationModel.Communication; +using Microsoft.Maui.ApplicationModel; +using Microsoft.Maui.Devices; + +namespace PostIt.Services; + +/// +/// Mobile implementation backed by MAUI Essentials Contacts.Default. +/// +/// Compiled only for ANDROID and IOS. On desktop targets, see +/// ContactService.Desktop.cs (the stub that wins at compile time). +/// +/// Note: at runtime, this class throws +/// NotImplementedInReferenceAssemblyException unless the host +/// application project also references the platform-specific +/// Microsoft.Maui.Essentials implementation (typically the +/// PostIt.Android project). On iOS the same is required via +/// PostIt.iOS. On desktop the stub is used and this file is excluded. +/// +public sealed class ContactService : IContactService +{ + public async Task> GetDeviceContactsAsync(CancellationToken ct = default) + { + if (DeviceInfo.Current.Platform == DevicePlatform.Unknown) + return Array.Empty(); + + try + { + var status = await Permissions.RequestAsync(); + if (status != PermissionStatus.Granted) + return Array.Empty(); + + var contacts = await Contacts.Default.GetAllAsync(); + if (contacts is null) return Array.Empty(); + + var result = new List(); + foreach (var c in contacts) + { + var emails = new List(); + if (c.Emails is not null) + { + foreach (var e in c.Emails) + { + if (!string.IsNullOrEmpty(e.EmailAddress)) + emails.Add(e.EmailAddress); + } + } + result.Add(new ContactDto(c.Id, c.DisplayName ?? string.Empty, emails)); + } + return result; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"ContactService: {ex.Message}"); + return Array.Empty(); + } + } +} +#endif diff --git a/src/PostIt/PostIt/Services/ContactService.cs b/src/PostIt/PostIt/Services/ContactService.cs deleted file mode 100644 index bc71d11b..00000000 --- a/src/PostIt/PostIt/Services/ContactService.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Microsoft.Maui.ApplicationModel.Communication; -using Microsoft.Maui.ApplicationModel; -using Microsoft.Maui.Devices; - -public class ContactService -{ - public async Task> GetDeviceContactsAsync() - { - // 1. Ensure the platform supports MAUI Essentials APIs - if (DeviceInfo.Current.Platform == DevicePlatform.Unknown) - { - throw new PlatformNotSupportedException("MAUI Essentials is not available on this platform."); - } - - try - { - // 2. Request runtime permission (Required for Android & iOS) - var status = await Permissions.RequestAsync(); - if (status != PermissionStatus.Granted) - { - // Permission denied by user - return Array.Empty(); - } - - // 3. Fetch all contacts - var contactsEnumerable = await Contacts.Default.GetAllAsync(); - return contactsEnumerable ?? Array.Empty(); - } - catch (Exception ex) - { - // Handle cross-platform exceptions or logs here - System.Diagnostics.Debug.WriteLine($"Error fetching contacts: {ex.Message}"); - return Array.Empty(); - } - } -} diff --git a/src/PostIt/PostIt/Services/IContactService.cs b/src/PostIt/PostIt/Services/IContactService.cs new file mode 100644 index 00000000..8c6da44a --- /dev/null +++ b/src/PostIt/PostIt/Services/IContactService.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace PostIt.Services; + +/// +/// Abstraction over device contact providers (MAUI Essentials on mobile, +/// future Google/Exchange/IMAP providers). +/// +/// Implementations live next to this file in platform-conditional +/// source files: ContactService.Mobile.cs (ANDROID/IOS) and +/// ContactService.Desktop.cs (everything else). +/// +public interface IContactService +{ + Task> GetDeviceContactsAsync(CancellationToken ct = default); +} + +/// +/// Platform-neutral contact DTO. Source-of-truth shape for the UI layer; +/// concrete providers (MAUI Essentials today, Google Contacts API later) +/// map to this type. +/// +public sealed record ContactDto( + string Id, + string DisplayName, + IReadOnlyList Emails);