Просмотр исходного кода

Add back requested event for TopLevel

Emmanuel Hansen 2 лет назад
Родитель
Сommit
5936f52b2c

+ 12 - 7
src/Android/Avalonia.Android/AvaloniaMainActivity.cs

@@ -1,18 +1,14 @@
 using System;
 using Android.App;
 using Android.Content;
-using Android.Content.Res;
 using Android.OS;
 using Android.Runtime;
 using Android.Views;
 using AndroidX.AppCompat.App;
-using AndroidX.Lifecycle;
-
-using AndroidRect = Android.Graphics.Rect;
 
 namespace Avalonia.Android
 {
-    public abstract class AvaloniaMainActivity : AppCompatActivity, IActivityResultHandler
+    public abstract class AvaloniaMainActivity : AppCompatActivity, IActivityResultHandler, IActivityNavigationService
     {
         internal static object ViewContent;
 
@@ -56,9 +52,18 @@ namespace Avalonia.Android
             }
         }
 
-        public override void OnConfigurationChanged(Configuration newConfig)
+        public event EventHandler<AndroidBackRequestedEventArgs> BackRequested;
+
+        public override void OnBackPressed()
         {
-            base.OnConfigurationChanged(newConfig);
+            var eventArgs = new AndroidBackRequestedEventArgs();
+
+            BackRequested?.Invoke(this, eventArgs);
+
+            if (!eventArgs.Handled)
+            {
+                base.OnBackPressed();
+            }
         }
 
         protected override void OnDestroy()

+ 14 - 0
src/Android/Avalonia.Android/IAndroidNavigationService.cs

@@ -0,0 +1,14 @@
+using System;
+
+namespace Avalonia.Android
+{
+    public interface IActivityNavigationService
+    {
+        event EventHandler<AndroidBackRequestedEventArgs> BackRequested;
+    }
+
+    public class AndroidBackRequestedEventArgs : EventArgs
+    {
+        public bool Handled { get; set; }
+    }
+}

+ 28 - 0
src/Android/Avalonia.Android/Platform/AndroidSystemNavigationManager.cs

@@ -0,0 +1,28 @@
+using System;
+using Avalonia.Interactivity;
+using Avalonia.Platform;
+
+namespace Avalonia.Android.Platform
+{
+    internal class AndroidSystemNavigationManager : ISystemNavigationManager
+    {
+        public event EventHandler<RoutedEventArgs> BackRequested;
+
+        public AndroidSystemNavigationManager(IActivityNavigationService? navigationService)
+        {
+            if(navigationService != null)
+            {
+                navigationService.BackRequested += OnBackRequested;
+            }
+        }
+
+        private void OnBackRequested(object sender, AndroidBackRequestedEventArgs e)
+        {
+            var routedEventArgs = new RoutedEventArgs();
+
+            BackRequested?.Invoke(this, routedEventArgs);
+
+            e.Handled = routedEventArgs.Handled;
+        }
+    }
+}

+ 6 - 1
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@@ -31,7 +31,8 @@ using Android.Graphics.Drawables;
 namespace Avalonia.Android.Platform.SkiaPlatform
 {
     class TopLevelImpl : IAndroidView, ITopLevelImpl, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo,
-        ITopLevelImplWithTextInputMethod, ITopLevelImplWithNativeControlHost, ITopLevelImplWithStorageProvider
+        ITopLevelImplWithTextInputMethod, ITopLevelImplWithNativeControlHost, ITopLevelImplWithStorageProvider,
+        ITopLevelWithSystemNavigationManager
     {
         private readonly IGlPlatformSurface _gl;
         private readonly IFramebufferPlatformSurface _framebuffer;
@@ -57,6 +58,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform
 
             NativeControlHost = new AndroidNativeControlHostImpl(avaloniaView);
             StorageProvider = new AndroidStorageProvider((Activity)avaloniaView.Context);
+
+            SystemNavigationManager = new AndroidSystemNavigationManager(avaloniaView.Context as IActivityNavigationService);
         }
 
         public virtual Point GetAvaloniaPointFromEvent(MotionEvent e, int pointerIndex) =>
@@ -300,6 +303,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform
         
         public IStorageProvider StorageProvider { get; }
 
+        public ISystemNavigationManager SystemNavigationManager { get; }
+
         public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
         {
             if (TransparencyLevel != transparencyLevel)

+ 18 - 0
src/Avalonia.Base/Platform/SystemNavigationManager.cs

@@ -0,0 +1,18 @@
+using System;
+using Avalonia.Interactivity;
+using Avalonia.Metadata;
+
+namespace Avalonia.Platform
+{
+    [Unstable]
+    public interface ITopLevelWithSystemNavigationManager
+    {
+        ISystemNavigationManager SystemNavigationManager { get; }
+    }
+
+    [Unstable]
+    public interface ISystemNavigationManager
+    {
+        public event EventHandler<RoutedEventArgs>? BackRequested;
+    }
+}

+ 56 - 0
src/Avalonia.Controls/TopLevel.cs

@@ -7,6 +7,7 @@ using Avalonia.Controls.Primitives;
 using Avalonia.Input;
 using Avalonia.Input.Raw;
 using Avalonia.Input.TextInput;
+using Avalonia.Interactivity;
 using Avalonia.Layout;
 using Avalonia.Logging;
 using Avalonia.LogicalTree;
@@ -76,6 +77,12 @@ namespace Avalonia.Controls
         public static readonly StyledProperty<IBrush> TransparencyBackgroundFallbackProperty =
             AvaloniaProperty.Register<TopLevel, IBrush>(nameof(TransparencyBackgroundFallback), Brushes.White);
 
+        /// <summary>
+        /// Defines the <see cref="BackRequested"/> event.
+        /// </summary>
+        public static readonly RoutedEvent<RoutedEventArgs> BackRequestedEvent = 
+            RoutedEvent.Register<TopLevel, RoutedEventArgs>(nameof(BackRequested), RoutingStrategies.Bubble);
+
         private static readonly WeakEvent<IResourceHost, ResourcesChangedEventArgs>
             ResourcesChangedWeakEvent = WeakEvent.Register<IResourceHost, ResourcesChangedEventArgs>(
                 (s, h) => s.ResourcesChanged += h,
@@ -89,6 +96,7 @@ namespace Avalonia.Controls
         private readonly IGlobalStyles? _globalStyles;
         private readonly PointerOverPreProcessor? _pointerOverPreProcessor;
         private readonly IDisposable? _pointerOverPreProcessorSubscription;
+        private readonly IDisposable? _backGestureSubscription;
         private Size _clientSize;
         private Size? _frameSize;
         private WindowTransparencyLevel _actualTransparencyLevel;
@@ -205,6 +213,44 @@ namespace Avalonia.Controls
 
             _pointerOverPreProcessor = new PointerOverPreProcessor(this);
             _pointerOverPreProcessorSubscription = _inputManager?.PreProcess.Subscribe(_pointerOverPreProcessor);
+
+            if(impl is ITopLevelWithSystemNavigationManager topLevelWithSystemNavigation)
+            {
+                topLevelWithSystemNavigation.SystemNavigationManager.BackRequested += (s, e) =>
+                {
+                    e.RoutedEvent = BackRequestedEvent;
+                    RaiseEvent(e);
+                };
+            }
+
+            var backKeyGesture = new KeyGesture(Key.Left, KeyModifiers.Alt);
+            _backGestureSubscription = _inputManager?.PreProcess.OfType<RawInputEventArgs>().Subscribe(e =>
+            {
+                bool backRequested = false;
+
+                if (e is RawKeyEventArgs rawKeyEventArgs && rawKeyEventArgs.Type == RawKeyEventType.KeyDown)
+                {
+                    var keyEvent = new KeyEventArgs()
+                    {
+                        KeyModifiers = (KeyModifiers)rawKeyEventArgs.Modifiers,
+                        Key = rawKeyEventArgs.Key
+                    };
+
+                    backRequested = backKeyGesture.Matches(keyEvent);
+                }
+                else if(e is RawPointerEventArgs pointerEventArgs)
+                {
+                    backRequested = pointerEventArgs.Type == RawPointerEventType.XButton1Down;
+                }
+
+                if (backRequested)
+                {
+                    var backRequestedEventArgs = new RoutedEventArgs(BackRequestedEvent);
+                    RaiseEvent(backRequestedEventArgs);
+
+                    e.Handled = backRequestedEventArgs.Handled;
+                }
+            });
         }
 
         /// <summary>
@@ -263,6 +309,15 @@ namespace Avalonia.Controls
             set => SetValue(TransparencyBackgroundFallbackProperty, value);
         }
 
+        /// <summary>
+        /// Occurs when physical Back Button is pressed or a back navigation has been requested.
+        /// </summary>
+        public event EventHandler<RoutedEvent> BackRequested
+        {
+            add { AddHandler(BackRequestedEvent, value); }
+            remove { RemoveHandler(BackRequestedEvent, value); }
+        }
+
         public ILayoutManager LayoutManager
         {
             get
@@ -382,6 +437,7 @@ namespace Avalonia.Controls
 
             _pointerOverPreProcessor?.OnCompleted();
             _pointerOverPreProcessorSubscription?.Dispose();
+            _backGestureSubscription?.Dispose();
 
             PlatformImpl = null;