diff --git a/src/DeviceDiscoveryUI/DDDevicePickerViewController.cs b/src/DeviceDiscoveryUI/DDDevicePickerViewController.cs new file mode 100644 index 0000000000..0da87f90e1 --- /dev/null +++ b/src/DeviceDiscoveryUI/DDDevicePickerViewController.cs @@ -0,0 +1,62 @@ +// +// Custom methods for DDDevicePickerViewController +// +// Authors: +// Israel Soto +// +// Copyright 2022 Microsoft Corporation. +// + +#nullable enable + +using ObjCRuntime; +using Foundation; +using Network; +using System; + +using OS_nw_endpoint = System.IntPtr; +using OS_nw_error = System.IntPtr; + +namespace DeviceDiscoveryUI { + + public partial class DDDevicePickerViewController { + /// + /// The type of the delegate that will be called when the user selects a device in the list of devices. + /// + public delegate void DevicePickerCompletionHandler (NWEndpoint? endpoint, NWError? error); + + /// + /// Checks if device discovery is supported for the current device + /// + public static bool IsSupported (NWBrowserDescriptor browseDescriptor, NWParameters? parameters) => + _IsSupported (browseDescriptor.GetNonNullHandle (nameof (browseDescriptor)), parameters.GetHandle ()); + + /// + /// A view controller that lists other devices on your network. + /// + /// + /// Verify if this controller is supported by calling DDDevicePickerViewController.IsSupported before creating an instance, as this will crash if not supported. + /// + [DesignatedInitializer] + public DDDevicePickerViewController (NWBrowserDescriptor browseDescriptor, NWParameters? parameters) : base (NSObjectFlag.Empty) => + InitializeHandle ( + _InitWithBrowseDescriptorAndParameters (browseDescriptor.GetNonNullHandle (nameof (browseDescriptor)), parameters.GetHandle ()), + "initWithBrowseDescriptor:parameters:"); + + /// + /// Set the delegate that will be called when the user selects a device in list of devices. + /// + public void SetDevicePicker (DevicePickerCompletionHandler devicePickerCompletionHandler) + { + _SetDevicePicker (InternalDevicePickerCompletionHandler); + + void InternalDevicePickerCompletionHandler (OS_nw_endpoint endpoint, OS_nw_error error) + { + NWEndpoint? ep = endpoint != IntPtr.Zero ? new NWEndpoint (endpoint, false) : null; + NWError? er = error != IntPtr.Zero ? new NWError (error, false) : null; + + devicePickerCompletionHandler (ep, er); + } + } + } +} diff --git a/src/build/generator-frameworks.g.cs b/src/build/generator-frameworks.g.cs index d73b6d5ec6..a7366d9a99 100644 --- a/src/build/generator-frameworks.g.cs +++ b/src/build/generator-frameworks.g.cs @@ -366,6 +366,7 @@ partial class Frameworks { "CoreText", "CoreVideo", "DeviceCheck", + "DeviceDiscoveryUI", "ExternalAccessory", "Foundation", "GameController", @@ -603,6 +604,7 @@ partial class Frameworks { bool? _CoreVideo; bool? _CoreWlan; bool? _DeviceCheck; + bool? _DeviceDiscoveryUI; bool? _EventKit; bool? _EventKitUI; bool? _ExecutionPolicy; @@ -768,6 +770,7 @@ partial class Frameworks { public bool HaveCoreVideo { get { if (!_CoreVideo.HasValue) _CoreVideo = GetValue ("CoreVideo"); return _CoreVideo.Value; } } public bool HaveCoreWlan { get { if (!_CoreWlan.HasValue) _CoreWlan = GetValue ("CoreWlan"); return _CoreWlan.Value; } } public bool HaveDeviceCheck { get { if (!_DeviceCheck.HasValue) _DeviceCheck = GetValue ("DeviceCheck"); return _DeviceCheck.Value; } } + public bool HaveDeviceDiscoveryUI { get { if (!_DeviceDiscoveryUI.HasValue) _DeviceDiscoveryUI = GetValue ("DeviceDiscoveryUI"); return _DeviceDiscoveryUI.Value; } } public bool HaveEventKit { get { if (!_EventKit.HasValue) _EventKit = GetValue ("EventKit"); return _EventKit.Value; } } public bool HaveEventKitUI { get { if (!_EventKitUI.HasValue) _EventKitUI = GetValue ("EventKitUI"); return _EventKitUI.Value; } } public bool HaveExecutionPolicy { get { if (!_ExecutionPolicy.HasValue) _ExecutionPolicy = GetValue ("ExecutionPolicy"); return _ExecutionPolicy.Value; } } diff --git a/src/devicediscoveryui.cs b/src/devicediscoveryui.cs new file mode 100644 index 0000000000..c5726126d2 --- /dev/null +++ b/src/devicediscoveryui.cs @@ -0,0 +1,46 @@ +// +// DeviceDiscoveryUI C# bindings +// +// Authors: +// Israel Soto +// +// Copyright 2022 Microsoft Corporation. +// + +using System; +using Foundation; +using ObjCRuntime; +using UIKit; + +using OS_nw_browse_descriptor = System.IntPtr; +using OS_nw_parameters = System.IntPtr; +using OS_nw_endpoint = System.IntPtr; +using OS_nw_error = System.IntPtr; + +#if !NET +using NativeHandle = System.IntPtr; +#endif + +namespace DeviceDiscoveryUI { + + [Internal] + delegate void _DevicePickerCompletionHandler ([NullAllowed] OS_nw_endpoint endpoint, [NullAllowed] OS_nw_error error); + + [NoMac, NoiOS, NoMacCatalyst, NoWatch, TV (16, 0)] + [BaseType (typeof (UIViewController))] + [DisableDefaultCtor] + interface DDDevicePickerViewController { + [Internal] + [Static] + [Export ("isSupportedForBrowseDescriptor:parameters:")] + bool _IsSupported (OS_nw_browse_descriptor browseDescriptor, [NullAllowed] OS_nw_parameters parameters); + + [Internal] + [Export ("initWithBrowseDescriptor:parameters:")] + NativeHandle _InitWithBrowseDescriptorAndParameters (OS_nw_browse_descriptor browseDescriptor, [NullAllowed] OS_nw_parameters parameters); + + [Internal] + [Export ("setDevicePickerCompletionHandler:")] + void _SetDevicePicker (_DevicePickerCompletionHandler devicePickerCompletionHandler); + } +} diff --git a/src/frameworks.sources b/src/frameworks.sources index 311e78a931..bc34c43987 100644 --- a/src/frameworks.sources +++ b/src/frameworks.sources @@ -726,6 +726,11 @@ COREWLAN_SOURCES = \ CoreWlan/CWInterface.cs \ CoreWlan/CWKeychain.cs \ +# DeviceDiscoveryUI + +DEVICEDISCOVERYUI_SOURCES = \ + DeviceDiscoveryUI/DDDevicePickerViewController.cs \ + # EventKit EVENTKIT_API_SOURCES = \ @@ -2318,6 +2323,7 @@ TVOS_FRAMEWORKS = \ CoreLocation \ CoreSpotlight \ CoreText \ + DeviceDiscoveryUI \ ExternalAccessory \ GLKit \ GameController \ diff --git a/src/rsp/dotnet/tvos-defines-dotnet.rsp b/src/rsp/dotnet/tvos-defines-dotnet.rsp index cfaf147b1c..da10bbee30 100644 --- a/src/rsp/dotnet/tvos-defines-dotnet.rsp +++ b/src/rsp/dotnet/tvos-defines-dotnet.rsp @@ -27,6 +27,7 @@ -d:HAS_CORETEXT -d:HAS_COREVIDEO -d:HAS_DEVICECHECK +-d:HAS_DEVICEDISCOVERYUI -d:HAS_EXTERNALACCESSORY -d:HAS_FOUNDATION -d:HAS_GAMECONTROLLER diff --git a/src/rsp/tvos-defines.rsp b/src/rsp/tvos-defines.rsp index f957db5286..e4791dbbb6 100644 --- a/src/rsp/tvos-defines.rsp +++ b/src/rsp/tvos-defines.rsp @@ -28,6 +28,7 @@ -d:HAS_CORETEXT -d:HAS_COREVIDEO -d:HAS_DEVICECHECK +-d:HAS_DEVICEDISCOVERYUI -d:HAS_EXTERNALACCESSORY -d:HAS_FOUNDATION -d:HAS_GAMECONTROLLER diff --git a/tests/cecil-tests/Documentation.KnownFailures.txt b/tests/cecil-tests/Documentation.KnownFailures.txt index 117d842d99..b4c65f7cb8 100644 --- a/tests/cecil-tests/Documentation.KnownFailures.txt +++ b/tests/cecil-tests/Documentation.KnownFailures.txt @@ -17555,6 +17555,7 @@ F:ObjCRuntime.Constants.CoreVideoLibrary F:ObjCRuntime.Constants.CoreWlanLibrary F:ObjCRuntime.Constants.CryptoTokenKitLibrary F:ObjCRuntime.Constants.DeviceCheckLibrary +F:ObjCRuntime.Constants.DeviceDiscoveryUILibrary F:ObjCRuntime.Constants.EventKitLibrary F:ObjCRuntime.Constants.EventKitUILibrary F:ObjCRuntime.Constants.ExecutionPolicyLibrary @@ -60018,6 +60019,14 @@ M:DeviceCheck.DCDeviceGenerateTokenCompletionHandler.BeginInvoke(Foundation.NSDa M:DeviceCheck.DCDeviceGenerateTokenCompletionHandler.EndInvoke(System.IAsyncResult) M:DeviceCheck.DCDeviceGenerateTokenCompletionHandler.Invoke(Foundation.NSData,Foundation.NSError) M:DeviceCheck.DCErrorExtensions.GetDomain(DeviceCheck.DCError) +M:DeviceDiscoveryUI.DDDevicePickerViewController.#ctor(Foundation.NSCoder) +M:DeviceDiscoveryUI.DDDevicePickerViewController.#ctor(Foundation.NSObjectFlag) +M:DeviceDiscoveryUI.DDDevicePickerViewController.#ctor(ObjCRuntime.NativeHandle) +M:DeviceDiscoveryUI.DDDevicePickerViewController.DevicePickerCompletionHandler.#ctor(System.Object,System.IntPtr) +M:DeviceDiscoveryUI.DDDevicePickerViewController.DevicePickerCompletionHandler.BeginInvoke(Network.NWEndpoint,Network.NWError,System.AsyncCallback,System.Object) +M:DeviceDiscoveryUI.DDDevicePickerViewController.DevicePickerCompletionHandler.EndInvoke(System.IAsyncResult) +M:DeviceDiscoveryUI.DDDevicePickerViewController.DevicePickerCompletionHandler.Invoke(Network.NWEndpoint,Network.NWError) +M:DeviceDiscoveryUI.DDDevicePickerViewController.get_ClassHandle M:EventKit.EKAlarm.#ctor(Foundation.NSObjectFlag) M:EventKit.EKAlarm.#ctor(ObjCRuntime.NativeHandle) M:EventKit.EKAlarm.get_ClassHandle @@ -127615,6 +127624,7 @@ P:DeviceCheck.DCAppAttestService.Supported P:DeviceCheck.DCDevice.ClassHandle P:DeviceCheck.DCDevice.CurrentDevice P:DeviceCheck.DCDevice.Supported +P:DeviceDiscoveryUI.DDDevicePickerViewController.ClassHandle P:EventKit.EKAlarm.AbsoluteDate P:EventKit.EKAlarm.ClassHandle P:EventKit.EKAlarm.EmailAddress diff --git a/tests/introspection/ApiSelectorTest.cs b/tests/introspection/ApiSelectorTest.cs index 51b7438f40..723cdd07cc 100644 --- a/tests/introspection/ApiSelectorTest.cs +++ b/tests/introspection/ApiSelectorTest.cs @@ -1242,6 +1242,8 @@ namespace Introspection { // CloudKit case "initWithExcludedZoneIDs:": case "initWithZoneIDs:": + // DDDevicePickerViewController + case "initWithBrowseDescriptor:parameters:": return true; default: return false; diff --git a/tests/monotouch-test/DeviceDiscoveryUI/DDDevicePickerViewControllerTest.cs b/tests/monotouch-test/DeviceDiscoveryUI/DDDevicePickerViewControllerTest.cs new file mode 100644 index 0000000000..a6c7077f9d --- /dev/null +++ b/tests/monotouch-test/DeviceDiscoveryUI/DDDevicePickerViewControllerTest.cs @@ -0,0 +1,86 @@ +// +// Unit tests for DDDevicePickerViewController +// +// Authors: +// Israel Soto +// +// Copyright 2022 Microsoft Corporation. +// + +#if __TVOS__ + +using System; +using DeviceDiscoveryUI; +using Foundation; +using ObjCRuntime; +using Network; +using NUnit.Framework; +using Xamarin.Utils; + +namespace MonoTouchFixtures.DeviceDiscoveryUI { + + [TestFixture] + [Preserve (AllMembers = true)] + public class DDDevicePickerViewControllerTest { + + const string serviceName = "MyAppService"; + + [OneTimeSetUp] + public void Init () => TestRuntime.AssertXcodeVersion (14, 0); + + [Test] + public void IsSupportedTest () + { + var browserDescriptor = NWBrowserDescriptor.CreateApplicationServiceName (serviceName); + var parameters = NWParameters.CreateApplicationService (); + var isSupported = DDDevicePickerViewController.IsSupported (browserDescriptor, parameters); + + // DDDevicePickerViewController seems to work only for devices + if (TestRuntime.IsSimulator) + Assert.IsFalse (isSupported, "IsSupported"); + else + Assert.IsTrue (isSupported, "IsSupported"); + } + + [Test] + public void InitWithBrowseDescriptorAndParametersTest () + { + // DDDevicePickerViewController seems to work only for devices + TestRuntime.AssertNotSimulator (); + + var browserDescriptor = NWBrowserDescriptor.CreateApplicationServiceName (serviceName); + var parameters = NWParameters.CreateApplicationService (); + var isSupported = DDDevicePickerViewController.IsSupported (browserDescriptor, parameters); + + // If this fails, please, double check that MyAppService is registered within the Info.plist + // https://developer.apple.com/documentation/bundleresources/information_property_list/nsapplicationservices + Assert.IsTrue (isSupported, $"The {serviceName} key might not be registered in the Info.plist."); + + Assert.DoesNotThrow (() => { var devicePicker = new DDDevicePickerViewController (browserDescriptor, parameters); }, + "InitWithBrowseDescriptorAndParameters"); + } + + [Test] + public void SetDevicePickerTest () + { + // DDDevicePickerViewController seems to work only for devices + TestRuntime.AssertNotSimulator (); + + var browserDescriptor = NWBrowserDescriptor.CreateApplicationServiceName (serviceName); + var parameters = NWParameters.CreateApplicationService (); + var isSupported = DDDevicePickerViewController.IsSupported (browserDescriptor, parameters); + + // If this fails, please, double check that MyAppService is registered within the Info.plist + // https://developer.apple.com/documentation/bundleresources/information_property_list/nsapplicationservices + Assert.IsTrue (isSupported, $"The {serviceName} key might not be registered in the Info.plist."); + + Assert.DoesNotThrow (() => { + var devicePicker = new DDDevicePickerViewController (browserDescriptor, parameters); + devicePicker.SetDevicePicker ((endpoint, error) => { }); + }, + "SetDevicePicker"); + } + } +} + +#endif // __TVOS__ diff --git a/tests/monotouch-test/Info.plist b/tests/monotouch-test/Info.plist index 9004a1a4b3..a7d1f5ab13 100644 --- a/tests/monotouch-test/Info.plist +++ b/tests/monotouch-test/Info.plist @@ -62,5 +62,22 @@ processing + NSApplicationServices + + Browses + + + NSApplicationServiceIdentifier + MyAppService + NSApplicationServicePlatformSupport + + iPadOS + iOS + + NSApplicationServiceUsageDescription + You can control this app using an iOS device. + + + diff --git a/tests/monotouch-test/monotouch-test.csproj b/tests/monotouch-test/monotouch-test.csproj index f9c597c999..f876a53c9e 100644 --- a/tests/monotouch-test/monotouch-test.csproj +++ b/tests/monotouch-test/monotouch-test.csproj @@ -247,6 +247,7 @@ + diff --git a/tests/xtro-sharpie/api-annotations-dotnet/tvOS-DeviceDiscoveryUI.todo b/tests/xtro-sharpie/api-annotations-dotnet/tvOS-DeviceDiscoveryUI.todo deleted file mode 100644 index eafe357499..0000000000 --- a/tests/xtro-sharpie/api-annotations-dotnet/tvOS-DeviceDiscoveryUI.todo +++ /dev/null @@ -1,4 +0,0 @@ -!missing-selector! +DDDevicePickerViewController::isSupportedForBrowseDescriptor:parameters: not bound -!missing-selector! DDDevicePickerViewController::initWithBrowseDescriptor:parameters: not bound -!missing-selector! DDDevicePickerViewController::setDevicePickerCompletionHandler: not bound -!missing-type! DDDevicePickerViewController not bound diff --git a/tests/xtro-sharpie/tvOS-DeviceDiscoveryUI.todo b/tests/xtro-sharpie/tvOS-DeviceDiscoveryUI.todo deleted file mode 100644 index eafe357499..0000000000 --- a/tests/xtro-sharpie/tvOS-DeviceDiscoveryUI.todo +++ /dev/null @@ -1,4 +0,0 @@ -!missing-selector! +DDDevicePickerViewController::isSupportedForBrowseDescriptor:parameters: not bound -!missing-selector! DDDevicePickerViewController::initWithBrowseDescriptor:parameters: not bound -!missing-selector! DDDevicePickerViewController::setDevicePickerCompletionHandler: not bound -!missing-type! DDDevicePickerViewController not bound diff --git a/tools/common/Frameworks.cs b/tools/common/Frameworks.cs index 1e076bc9e3..726547d30e 100644 --- a/tools/common/Frameworks.cs +++ b/tools/common/Frameworks.cs @@ -643,6 +643,7 @@ public class Frameworks : Dictionary { #if !NET { "Chip", "CHIP", new Version (15, 0), NotAvailableInSimulator /* no headers in beta 2 */ }, #endif + { "DeviceDiscoveryUI", "DeviceDiscoveryUI", 16,0 }, { "OSLog", "OSLog", 15,0 }, { "CoreMidi", "CoreMIDI", 15,0 }, { "ShazamKit", "ShazamKit", new Version (15, 0), NotAvailableInSimulator},