diff --git a/samples/MauiEmbedding/MauiEmbedding.MauiControls/AppBuildExtensions.cs b/samples/MauiEmbedding/MauiEmbedding.MauiControls/AppBuildExtensions.cs index ee6bec10b7..078c5fc06e 100644 --- a/samples/MauiEmbedding/MauiEmbedding.MauiControls/AppBuildExtensions.cs +++ b/samples/MauiEmbedding/MauiEmbedding.MauiControls/AppBuildExtensions.cs @@ -7,6 +7,9 @@ public static class MauiAppBuilderExtensions public static MauiAppBuilder UseCustomLibrary(this MauiAppBuilder builder) { CustomEntry.Init(); + builder.ConfigureEssentials(); + builder.Services.AddSingleton(ctx => Accelerometer.Default); + builder.Services.AddSingleton(ctx => Vibration.Default); builder.ConfigureSyncfusionCore(); return builder; } diff --git a/samples/MauiEmbedding/MauiEmbedding.Mobile/Android/AndroidManifest.xml b/samples/MauiEmbedding/MauiEmbedding.Mobile/Android/AndroidManifest.xml index 95ae07533a..addbe86965 100644 --- a/samples/MauiEmbedding/MauiEmbedding.Mobile/Android/AndroidManifest.xml +++ b/samples/MauiEmbedding/MauiEmbedding.Mobile/Android/AndroidManifest.xml @@ -1,4 +1,4 @@ - + - + diff --git a/samples/MauiEmbedding/MauiEmbedding.Mobile/Android/Main.Android.cs b/samples/MauiEmbedding/MauiEmbedding.Mobile/Android/Main.Android.cs index 289716925c..bbbf0a5af5 100644 --- a/samples/MauiEmbedding/MauiEmbedding.Mobile/Android/Main.Android.cs +++ b/samples/MauiEmbedding/MauiEmbedding.Mobile/Android/Main.Android.cs @@ -11,7 +11,7 @@ using Com.Nostra13.Universalimageloader.Core; using Microsoft.UI.Xaml.Media; - +[assembly: UsesPermission(Android.Manifest.Permission.Vibrate)] [assembly: Android.App.UsesPermission(Android.Manifest.Permission.BatteryStats)] namespace MauiEmbedding.Droid; diff --git a/samples/MauiEmbedding/MauiEmbedding.Mobile/MauiEmbedding.Mobile.csproj b/samples/MauiEmbedding/MauiEmbedding.Mobile/MauiEmbedding.Mobile.csproj index 1ceb79e5a1..b57a973f37 100644 --- a/samples/MauiEmbedding/MauiEmbedding.Mobile/MauiEmbedding.Mobile.csproj +++ b/samples/MauiEmbedding/MauiEmbedding.Mobile/MauiEmbedding.Mobile.csproj @@ -39,7 +39,7 @@ - + diff --git a/samples/MauiEmbedding/MauiEmbedding/Presentation/MainViewModel.cs b/samples/MauiEmbedding/MauiEmbedding/Presentation/MainViewModel.cs index 1e9a6b8e77..4fd66a5089 100644 --- a/samples/MauiEmbedding/MauiEmbedding/Presentation/MainViewModel.cs +++ b/samples/MauiEmbedding/MauiEmbedding/Presentation/MainViewModel.cs @@ -1,3 +1,6 @@ +using Microsoft.Maui.Devices; +using Microsoft.Maui.Devices.Sensors; + namespace MauiEmbedding.Presentation; public partial class MainViewModel : ObservableObject @@ -10,12 +13,22 @@ public partial class MainViewModel : ObservableObject public MainViewModel( IStringLocalizer localizer, IOptions appInfo, - INavigator navigator) + INavigator navigator, + IAccelerometer accelerometer, + IVibration vibrate) { _navigator = navigator; Title = "Main"; Title += $" - {localizer["ApplicationName"]}"; Title += $" - {appInfo?.Value?.Environment}"; + accelerometer.ShakeDetected += Accelerometer_ShakeDetected; + accelerometer.Start(SensorSpeed.Default); + vibrate.Vibrate(3000); + } + + private void Accelerometer_ShakeDetected(object? sender, EventArgs e) + { + } public string? Title { get; } diff --git a/src/Uno.Extensions.Maui.UI/MauiEmbedding.cs b/src/Uno.Extensions.Maui.UI/MauiEmbedding.cs index c2fe4a62e8..d6a4652ecd 100644 --- a/src/Uno.Extensions.Maui.UI/MauiEmbedding.cs +++ b/src/Uno.Extensions.Maui.UI/MauiEmbedding.cs @@ -18,21 +18,7 @@ public static partial class MauiEmbedding /// Optional lambda to configure the Maui app builder. public static IApplicationBuilder UseMauiEmbedding(this IApplicationBuilder builder, Action? configure = null) where TApp : MauiApplication - { - builder.App.UseMauiEmbedding(builder.Window, configure); - return builder; - } - - /// - /// Registers Maui embedding in the Uno Platform app builder. - /// - /// The updated app builder. - /// The IHost builder. - /// The Uno app. - /// The Main Application Window. - /// Optional lambda to configure the Maui app builder. - public static IHostBuilder UseMauiEmbedding(this IHostBuilder builder, Microsoft.UI.Xaml.Application app, Microsoft.UI.Xaml.Window window, Action? configure = null) => - builder.UseMauiEmbedding(app, window, configure); + => builder.Configure(hostBuilder => hostBuilder.UseMauiEmbedding(builder.App, builder.Window, configure)); /// /// Registers Maui embedding in the Uno Platform app builder. @@ -44,13 +30,24 @@ public static IHostBuilder UseMauiEmbedding(this IHostBuilder builder, Microsoft /// Optional lambda to configure the Maui app builder. public static IHostBuilder UseMauiEmbedding(this IHostBuilder builder, Microsoft.UI.Xaml.Application app, Microsoft.UI.Xaml.Window window, Action? configure = null) where TApp : MauiApplication - => builder.ConfigureServices(services => + { + MauiApp? mauiApp = default; + return builder + .UseServiceProviderFactory(ctx => new LinkedServiceProviderFactory(mauiApp!.Services)) + //.UseServiceProviderFactory(ctx => new LinkedScopedServiceProviderFactory(mauiApp!.Services)) + .ConfigureServices(services => { // Expose the MauiApp to the Uno app via the IHost.Services - var mauiApp = app.UseMauiEmbedding(window, configure); + mauiApp = app.UseMauiEmbedding(window, configure); services.AddSingleton(mauiApp); + //services + // .AddSingleton(()=> app.UseMauiEmbedding(window, configure)) + // .AddHostedService< MauiLoaderService>(); + }); + } + /// /// Registers Maui embedding with WinUI3 and WPF application builder. /// @@ -83,10 +80,11 @@ public static MauiApp UseMauiEmbedding(this Microsoft.UI.Xaml.Application { WindowStateManager.Default.OnActivated(window, args); }; -#endif - #endif return mauiApp; +#else + return default!; +#endif } #if MAUI_EMBEDDING @@ -168,3 +166,106 @@ public static MauiAppBuilder MapStyleHandler(this MauiAppBuilder build } */ } + +internal record MauiLoaderService(MauiApp MauiApp) : IHostedService +{ + public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask; + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; +} + +internal class LinkedServiceProviderFactory : IServiceProviderFactory +{ + private readonly IServiceProvider _childServiceProvider; + + public LinkedServiceProviderFactory(IServiceProvider childServiceProvider) + { + _childServiceProvider = childServiceProvider; + } + + public IServiceCollection CreateBuilder(IServiceCollection services) + { + return services + .AddSingleton< IServiceScopeFactory>(sp => new LinkedServiceScopeFactory(sp, _childServiceProvider)); + } + + public IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder) + { + return new LinkedServiceProvider(containerBuilder.BuildServiceProvider(), _childServiceProvider); + } +} +internal class LinkedServiceScopeFactory : IServiceScopeFactory +{ + private readonly IServiceProvider _childServiceProvider; + private readonly IServiceProvider _serviceProvider; + + public LinkedServiceScopeFactory(IServiceProvider serviceProvider, IServiceProvider childServiceProvider) + { + _serviceProvider = serviceProvider; + _childServiceProvider = childServiceProvider; + } + + public IServiceScope CreateScope() + { + var scope = _serviceProvider.CreateScope(); + return new LinkedServiceScope(scope.ServiceProvider, _childServiceProvider); + } +} + +public record LinkedServiceScope(IServiceProvider serviceProvider, IServiceProvider childServiceProvider) : IServiceScope +{ + public IServiceProvider ServiceProvider => new LinkedServiceProvider(serviceProvider, childServiceProvider); + + public void Dispose() { } +} + +//internal class LinkedScopedServiceProviderFactory : IServiceProviderFactory +//{ +// private readonly IServiceProvider _childServiceProvider; + +// public LinkedScopedServiceProviderFactory(IServiceProvider childServiceProvider) +// { +// _childServiceProvider = childServiceProvider; +// } +// public IServiceScopeFactory CreateBuilder(IServiceCollection services) +// { +// return new DefaultServiceProviderFactory().CreateBuilder(services).BuildServiceProvider().GetRequiredService(); +// } + +// public IServiceProvider CreateServiceProvider(IServiceScopeFactory containerBuilder) +// { +// return new LinkedServiceProvider(containerBuilder.BuildServiceProvider(), _childServiceProvider); +// //var scope = containerBuilder.CreateScope(); +// //var serviceProvider = scope.ServiceProvider; + +// //return new LinkedServiceProvider(serviceProvider, _childServiceProvider); +// } +//} + +internal class LinkedServiceProvider : IServiceProvider, IDisposable +{ + private readonly IServiceProvider _parent; + public readonly IServiceProvider _child; + + public LinkedServiceProvider(IServiceProvider parent, IServiceProvider child) + { + _parent = parent; + _child = child; + } + + public object? GetService(Type serviceType) + { + if(serviceType == typeof(IServiceScopeFactory)) + { + return new LinkedServiceScopeFactory(_parent, _child); + } + return _parent.GetService(serviceType) ?? _child.GetService(serviceType); + } + + public void Dispose() + { + if (_parent is IDisposable disposableChild) + { + disposableChild.Dispose(); + } + } +} diff --git a/src/Uno.Extensions.Navigation.UI/FrameworkElementExtensions.cs b/src/Uno.Extensions.Navigation.UI/FrameworkElementExtensions.cs index 6de5bf7bf0..1f2e01c958 100644 --- a/src/Uno.Extensions.Navigation.UI/FrameworkElementExtensions.cs +++ b/src/Uno.Extensions.Navigation.UI/FrameworkElementExtensions.cs @@ -14,6 +14,7 @@ public static class FrameworkElementExtensions /// The attached IServiceProvider instance - scoped for use in this visual hierarchy public static IServiceProvider AttachServiceProvider(this UIElement element, IServiceProvider services) { + var factory = services.GetService(); var scopedServices = services.CreateScope().ServiceProvider; element.SetServiceProvider(scopedServices); return scopedServices; diff --git a/src/Uno.Extensions.Navigation.UI/Navigators/ControlNavigator.cs b/src/Uno.Extensions.Navigation.UI/Navigators/ControlNavigator.cs index 26f8e72a98..934e3a3904 100644 --- a/src/Uno.Extensions.Navigation.UI/Navigators/ControlNavigator.cs +++ b/src/Uno.Extensions.Navigation.UI/Navigators/ControlNavigator.cs @@ -227,7 +227,15 @@ protected virtual void UpdateRoute(Route? route) services.AddScopedInstance(request); - var created = services.GetService(mapping!.ViewModel); + object? created = default; + try + { + created = services.GetService(mapping!.ViewModel); + } + catch + { + if (Logger.IsEnabled(LogLevel.Debug)) Logger.LogDebug("Unable to create viewmodel directly via service provider, will fall back to trying the constructor"); + } if (created is not null) {