diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs index 91b03a2aa5..fcfa1a6ffc 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs @@ -1,30 +1,28 @@ using System; using Android.Content; using Android.Graphics; -using Android.OS; using Android.Runtime; -using Android.Util; using Android.Views; using Avalonia.Android.Platform.SkiaPlatform; +using Avalonia.Logging; using Avalonia.Platform; namespace Avalonia.Android { internal abstract class InvalidationAwareSurfaceView : SurfaceView, ISurfaceHolderCallback, INativePlatformHandleSurface { - bool _invalidateQueued; - private bool _isDisposed; - private bool _isSurfaceValid; - readonly object _lock = new object(); - private readonly Handler _handler; + private IntPtr _nativeWindowHandle = IntPtr.Zero; + private PixelSize _size = new(1, 1); + private double _scaling = 1; - internal event EventHandler? SurfaceWindowCreated; + public event EventHandler? SurfaceWindowCreated; + public PixelSize Size => _size; + public double Scaling => _scaling; - IntPtr IPlatformHandle.Handle => _isSurfaceValid && Holder?.Surface?.Handle is { } handle ? - AndroidFramebuffer.ANativeWindow_fromSurface(JNIEnv.Handle, handle) : - default; + IntPtr IPlatformHandle.Handle => _nativeWindowHandle; + string IPlatformHandle.HandleDescriptor => "SurfaceView"; - public InvalidationAwareSurfaceView(Context context) : base(context) + protected InvalidationAwareSurfaceView(Context context) : base(context) { if (Holder is null) throw new InvalidOperationException( @@ -32,71 +30,59 @@ namespace Avalonia.Android Holder.AddCallback(this); Holder.SetFormat(global::Android.Graphics.Format.Transparent); - _handler = new Handler(context.MainLooper!); } - public override void Invalidate() + protected override void Dispose(bool disposing) { - lock (_lock) - { - if (_invalidateQueued) - return; - _handler.Post(() => - { - if (_isDisposed || Holder?.Surface?.IsValid != true) - return; - try - { - DoDraw(); - } - catch (Exception e) - { - Log.WriteLine(LogPriority.Error, "Avalonia", e.ToString()); - } - }); - } - } - - internal new void Dispose() - { - _isDisposed = true; + ReleaseNativeWindowHandle(); + base.Dispose(disposing); } - public void SurfaceChanged(ISurfaceHolder holder, Format format, int width, int height) + public virtual void SurfaceChanged(ISurfaceHolder holder, Format format, int width, int height) { - _isSurfaceValid = true; - Log.Info("AVALONIA", "Surface Changed"); - DoDraw(); + CacheSurfaceProperties(holder); + Logger.TryGet(LogEventLevel.Verbose, LogArea.AndroidPlatform)? + .Log(this, "InvalidationAwareSurfaceView Changed"); } public void SurfaceCreated(ISurfaceHolder holder) { - _isSurfaceValid = true; - Log.Info("AVALONIA", "Surface Created"); + CacheSurfaceProperties(holder); + Logger.TryGet(LogEventLevel.Verbose, LogArea.AndroidPlatform)? + .Log(this, "InvalidationAwareSurfaceView Created"); SurfaceWindowCreated?.Invoke(this, EventArgs.Empty); - DoDraw(); } public void SurfaceDestroyed(ISurfaceHolder holder) { - _isSurfaceValid = false; - Log.Info("AVALONIA", "Surface Destroyed"); + ReleaseNativeWindowHandle(); + _size = new PixelSize(1, 1); + _scaling = 1; + Logger.TryGet(LogEventLevel.Verbose, LogArea.AndroidPlatform)? + .Log(this, "InvalidationAwareSurfaceView Destroyed"); + } + + private void CacheSurfaceProperties(ISurfaceHolder holder) + { + ReleaseNativeWindowHandle(); + var surface = holder?.Surface; + if (surface?.Handle is { } handle) + _nativeWindowHandle = AndroidFramebuffer.ANativeWindow_fromSurface(JNIEnv.Handle, handle); + else + _nativeWindowHandle = IntPtr.Zero; + var frame = holder?.SurfaceFrame; + _size = frame != null ? new PixelSize(frame.Width(), frame.Height()) : new PixelSize(1, 1); + _scaling = Resources?.DisplayMetrics?.Density ?? 1; } - protected void DoDraw() + private void ReleaseNativeWindowHandle() { - lock (_lock) + if (_nativeWindowHandle != IntPtr.Zero) { - _invalidateQueued = false; + AndroidFramebuffer.ANativeWindow_release(_nativeWindowHandle); + _nativeWindowHandle = IntPtr.Zero; } - Draw(); } - protected abstract void Draw(); - public string HandleDescriptor => "SurfaceView"; - - public PixelSize Size => new(Holder?.SurfaceFrame?.Width() ?? 1, Holder?.SurfaceFrame?.Height() ?? 1); - - public double Scaling => Resources?.DisplayMetrics?.Density ?? 1; } } diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index ffa55f6612..3f8d92b047 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -124,11 +124,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform InputRoot = inputRoot; } - void Draw() - { - Paint?.Invoke(new Rect(new Point(0, 0), ClientSize)); - } - public virtual void Dispose() { _systemNavigationManager.Dispose(); @@ -146,10 +141,11 @@ namespace Avalonia.Android.Platform.SkiaPlatform Resized?.Invoke(size, WindowResizeReason.Layout); } - class ViewImpl : InvalidationAwareSurfaceView, ISurfaceHolderCallback, IInitEditorInfo + sealed class ViewImpl : InvalidationAwareSurfaceView, IInitEditorInfo { private readonly TopLevelImpl _tl; private Size _oldSize; + private double _oldScaling; public ViewImpl(Context context, TopLevelImpl tl, bool placeOnTop) : base(context) { @@ -158,13 +154,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform SetZOrderOnTop(true); } - public TopLevelImpl TopLevelImpl => _tl; - - protected override void Draw() - { - _tl.Draw(); - } - protected override void DispatchDraw(global::Android.Graphics.Canvas canvas) { // Workaround issue #9230 on where screen remains gray after splash screen. @@ -211,17 +200,24 @@ namespace Avalonia.Android.Platform.SkiaPlatform return res ?? baseResult; } - void ISurfaceHolderCallback.SurfaceChanged(ISurfaceHolder holder, Format format, int width, int height) + public override void SurfaceChanged(ISurfaceHolder holder, Format format, int width, int height) { - var newSize = new PixelSize(width, height).ToSize(_tl.RenderScaling); + base.SurfaceChanged(holder, format, width, height); + + var newSize = Size.ToSize(Scaling); + var newScaling = Scaling; if (newSize != _oldSize) { _oldSize = newSize; _tl.OnResized(newSize); } - - base.SurfaceChanged(holder, format, width, height); + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (newScaling != _oldScaling) + { + _oldScaling = newScaling; + _tl.ScalingChanged?.Invoke(newScaling); + } } public sealed override bool OnCheckIsTextEditor() @@ -236,11 +232,10 @@ namespace Avalonia.Android.Platform.SkiaPlatform _initEditorInfo = init; } - public sealed override IInputConnection OnCreateInputConnection(EditorInfo? outAttrs) + public override IInputConnection OnCreateInputConnection(EditorInfo? outAttrs) { return _initEditorInfo?.Invoke(_tl, outAttrs!)!; } - } public IPopupImpl? CreatePopup() => null; @@ -280,10 +275,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform IntPtr EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Handle => ((IPlatformHandle)_view).Handle; bool EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfoWithWaitPolicy.SkipWaits => true; - - public PixelSize Size => _view.Size; - - public double Scaling => RenderScaling; + PixelSize EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Size => _view.Size; + double EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Scaling => _view.Scaling; public void SetTransparencyLevelHint(IReadOnlyList transparencyLevels) {