yavsc/src/PostIt/PostIt.Android/Services/AndroidCapturePreview.cs

90 lines
3.1 KiB
C#
Raw Normal View History

using System;
using Android.Views;
using Avalonia.Android;
using Avalonia.Controls;
using Avalonia.Platform;
using Avalonia.Threading;
namespace PostIt.Android.Services;
internal sealed class AndroidCapturePreview : Grid, IDisposable
{
private readonly PreviewHost _host;
private readonly TextBlock _status = new() { TextWrapping = Avalonia.Media.TextWrapping.Wrap };
internal AndroidCapturePreview(AndroidCameraFrameRotator rotator)
{
RowDefinitions = new RowDefinitions("*,Auto");
_host = new PreviewHost(rotator, ReportFailure);
Children.Add(_host);
SetRow(_status, 1);
Children.Add(_status);
}
private void ReportFailure(Exception error)
{
global::Android.Util.Log.Error("PostIt.Podcast", $"Apercu indisponible : {error}");
Dispatcher.UIThread.Post(() => _status.Text = $"Apercu indisponible : {error.Message}. La capture continue.");
}
public void Dispose() => _host.Dispose();
private sealed class PreviewHost(AndroidCameraFrameRotator rotator, Action<Exception> failed)
: NativeControlHost, IDisposable
{
private SurfaceView? _view;
private SurfaceCallback? _callback;
private bool _disposed;
protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
{
var activity = MainActivity.Current
?? throw new InvalidOperationException("Activite Android indisponible pour l'apercu.");
_view = new SurfaceView(activity);
_callback = new SurfaceCallback(this);
_view.Holder!.AddCallback(_callback);
return new AndroidViewControlHandle(_view);
}
private void SetSurface(ISurfaceHolder holder, int width, int height)
{
if (_disposed) return;
try { rotator.SetPreviewAsync(holder.Surface, width, height, failed).GetAwaiter().GetResult(); }
catch (Exception ex) { failed(ex); }
}
private void Detach()
{
try { rotator.SetPreviewAsync(null).GetAwaiter().GetResult(); }
catch (Exception ex) { failed(ex); }
}
protected override void DestroyNativeControlCore(IPlatformHandle control)
{
Detach();
if (_callback is not null)
{
_view?.Holder?.RemoveCallback(_callback);
_callback.Dispose();
_callback = null;
}
_view = null;
base.DestroyNativeControlCore(control);
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
Detach();
}
private sealed class SurfaceCallback(PreviewHost owner) : Java.Lang.Object, ISurfaceHolderCallback
{
public void SurfaceCreated(ISurfaceHolder holder) { }
public void SurfaceChanged(ISurfaceHolder holder, global::Android.Graphics.Format format, int width, int height)
=> owner.SetSurface(holder, width, height);
public void SurfaceDestroyed(ISurfaceHolder holder) => owner.Detach();
}
}
}