using System; using System.Collections.Generic; using System.Linq; using Avalonia.Android; using Avalonia.Android.Platform; using Avalonia.Android.Platform.Input; using Avalonia.Android.Platform.Vulkan; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.OpenGL.Egl; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Rendering.Composition; using Avalonia.Threading; using Avalonia.Vulkan; namespace Avalonia { public static class AndroidApplicationExtensions { public static AppBuilder UseAndroid(this AppBuilder builder) { return builder .UseAndroidRuntimePlatformSubsystem() .UseWindowingSubsystem(() => AndroidPlatform.Initialize(), "Android") .UseSkia(); } } /// /// Represents the rendering mode for platform graphics. /// public enum AndroidRenderingMode { /// /// Avalonia is rendered into a framebuffer. /// Software = 1, /// /// Enables android EGL rendering. /// Egl = 2, /// /// Enables Vulkan rendering /// Vulkan = 3 } public sealed class AndroidPlatformOptions { /// /// Gets or sets Avalonia rendering modes with fallbacks. /// The first element in the array has the highest priority. /// The default value is: , . /// /// /// If application should work on as wide range of devices as possible, at least add as a fallback value. /// /// Thrown if no values were matched. public IReadOnlyList RenderingMode { get; set; } = new[] { AndroidRenderingMode.Egl, AndroidRenderingMode.Software }; } } namespace Avalonia.Android { class AndroidPlatform { public static readonly AndroidPlatform Instance = new AndroidPlatform(); public static AndroidPlatformOptions? Options { get; private set; } internal static Compositor? Compositor { get; private set; } public static void Initialize() { Options = AvaloniaLocator.Current.GetService() ?? new AndroidPlatformOptions(); AvaloniaLocator.CurrentMutable .Bind().ToTransient() .Bind().ToConstant(new WindowingPlatformStub()) .Bind().ToSingleton() .Bind().ToSingleton() .Bind().ToConstant(new AndroidDispatcherImpl()) .Bind().ToSingleton() .Bind().ToConstant(new ChoreographerTimer()) .Bind().ToSingleton() .Bind().ToConstant(new KeyGestureFormatInfo(new Dictionary() { })) .Bind().ToConstant(new AndroidActivatableLifetime()); var graphics = InitializeGraphics(Options); if (graphics is not null) { AvaloniaLocator.CurrentMutable.Bind().ToConstant(graphics); } Compositor = new Compositor(graphics); AvaloniaLocator.CurrentMutable.Bind().ToConstant(Compositor); } private static IPlatformGraphics? InitializeGraphics(AndroidPlatformOptions opts) { if (opts.RenderingMode is null || !opts.RenderingMode.Any()) { throw new InvalidOperationException($"{nameof(AndroidPlatformOptions)}.{nameof(AndroidPlatformOptions.RenderingMode)} must not be empty or null"); } foreach (var renderingMode in opts.RenderingMode) { if (renderingMode == AndroidRenderingMode.Software) { return null; } if (renderingMode == AndroidRenderingMode.Egl) { if (EglPlatformGraphics.TryCreate() is { } egl) { return egl; } } if (renderingMode == AndroidRenderingMode.Vulkan) { var vulkan = VulkanSupport.TryInitialize(AvaloniaLocator.Current.GetService() ?? new()); if (vulkan != null) return vulkan; } } throw new InvalidOperationException($"{nameof(AndroidPlatformOptions)}.{nameof(AndroidPlatformOptions.RenderingMode)} has a value of \"{string.Join(", ", opts.RenderingMode)}\", but no options were applied."); } } }