diff --git a/.editorconfig b/.editorconfig
index 128b6f1712..179491eece 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -134,6 +134,9 @@ csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
space_within_single_line_array_initializer_braces = true
+#Net Analyzer
+dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomment when all violations are fixed.
+
# Wrapping preferences
csharp_wrap_before_ternary_opsigns = false
diff --git a/Avalonia.sln b/Avalonia.sln
index 61592e1d44..c000f56d09 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -90,6 +90,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}"
ProjectSection(SolutionItems) = preProject
build\ApiDiff.props = build\ApiDiff.props
+ build\AvaloniaPublicKey.props = build\AvaloniaPublicKey.props
build\Base.props = build\Base.props
build\Binding.props = build\Binding.props
build\CoreLibraries.props = build\CoreLibraries.props
@@ -102,8 +103,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\Microsoft.CSharp.props = build\Microsoft.CSharp.props
build\Microsoft.Reactive.Testing.props = build\Microsoft.Reactive.Testing.props
build\Moq.props = build\Moq.props
+ build\NetAnalyzers.props = build\NetAnalyzers.props
build\NetCore.props = build\NetCore.props
build\NetFX.props = build\NetFX.props
+ build\NullableEnable.props = build\NullableEnable.props
build\ReactiveUI.props = build\ReactiveUI.props
build\ReferenceCoreLibraries.props = build\ReferenceCoreLibraries.props
build\Rx.props = build\Rx.props
diff --git a/build/NetAnalyzers.props b/build/NetAnalyzers.props
new file mode 100644
index 0000000000..dfca9ecf9e
--- /dev/null
+++ b/build/NetAnalyzers.props
@@ -0,0 +1,5 @@
+
+
+ true
+
+
diff --git a/samples/ControlCatalog.Android/MainActivity.cs b/samples/ControlCatalog.Android/MainActivity.cs
index 33ca511340..62c582610c 100644
--- a/samples/ControlCatalog.Android/MainActivity.cs
+++ b/samples/ControlCatalog.Android/MainActivity.cs
@@ -5,16 +5,8 @@ using Avalonia.Android;
namespace ControlCatalog.Android
{
- [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
- public class MainActivity : AvaloniaActivity
+ [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
+ public class MainActivity : AvaloniaMainActivity
{
- protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
- {
- return base.CustomizeAppBuilder(builder)
- .AfterSetup(_ =>
- {
- Pages.EmbedSample.Implementation = new EmbedSampleAndroid();
- });
- }
}
}
diff --git a/samples/ControlCatalog.Android/SplashActivity.cs b/samples/ControlCatalog.Android/SplashActivity.cs
index dc292fd37b..908b5f082a 100644
--- a/samples/ControlCatalog.Android/SplashActivity.cs
+++ b/samples/ControlCatalog.Android/SplashActivity.cs
@@ -1,12 +1,23 @@
using Android.App;
using Android.Content;
+using Android.Content.PM;
using Android.OS;
+using Avalonia.Android;
namespace ControlCatalog.Android
{
[Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)]
- public class SplashActivity : Activity
+ public class SplashActivity : AvaloniaSplashActivity
{
+ protected override Avalonia.AppBuilder CustomizeAppBuilder(Avalonia.AppBuilder builder)
+ {
+ return base.CustomizeAppBuilder(builder)
+ .AfterSetup(_ =>
+ {
+ Pages.EmbedSample.Implementation = new EmbedSampleAndroid();
+ });
+ }
+
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
diff --git a/samples/ControlCatalog.Web/Program.cs b/samples/ControlCatalog.Web/Program.cs
index 52acabb0fa..7d05c8e462 100644
--- a/samples/ControlCatalog.Web/Program.cs
+++ b/samples/ControlCatalog.Web/Program.cs
@@ -1,8 +1,11 @@
+using System.Runtime.Versioning;
using Avalonia;
using Avalonia.Web;
using ControlCatalog;
using ControlCatalog.Web;
+[assembly:SupportedOSPlatform("browser")]
+
internal partial class Program
{
private static void Main(string[] args)
diff --git a/samples/MobileSandbox.Android/MainActivity.cs b/samples/MobileSandbox.Android/MainActivity.cs
index ac9242dd52..d65f0dec92 100644
--- a/samples/MobileSandbox.Android/MainActivity.cs
+++ b/samples/MobileSandbox.Android/MainActivity.cs
@@ -5,8 +5,8 @@ using Avalonia.Android;
namespace MobileSandbox.Android
{
- [Activity(Label = "MobileSandbox.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
- public class MainActivity : AvaloniaActivity
+ [Activity(Label = "MobileSandbox.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
+ public class MainActivity : AvaloniaMainActivity
{
}
}
diff --git a/samples/MobileSandbox.Android/SplashActivity.cs b/samples/MobileSandbox.Android/SplashActivity.cs
index c26371d6fe..ced092554d 100644
--- a/samples/MobileSandbox.Android/SplashActivity.cs
+++ b/samples/MobileSandbox.Android/SplashActivity.cs
@@ -1,11 +1,11 @@
using Android.App;
using Android.Content;
-using Android.OS;
+using Avalonia.Android;
namespace MobileSandbox.Android
{
[Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)]
- public class SplashActivity : Activity
+ public class SplashActivity : AvaloniaSplashActivity
{
protected override void OnResume()
{
diff --git a/src/Android/Avalonia.Android/AvaloniaActivity.cs b/src/Android/Avalonia.Android/AvaloniaActivity.cs
deleted file mode 100644
index 4ee4bc1375..0000000000
--- a/src/Android/Avalonia.Android/AvaloniaActivity.cs
+++ /dev/null
@@ -1,105 +0,0 @@
-using Android.OS;
-using AndroidX.AppCompat.App;
-using Android.Content.Res;
-using AndroidX.Lifecycle;
-using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Controls;
-using Android.Runtime;
-using Android.App;
-using Android.Content;
-using System;
-
-namespace Avalonia.Android
-{
- public abstract class AvaloniaActivity : AppCompatActivity
- {
- internal class SingleViewLifetime : ISingleViewApplicationLifetime
- {
- public AvaloniaView View { get; internal set; }
-
- public Control MainView
- {
- get => (Control)View.Content;
- set => View.Content = value;
- }
- }
-
- internal Action ActivityResult;
- internal AvaloniaView View;
- internal AvaloniaViewModel _viewModel;
-
- protected abstract AppBuilder CreateAppBuilder();
-
- protected override void OnCreate(Bundle savedInstanceState)
- {
- var builder = CreateAppBuilder();
-
-
- var lifetime = new SingleViewLifetime();
-
- builder.AfterSetup(x =>
- {
- _viewModel = new ViewModelProvider(this).Get(Java.Lang.Class.FromType(typeof(AvaloniaViewModel))) as AvaloniaViewModel;
-
- View = new AvaloniaView(this);
- if (_viewModel.Content != null)
- {
- View.Content = _viewModel.Content;
- }
-
- SetContentView(View);
- lifetime.View = View;
-
- View.Prepare();
- });
-
- builder.SetupWithLifetime(lifetime);
-
- base.OnCreate(savedInstanceState);
- }
- public object Content
- {
- get
- {
- return _viewModel.Content;
- }
- set
- {
- _viewModel.Content = value;
- if (View != null)
- View.Content = value;
- }
- }
-
- public override void OnConfigurationChanged(Configuration newConfig)
- {
- base.OnConfigurationChanged(newConfig);
- }
-
- protected override void OnDestroy()
- {
- View.Content = null;
-
- base.OnDestroy();
- }
-
- protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
- {
- base.OnActivityResult(requestCode, resultCode, data);
-
- ActivityResult?.Invoke(requestCode, resultCode, data);
- }
- }
-
- public abstract class AvaloniaActivity : AvaloniaActivity where TApp : Application, new()
- {
- protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid();
-
- protected override AppBuilder CreateAppBuilder()
- {
- var builder = AppBuilder.Configure();
-
- return CustomizeAppBuilder(builder);
- }
- }
-}
diff --git a/src/Android/Avalonia.Android/AvaloniaMainActivity.cs b/src/Android/Avalonia.Android/AvaloniaMainActivity.cs
new file mode 100644
index 0000000000..705fa3c59d
--- /dev/null
+++ b/src/Android/Avalonia.Android/AvaloniaMainActivity.cs
@@ -0,0 +1,72 @@
+using System;
+using Android.App;
+using Android.Content;
+using Android.Content.Res;
+using Android.OS;
+using Android.Runtime;
+using AndroidX.AppCompat.App;
+using AndroidX.Lifecycle;
+
+namespace Avalonia.Android
+{
+ public abstract class AvaloniaMainActivity : AppCompatActivity
+ {
+ internal static object ViewContent;
+
+ internal Action ActivityResult;
+ internal AvaloniaView View;
+
+ protected override void OnCreate(Bundle savedInstanceState)
+ {
+ View = new AvaloniaView(this);
+ if (ViewContent != null)
+ {
+ View.Content = ViewContent;
+ }
+
+ View.Prepare();
+
+ if (Avalonia.Application.Current.ApplicationLifetime is SingleViewLifetime lifetime)
+ {
+ lifetime.View = View;
+ }
+
+ base.OnCreate(savedInstanceState);
+
+ SetContentView(View);
+ }
+
+ public object Content
+ {
+ get
+ {
+ return ViewContent;
+ }
+ set
+ {
+ ViewContent = value;
+ if (View != null)
+ View.Content = value;
+ }
+ }
+
+ public override void OnConfigurationChanged(Configuration newConfig)
+ {
+ base.OnConfigurationChanged(newConfig);
+ }
+
+ protected override void OnDestroy()
+ {
+ View.Content = null;
+
+ base.OnDestroy();
+ }
+
+ protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
+ {
+ base.OnActivityResult(requestCode, resultCode, data);
+
+ ActivityResult?.Invoke(requestCode, resultCode, data);
+ }
+ }
+}
diff --git a/src/Android/Avalonia.Android/AvaloniaSplashActivity.cs b/src/Android/Avalonia.Android/AvaloniaSplashActivity.cs
new file mode 100644
index 0000000000..5b5ebd1bd9
--- /dev/null
+++ b/src/Android/Avalonia.Android/AvaloniaSplashActivity.cs
@@ -0,0 +1,34 @@
+using Android.OS;
+using AndroidX.AppCompat.App;
+using AndroidX.Lifecycle;
+
+namespace Avalonia.Android
+{
+ public abstract class AvaloniaSplashActivity : AppCompatActivity
+ {
+ protected abstract AppBuilder CreateAppBuilder();
+
+ protected override void OnCreate(Bundle? savedInstanceState)
+ {
+ base.OnCreate(savedInstanceState);
+
+ var builder = CreateAppBuilder();
+
+ var lifetime = new SingleViewLifetime();
+
+ builder.SetupWithLifetime(lifetime);
+ }
+ }
+
+ public abstract class AvaloniaSplashActivity : AvaloniaSplashActivity where TApp : Application, new()
+ {
+ protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid();
+
+ protected override AppBuilder CreateAppBuilder()
+ {
+ var builder = AppBuilder.Configure();
+
+ return CustomizeAppBuilder(builder);
+ }
+ }
+}
diff --git a/src/Android/Avalonia.Android/AvaloniaView.cs b/src/Android/Avalonia.Android/AvaloniaView.cs
index 94e863210b..34fa121cd7 100644
--- a/src/Android/Avalonia.Android/AvaloniaView.cs
+++ b/src/Android/Avalonia.Android/AvaloniaView.cs
@@ -74,7 +74,7 @@ namespace Avalonia.Android
class ViewImpl : TopLevelImpl
{
- public ViewImpl(AvaloniaView avaloniaView) : base(avaloniaView)
+ public ViewImpl(AvaloniaView avaloniaView) : base(avaloniaView, true)
{
View.Focusable = true;
View.FocusChange += ViewImpl_FocusChange;
diff --git a/src/Android/Avalonia.Android/AvaloniaViewModel.cs b/src/Android/Avalonia.Android/AvaloniaViewModel.cs
deleted file mode 100644
index 1b2c00987a..0000000000
--- a/src/Android/Avalonia.Android/AvaloniaViewModel.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Avalonia.Android
-{
- internal class AvaloniaViewModel : AndroidX.Lifecycle.ViewModel
- {
- public object Content { get; set; }
- }
-}
diff --git a/src/Android/Avalonia.Android/OpenGL/GlPlatformSurface.cs b/src/Android/Avalonia.Android/OpenGL/GlPlatformSurface.cs
index a9710039f8..e85ed11028 100644
--- a/src/Android/Avalonia.Android/OpenGL/GlPlatformSurface.cs
+++ b/src/Android/Avalonia.Android/OpenGL/GlPlatformSurface.cs
@@ -1,4 +1,5 @@
-using Avalonia.OpenGL.Egl;
+using Avalonia.OpenGL;
+using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
namespace Avalonia.Android.OpenGL
@@ -19,7 +20,8 @@ namespace Avalonia.Android.OpenGL
public static GlPlatformSurface TryCreate(IEglWindowGlPlatformSurfaceInfo info)
{
- if (EglPlatformOpenGlInterface.TryCreate() is EglPlatformOpenGlInterface egl)
+ var feature = AvaloniaLocator.Current.GetService();
+ if (feature is EglPlatformOpenGlInterface egl)
{
return new GlPlatformSurface(egl, info);
}
diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
index f8eaeba897..7ce72aaca5 100644
--- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
+++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
@@ -3,20 +3,14 @@ using System.Collections.Generic;
using Android.Content;
using Android.Graphics;
-using Android.Media.TV;
-using Android.OS;
using Android.Runtime;
-using Android.Text;
using Android.Views;
using Android.Views.InputMethods;
-using Android.Widget;
using Avalonia.Android.OpenGL;
-using Avalonia.Android.Platform.Input;
using Avalonia.Android.Platform.Specific;
using Avalonia.Android.Platform.Specific.Helpers;
using Avalonia.Android.Platform.Storage;
using Avalonia.Controls;
-using Avalonia.Controls.Documents;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Input;
@@ -29,7 +23,6 @@ using Avalonia.Platform.Storage;
using Avalonia.Rendering;
using Avalonia.Rendering.Composition;
using Java.Lang;
-using static System.Net.Mime.MediaTypeNames;
namespace Avalonia.Android.Platform.SkiaPlatform
{
@@ -59,7 +52,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_view.Resources.DisplayMetrics.HeightPixels).ToSize(RenderScaling);
NativeControlHost = new AndroidNativeControlHostImpl(avaloniaView);
- StorageProvider = new AndroidStorageProvider((AvaloniaActivity)avaloniaView.Context);
+ StorageProvider = new AndroidStorageProvider((AvaloniaMainActivity)avaloniaView.Context);
}
public virtual Point GetAvaloniaPointFromEvent(MotionEvent e, int pointerIndex) =>
@@ -301,7 +294,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
IsComposing = true;
- _inputMethod.Client.SetPreeditText(ComposingText);
+ _inputMethod.Client?.SetPreeditText(ComposingText);
return base.SetComposingText(text, newCursorPosition);
}
diff --git a/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageProvider.cs b/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageProvider.cs
index 653f450ec8..3a1a9e76ea 100644
--- a/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageProvider.cs
+++ b/src/Android/Avalonia.Android/Platform/Storage/AndroidStorageProvider.cs
@@ -14,10 +14,10 @@ namespace Avalonia.Android.Platform.Storage;
internal class AndroidStorageProvider : IStorageProvider
{
- private readonly AvaloniaActivity _activity;
+ private readonly AvaloniaMainActivity _activity;
private int _lastRequestCode = 20000;
- public AndroidStorageProvider(AvaloniaActivity activity)
+ public AndroidStorageProvider(AvaloniaMainActivity activity)
{
_activity = activity;
}
diff --git a/src/Android/Avalonia.Android/SingleViewLifetime.cs b/src/Android/Avalonia.Android/SingleViewLifetime.cs
new file mode 100644
index 0000000000..eef763a932
--- /dev/null
+++ b/src/Android/Avalonia.Android/SingleViewLifetime.cs
@@ -0,0 +1,26 @@
+using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
+
+namespace Avalonia.Android
+{
+ internal class SingleViewLifetime : ISingleViewApplicationLifetime
+ {
+ private AvaloniaView _view;
+
+ public AvaloniaView View
+ {
+ get => _view; internal set
+ {
+ if (_view != null)
+ {
+ _view.Content = null;
+ _view.Dispose();
+ }
+ _view = value;
+ _view.Content = MainView;
+ }
+ }
+
+ public Control MainView { get; set; }
+ }
+}
diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
index 22c410198f..74debed828 100644
--- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
+++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
@@ -9,6 +9,11 @@
NU1605;CS8632
+
+
+ false
+
+
Shared/AvaloniaResourcesIndex.cs
diff --git a/src/Avalonia.Themes.Fluent/Assets/Inter-Bold.ttf b/src/Avalonia.Themes.Fluent/Assets/Inter-Bold.ttf
index 847ffd191b..a372c5fcca 100644
Binary files a/src/Avalonia.Themes.Fluent/Assets/Inter-Bold.ttf and b/src/Avalonia.Themes.Fluent/Assets/Inter-Bold.ttf differ
diff --git a/src/Avalonia.Themes.Fluent/Assets/Inter-ExtraLight.ttf b/src/Avalonia.Themes.Fluent/Assets/Inter-ExtraLight.ttf
index 33267cd799..13ef261fff 100644
Binary files a/src/Avalonia.Themes.Fluent/Assets/Inter-ExtraLight.ttf and b/src/Avalonia.Themes.Fluent/Assets/Inter-ExtraLight.ttf differ
diff --git a/src/Avalonia.Themes.Fluent/Assets/Inter-Light.ttf b/src/Avalonia.Themes.Fluent/Assets/Inter-Light.ttf
index c22eafe9fb..9f75ff2780 100644
Binary files a/src/Avalonia.Themes.Fluent/Assets/Inter-Light.ttf and b/src/Avalonia.Themes.Fluent/Assets/Inter-Light.ttf differ
diff --git a/src/Avalonia.Themes.Fluent/Assets/Inter-Medium.ttf b/src/Avalonia.Themes.Fluent/Assets/Inter-Medium.ttf
index f782894eea..2c5e25453c 100644
Binary files a/src/Avalonia.Themes.Fluent/Assets/Inter-Medium.ttf and b/src/Avalonia.Themes.Fluent/Assets/Inter-Medium.ttf differ
diff --git a/src/Avalonia.Themes.Fluent/Assets/Inter-Regular.ttf b/src/Avalonia.Themes.Fluent/Assets/Inter-Regular.ttf
index 3b7e686e54..c28fe4d744 100644
Binary files a/src/Avalonia.Themes.Fluent/Assets/Inter-Regular.ttf and b/src/Avalonia.Themes.Fluent/Assets/Inter-Regular.ttf differ
diff --git a/src/Avalonia.Themes.Fluent/Assets/Inter-SemiBold.ttf b/src/Avalonia.Themes.Fluent/Assets/Inter-SemiBold.ttf
index 556e972f48..d085eb4994 100644
Binary files a/src/Avalonia.Themes.Fluent/Assets/Inter-SemiBold.ttf and b/src/Avalonia.Themes.Fluent/Assets/Inter-SemiBold.ttf differ
diff --git a/src/Avalonia.Themes.Fluent/Assets/Inter-Thin.ttf b/src/Avalonia.Themes.Fluent/Assets/Inter-Thin.ttf
index e49058e33d..016a13482b 100644
Binary files a/src/Avalonia.Themes.Fluent/Assets/Inter-Thin.ttf and b/src/Avalonia.Themes.Fluent/Assets/Inter-Thin.ttf differ
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 869602e452..55a1014188 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -2,6 +2,7 @@
+
Shared\_ModuleInitializer.cs