Ver código fonte

Merge branch 'master' into fixes/batchupdate-set-cleared-value-end-update

Dariusz Komosiński 4 anos atrás
pai
commit
8eee1b9f9d

+ 0 - 47
src/Android/Avalonia.Android/ActivityTracker.cs

@@ -1,47 +0,0 @@
-using Android.App;
-using Android.OS;
-
-namespace Avalonia.Android
-{
-    internal class ActivityTracker : Java.Lang.Object, global::Android.App.Application.IActivityLifecycleCallbacks
-    {
-        public static Activity Current { get; private set; }
-        public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
-        {
-            Current = activity;
-        }
-
-        public void OnActivityDestroyed(Activity activity)
-        {
-            if (Current == activity)
-                Current = null;
-        }
-
-        public void OnActivityPaused(Activity activity)
-        {
-            if (Current == activity)
-                Current = null;
-        }
-
-        public void OnActivityResumed(Activity activity)
-        {
-            Current = activity;
-        }
-
-        public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
-        {
-            Current = activity;
-        }
-
-        public void OnActivityStarted(Activity activity)
-        {
-            Current = activity;
-        }
-
-        public void OnActivityStopped(Activity activity)
-        {
-            if (Current == activity)
-                Current = null;
-        }
-    }
-}

+ 6 - 4
src/Android/Avalonia.Android/AndroidPlatform.cs

@@ -32,6 +32,7 @@ namespace Avalonia.Android
     class AndroidPlatform : IPlatformSettings, IWindowingPlatform
     {
         public static readonly AndroidPlatform Instance = new AndroidPlatform();
+        public static AndroidPlatformOptions Options { get; private set; }
         public Size DoubleClickSize => new Size(4, 4);
         public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(200);
         public double RenderScalingFactor => _scalingFactor;
@@ -46,23 +47,23 @@ namespace Avalonia.Android
 
         public static void Initialize(Type appType, AndroidPlatformOptions options)
         {
+            Options = options;
+
             AvaloniaLocator.CurrentMutable
                 .Bind<IClipboard>().ToTransient<ClipboardImpl>()
-                .Bind<IStandardCursorFactory>().ToTransient<CursorFactory>()
+                .Bind<ICursorFactory>().ToTransient<CursorFactory>()
                 .Bind<IKeyboardDevice>().ToSingleton<AndroidKeyboardDevice>()
                 .Bind<IPlatformSettings>().ToConstant(Instance)
                 .Bind<IPlatformThreadingInterface>().ToConstant(new AndroidThreadingInterface())
                 .Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>()
                 .Bind<IWindowingPlatform>().ToConstant(Instance)
                 .Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>()
-                .Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
+                .Bind<IRenderTimer>().ToConstant(new ChoreographerTimer())
                 .Bind<IRenderLoop>().ToConstant(new RenderLoop())
                 .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
                 .Bind<IAssetLoader>().ToConstant(new AssetLoader(appType.Assembly));
 
             SkiaPlatform.Initialize();
-            ((global::Android.App.Application) global::Android.App.Application.Context.ApplicationContext)
-                .RegisterActivityLifecycleCallbacks(new ActivityTracker());
 
             if (options.UseGpu)
             {
@@ -83,6 +84,7 @@ namespace Avalonia.Android
 
     public sealed class AndroidPlatformOptions
     {
+        public bool UseDeferredRendering { get; set; } = true;
         public bool UseGpu { get; set; } = true;
     }
 }

+ 1 - 2
src/Android/Avalonia.Android/AvaloniaActivity.cs

@@ -13,9 +13,8 @@ namespace Avalonia.Android
 
         protected override void OnCreate(Bundle savedInstanceState)
         {
-            RequestWindowFeature(WindowFeatures.NoTitle);
             View = new AvaloniaView(this);
-            if(_content != null)
+            if (_content != null)
                 View.Content = _content;
             SetContentView(View);
             TakeKeyEvents(true);

+ 34 - 1
src/Android/Avalonia.Android/AvaloniaView.cs

@@ -1,11 +1,12 @@
 using System;
 using Android.Content;
+using Android.Runtime;
 using Android.Views;
 using Android.Widget;
 using Avalonia.Android.Platform.SkiaPlatform;
 using Avalonia.Controls;
 using Avalonia.Controls.Embedding;
-using Avalonia.Platform;
+using Avalonia.Rendering;
 
 namespace Avalonia.Android
 {
@@ -14,6 +15,8 @@ namespace Avalonia.Android
         private readonly EmbeddableControlRoot _root;
         private readonly ViewImpl _view;
 
+        private IDisposable? _timerSubscription;
+
         public AvaloniaView(Context context) : base(context)
         {
             _view = new ViewImpl(context);
@@ -33,6 +36,36 @@ namespace Avalonia.Android
             return _view.View.DispatchKeyEvent(e);
         }
 
+        public override void OnVisibilityAggregated(bool isVisible)
+        {
+            base.OnVisibilityAggregated(isVisible);
+            OnVisibilityChanged(isVisible);
+        }
+
+        protected override void OnVisibilityChanged(View changedView, [GeneratedEnum] ViewStates visibility)
+        {
+            base.OnVisibilityChanged(changedView, visibility);
+            OnVisibilityChanged(visibility == ViewStates.Visible);
+        }
+
+        private void OnVisibilityChanged(bool isVisible)
+        {
+            if (isVisible)
+            {
+                if (AvaloniaLocator.Current.GetService<IRenderTimer>() is ChoreographerTimer timer)
+                {
+                    _timerSubscription = timer.SubscribeView(this);
+                }
+
+                _root.Renderer.Start();
+            }
+            else
+            {
+                _root.Renderer.Stop();
+                _timerSubscription?.Dispose();
+            }
+        }
+
         class ViewImpl : TopLevelImpl
         {
             public ViewImpl(Context context) : base(context)

+ 101 - 0
src/Android/Avalonia.Android/ChoreographerTimer.cs

@@ -0,0 +1,101 @@
+using System;
+using System.Collections.Generic;
+using System.Reactive.Disposables;
+using System.Threading.Tasks;
+
+using Android.OS;
+using Android.Views;
+
+using Avalonia.Rendering;
+
+using Java.Lang;
+
+namespace Avalonia.Android
+{
+    internal sealed class ChoreographerTimer : Java.Lang.Object, IRenderTimer, Choreographer.IFrameCallback
+    {
+        private readonly object _lock = new object();
+
+        private readonly Thread _thread;
+        private readonly TaskCompletionSource<Choreographer> _choreographer = new TaskCompletionSource<Choreographer>();
+
+        private readonly ISet<AvaloniaView> _views = new HashSet<AvaloniaView>();
+
+        private Action<TimeSpan> _tick;
+        private int _count;
+
+        public ChoreographerTimer()
+        {
+            _thread = new Thread(Loop);
+            _thread.Start();
+        }
+
+        public event Action<TimeSpan> Tick
+        {
+            add
+            {
+                lock (_lock)
+                {
+                    _tick += value;
+                    _count++;
+
+                    if (_count == 1)
+                    {
+                        _choreographer.Task.Result.PostFrameCallback(this);
+                    }
+                }
+            }
+            remove
+            {
+                lock (_lock)
+                {
+                    _tick -= value;
+                    _count--;
+                }
+            }
+        }
+
+        internal IDisposable SubscribeView(AvaloniaView view)
+        {
+            lock (_lock)
+            {
+                _views.Add(view);
+
+                if (_views.Count == 1)
+                {
+                    _choreographer.Task.Result.PostFrameCallback(this);
+                }
+            }
+
+            return Disposable.Create(
+                () =>
+                {
+                    lock (_lock)
+                    {
+                        _views.Remove(view);
+                    }
+                }
+            );
+        }
+
+        private void Loop()
+        {
+            Looper.Prepare();
+            _choreographer.SetResult(Choreographer.Instance);
+            Looper.Loop();
+        }
+
+        public void DoFrame(long frameTimeNanos)
+        {
+            _tick?.Invoke(TimeSpan.FromTicks(frameTimeNanos / 100));
+
+            lock (_lock)
+            {
+                if (_count > 0 && _views.Count > 0)
+                {
+                    Choreographer.Instance.PostFrameCallback(this);
+                }
+            }
+        }
+    }
+}

+ 13 - 4
src/Android/Avalonia.Android/CursorFactory.cs

@@ -1,12 +1,21 @@
-using System;
 using Avalonia.Input;
 using Avalonia.Platform;
 
 namespace Avalonia.Android
 {
-    internal class CursorFactory : IStandardCursorFactory
+    internal class CursorFactory : ICursorFactory
     {
-        public IPlatformHandle GetCursor(StandardCursorType cursorType)
-            => new PlatformHandle(IntPtr.Zero, "ZeroCursor");
+        public ICursorImpl CreateCursor(IBitmapImpl cursor, PixelPoint hotSpot) => CursorImpl.ZeroCursor;
+
+        public ICursorImpl GetCursor(StandardCursorType cursorType) => CursorImpl.ZeroCursor;
+
+        private sealed class CursorImpl : ICursorImpl
+        {
+            public static CursorImpl ZeroCursor { get; } = new CursorImpl();
+
+            private CursorImpl() { }
+
+            public void Dispose() { }
+        }
     }
 }

+ 2 - 4
src/Android/Avalonia.Android/OpenGL/GlPlatformSurface.cs

@@ -1,6 +1,4 @@
-using System.Linq;
-
-using Avalonia.OpenGL.Egl;
+using Avalonia.OpenGL.Egl;
 using Avalonia.OpenGL.Surfaces;
 
 namespace Avalonia.Android.OpenGL
@@ -17,7 +15,7 @@ namespace Avalonia.Android.OpenGL
         }
 
         public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() =>
-            new GlRenderTarget(_egl, _info, _egl.CreateWindowSurface(_info.Handle));
+            new GlRenderTarget(_egl, _info, _egl.CreateWindowSurface(_info.Handle), _info.Handle);
 
         public static GlPlatformSurface TryCreate(IEglWindowGlPlatformSurfaceInfo info)
         {

+ 10 - 3
src/Android/Avalonia.Android/OpenGL/GlRenderTarget.cs

@@ -1,23 +1,30 @@
-using Avalonia.OpenGL.Egl;
+using System;
+
+using Avalonia.OpenGL.Egl;
 using Avalonia.OpenGL.Surfaces;
 
 namespace Avalonia.Android.OpenGL
 {
-    internal sealed class GlRenderTarget : EglPlatformSurfaceRenderTargetBase
+    internal sealed class GlRenderTarget : EglPlatformSurfaceRenderTargetBase, IGlPlatformSurfaceRenderTargetWithCorruptionInfo
     {
         private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info;
         private readonly EglSurface _surface;
+        private readonly IntPtr _handle;
 
         public GlRenderTarget(
             EglPlatformOpenGlInterface egl,
             EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info,
-            EglSurface surface)
+            EglSurface surface,
+            IntPtr handle)
             : base(egl)
         {
             _info = info;
             _surface = surface;
+            _handle = handle;
         }
 
+        public bool IsCorrupted => _handle != _info.Handle;
+
         public override IGlPlatformSurfaceRenderingSession BeginDraw() => BeginDraw(_surface, _info);
     }
 }

+ 5 - 5
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@@ -98,10 +98,10 @@ namespace Avalonia.Android.Platform.SkiaPlatform
 
         public IEnumerable<object> Surfaces => new object[] { _gl, _framebuffer };
 
-        public IRenderer CreateRenderer(IRenderRoot root)
-        {
-            return new ImmediateRenderer(root);
-        }
+        public IRenderer CreateRenderer(IRenderRoot root) =>
+            AndroidPlatform.Options.UseDeferredRendering
+            ? new DeferredRenderer(root, AvaloniaLocator.Current.GetService<IRenderLoop>()) { RenderOnlyOnRenderThread = true }
+            : new ImmediateRenderer(root);
 
         public virtual void Hide()
         {
@@ -123,7 +123,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
             return PixelPoint.FromPoint(point, 1);
         }
 
-        public void SetCursor(IPlatformHandle cursor)
+        public void SetCursor(ICursorImpl cursor)
         {
             //still not implemented
         }