90 lines
3.1 KiB
C#
90 lines
3.1 KiB
C#
|
|
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();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|