فهرست منبع

iOS and Android implementations

Max Katz 2 سال پیش
والد
کامیت
a4bffbf660

+ 1 - 1
src/Android/Avalonia.Android/AndroidPlatform.cs

@@ -43,7 +43,7 @@ namespace Avalonia.Android
                 .Bind<ICursorFactory>().ToTransient<CursorFactory>()
                 .Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformStub())
                 .Bind<IKeyboardDevice>().ToSingleton<AndroidKeyboardDevice>()
-                .Bind<IPlatformSettings>().ToSingleton<DefaultPlatformSettings>()
+                .Bind<IPlatformSettings>().ToSingleton<AndroidPlatformSettings>()
                 .Bind<IPlatformThreadingInterface>().ToConstant(new AndroidThreadingInterface())
                 .Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoaderStub>()
                 .Bind<IRenderTimer>().ToConstant(new ChoreographerTimer())

+ 16 - 0
src/Android/Avalonia.Android/AvaloniaView.cs

@@ -1,11 +1,14 @@
 using System;
 using Android.Content;
+using Android.Content.Res;
 using Android.Runtime;
 using Android.Views;
 using Android.Widget;
+using Avalonia.Android.Platform;
 using Avalonia.Android.Platform.SkiaPlatform;
 using Avalonia.Controls;
 using Avalonia.Controls.Embedding;
+using Avalonia.Platform;
 using Avalonia.Rendering;
 
 namespace Avalonia.Android
@@ -26,6 +29,7 @@ namespace Avalonia.Android
             _root.Prepare();
 
             this.SetBackgroundColor(global::Android.Graphics.Color.Transparent);
+            OnConfigurationChanged();
         }
 
         internal TopLevelImpl TopLevelImpl => _view;
@@ -70,6 +74,18 @@ namespace Avalonia.Android
                 _timerSubscription?.Dispose();
             }
         }
+        
+        protected override void OnConfigurationChanged(Configuration newConfig)
+        {
+            base.OnConfigurationChanged(newConfig);
+            OnConfigurationChanged();
+        }
+
+        private void OnConfigurationChanged()
+        {
+            var settings = AvaloniaLocator.Current.GetRequiredService<IPlatformSettings>() as AndroidPlatformSettings;
+            settings?.OnViewConfigurationChanged(Context);
+        }
 
         class ViewImpl : TopLevelImpl
         {

+ 73 - 0
src/Android/Avalonia.Android/Platform/AndroidPlatformSettings.cs

@@ -0,0 +1,73 @@
+using System;
+using Android;
+using Android.Content;
+using Android.Content.Res;
+using Android.Graphics;
+using AndroidX.Core.Content.Resources;
+using Avalonia.Media;
+using Avalonia.Platform;
+using Color = Avalonia.Media.Color;
+
+namespace Avalonia.Android.Platform;
+
+// TODO: ideally should be created per view/activity.
+internal class AndroidPlatformSettings : DefaultPlatformSettings
+{
+    private PlatformColorValues _latestValues;
+
+    public AndroidPlatformSettings()
+    {
+        _latestValues = base.GetColorValues();
+    }
+
+    public override PlatformColorValues GetColorValues()
+    {
+        return _latestValues;
+    }
+
+    internal void OnViewConfigurationChanged(Context context)
+    {
+        if (context.Resources?.Configuration is null)
+        {
+            return;
+        }
+
+        var systemTheme = (context.Resources.Configuration.UiMode & UiMode.NightMask) switch
+        {
+            UiMode.NightYes => PlatformThemeVariant.Dark,
+            UiMode.NightNo => PlatformThemeVariant.Light,
+            _ => throw new ArgumentOutOfRangeException()
+        };
+
+        if (OperatingSystem.IsAndroidVersionAtLeast(31))
+        {
+            // See https://developer.android.com/reference/android/R.color
+            var accent1 = context.Resources.GetColor(17170494, context.Theme); // Resource.Color.SystemAccent1500
+            var accent2 = context.Resources.GetColor(17170507, context.Theme); // Resource.Color.SystemAccent2500
+            var accent3 = context.Resources.GetColor(17170520, context.Theme); // Resource.Color.SystemAccent3500
+
+            _latestValues = new PlatformColorValues(
+                systemTheme,
+                new Color(accent1.A, accent1.R, accent1.G, accent1.B),
+                new Color(accent2.A, accent2.R, accent2.G, accent2.B),
+                new Color(accent3.A, accent3.R, accent3.G, accent3.B));
+        }
+        else if (OperatingSystem.IsAndroidVersionAtLeast(23))
+        {
+            // See https://developer.android.com/reference/android/R.attr
+            var array = context.Theme.ObtainStyledAttributes(new[] { 16843829 }); // Resource.Attribute.ColorAccent
+            var accent = array.GetColor(0, 0);
+
+            _latestValues = new PlatformColorValues(
+                systemTheme,
+                new Color(accent.A, accent.R, accent.G, accent.B));
+            array.Recycle();
+        }
+        else
+        {
+            _latestValues = _latestValues with { ThemeVariant = systemTheme };
+        }
+
+        OnColorValuesChanged(_latestValues);
+    }
+}

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

@@ -26,6 +26,7 @@ using Avalonia.Rendering.Composition;
 using Java.Lang;
 using Math = System.Math;
 using AndroidRect = Android.Graphics.Rect;
+using Window = Android.Views.Window;
 using Android.Graphics.Drawables;
 
 namespace Avalonia.Android.Platform.SkiaPlatform
@@ -286,6 +287,11 @@ namespace Avalonia.Android.Platform.SkiaPlatform
 
         public WindowTransparencyLevel TransparencyLevel { get; private set; }
 
+        public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
+        {
+            // TODO adjust status bar depending on full screen mode.
+        }
+
         public AcrylicPlatformCompensationLevels AcrylicCompensationLevels => new AcrylicPlatformCompensationLevels(1, 1, 1);
 
         IntPtr EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Handle => ((IPlatformHandle)_view).Handle;

+ 26 - 1
src/iOS/Avalonia.iOS/AvaloniaView.cs

@@ -52,6 +52,14 @@ namespace Avalonia.iOS
 
         public override bool CanResignFirstResponder => true;
 
+        public override void TraitCollectionDidChange(UITraitCollection previousTraitCollection)
+        {
+            base.TraitCollectionDidChange(previousTraitCollection);
+            
+            var settings = AvaloniaLocator.Current.GetRequiredService<IPlatformSettings>() as PlatformSettings;
+            settings?.TraitCollectionDidChange();
+        }
+
         internal class TopLevelImpl : ITopLevelImplWithTextInputMethod, ITopLevelImplWithNativeControlHost,
             ITopLevelImplWithStorageProvider
         {
@@ -120,7 +128,24 @@ namespace Avalonia.iOS
             // legacy no-op
             public IMouseDevice MouseDevice { get; } = new MouseDevice();
             public WindowTransparencyLevel TransparencyLevel { get; }
-
+            
+            public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
+            {
+                // TODO adjust status bar depending on full screen mode.
+                if (OperatingSystem.IsIOSVersionAtLeast(13))
+                {
+                    var uiStatusBarStyle = themeVariant switch
+                    {
+                        PlatformThemeVariant.Light => UIStatusBarStyle.DarkContent,
+                        PlatformThemeVariant.Dark => UIStatusBarStyle.LightContent,
+                        _ => throw new ArgumentOutOfRangeException(nameof(themeVariant), themeVariant, null)
+                    };
+                    
+                    // Consider using UIViewController.PreferredStatusBarStyle in the future.
+                    UIApplication.SharedApplication.SetStatusBarStyle(uiStatusBarStyle, true);
+                }
+            }
+            
             public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } =
                 new AcrylicPlatformCompensationLevels();
 

+ 6 - 0
src/iOS/Avalonia.iOS/AvaloniaViewController.cs

@@ -0,0 +1,6 @@
+namespace Avalonia.iOS;
+
+public class AvaloniaViewController
+{
+    
+}

+ 1 - 1
src/iOS/Avalonia.iOS/Platform.cs

@@ -40,7 +40,7 @@ namespace Avalonia.iOS
                 .Bind<ICursorFactory>().ToConstant(new CursorFactoryStub())
                 .Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformStub())
                 .Bind<IClipboard>().ToConstant(new ClipboardImpl())
-                .Bind<IPlatformSettings>().ToSingleton<DefaultPlatformSettings>()
+                .Bind<IPlatformSettings>().ToSingleton<PlatformSettings>()
                 .Bind<IPlatformIconLoader>().ToConstant(new PlatformIconLoaderStub())
                 .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
                 .Bind<IRenderLoop>().ToSingleton<RenderLoop>()

+ 34 - 0
src/iOS/Avalonia.iOS/PlatformSettings.cs

@@ -0,0 +1,34 @@
+using System;
+using System.Linq;
+using Avalonia.Media;
+using Avalonia.Platform;
+using Foundation;
+using UIKit;
+
+namespace Avalonia.iOS;
+
+// TODO: ideally should be created per view/activity.
+internal class PlatformSettings : DefaultPlatformSettings
+{
+    private PlatformColorValues _lastColorValues;
+
+    public override PlatformColorValues GetColorValues()
+    {
+        var themeVariant = UITraitCollection.CurrentTraitCollection.UserInterfaceStyle == UIUserInterfaceStyle.Dark ?
+            PlatformThemeVariant.Dark :
+            PlatformThemeVariant.Light;
+        
+        return _lastColorValues = new PlatformColorValues(themeVariant);
+    }
+
+    public void TraitCollectionDidChange()
+    {
+        var oldColorValues = _lastColorValues;
+        var colorValues = GetColorValues();
+
+        if (oldColorValues != colorValues)
+        {
+            OnColorValuesChanged(colorValues);
+        }
+    }
+}