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")
.UseHarfBuzz()
.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; }
internal static ChoreographerTimer? Timer { get; private set; }
public static void Initialize()
{
Options = AvaloniaLocator.Current.GetService() ?? new AndroidPlatformOptions();
Dispatcher.InitializeUIThreadDispatcher(new AndroidDispatcherImpl());
Timer = new ChoreographerTimer();
AvaloniaLocator.CurrentMutable
.Bind().ToTransient()
.Bind().ToConstant(new WindowingPlatformStub())
.Bind().ToSingleton()
.Bind().ToSingleton()
.Bind().ToSingleton()
.Bind().ToConstant(RenderLoop.FromTimer(Timer))
.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.");
}
}
}