Move android app initialization to custom application class (#18756)
* move android app initialization to custom application class * fix tests * update api suppression * address comments
This commit is contained in:
@@ -1,6 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
|
||||
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0001</DiagnosticId>
|
||||
<Target>T:Avalonia.Android.AvaloniaMainActivity`1</Target>
|
||||
<Left>baseline/net8.0-android34.0/Avalonia.Android.dll</Left>
|
||||
<Right>target/net8.0-android34.0/Avalonia.Android.dll</Right>
|
||||
</Suppression>
|
||||
<Suppression>
|
||||
<DiagnosticId>CP0002</DiagnosticId>
|
||||
<Target>M:Avalonia.Android.AndroidViewControlHandle.get_HandleDescriptor</Target>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using Android.Runtime;
|
||||
using Avalonia;
|
||||
using Avalonia.Android;
|
||||
|
||||
namespace ControlCatalog.Android
|
||||
{
|
||||
[Application]
|
||||
public class Application : AvaloniaAndroidApplication<App>
|
||||
{
|
||||
protected Application(nint javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
|
||||
{
|
||||
return base.CustomizeAppBuilder(builder)
|
||||
.AfterSetup(_ =>
|
||||
{
|
||||
Pages.EmbedSample.Implementation = new EmbedSampleAndroid();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using Android.App;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Avalonia;
|
||||
using Avalonia.Android;
|
||||
using static Android.Content.Intent;
|
||||
|
||||
@@ -13,17 +12,9 @@ namespace ControlCatalog.Android
|
||||
{
|
||||
[Activity(Name = "com.Avalonia.ControlCatalog.MainActivity", Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", MainLauncher = true, Exported = true, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)]
|
||||
// CategoryLeanbackLauncher is required for Android TV.
|
||||
[IntentFilter(new [] { ActionView }, Categories = new [] { CategoryDefault, CategoryLeanbackLauncher })]
|
||||
public class MainActivity : AvaloniaMainActivity<App>
|
||||
[IntentFilter(new[] { ActionView }, Categories = new[] { CategoryDefault, CategoryLeanbackLauncher })]
|
||||
public class MainActivity : AvaloniaMainActivity
|
||||
{
|
||||
protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
|
||||
{
|
||||
return base.CustomizeAppBuilder(builder)
|
||||
.AfterSetup(_ =>
|
||||
{
|
||||
Pages.EmbedSample.Implementation = new EmbedSampleAndroid();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -31,7 +22,7 @@ namespace ControlCatalog.Android
|
||||
/// `AvaloniaActivity` internally will redirect parameters to the Avalonia Application.
|
||||
/// </summary>
|
||||
[Activity(NoHistory = true, LaunchMode = LaunchMode.SingleTop, Exported = true, Theme = "@android:style/Theme.NoDisplay")]
|
||||
[IntentFilter(new[] {ActionView}, Categories = new[] {CategoryDefault, CategoryBrowsable}, DataScheme = "avln")]
|
||||
[IntentFilter(new[] { ActionView }, Categories = new[] { CategoryDefault, CategoryBrowsable }, DataScheme = "avln")]
|
||||
public class DataSchemeActivity : AvaloniaActivity
|
||||
{
|
||||
protected override void OnCreate(Bundle? savedInstanceState)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto">
|
||||
<application android:label="ControlCatalog.Android" android:icon="@drawable/Icon"></application>
|
||||
<application android:name=".Application" android:label="ControlCatalog.Android" android:icon="@drawable/Icon"></application>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE " />
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using Android.App;
|
||||
using Android.Runtime;
|
||||
using Avalonia.Android;
|
||||
|
||||
namespace SafeAreaDemo.Android
|
||||
{
|
||||
[Application]
|
||||
public class Application : AvaloniaAndroidApplication<App>
|
||||
{
|
||||
protected Application(nint javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using Avalonia.Android;
|
||||
namespace SafeAreaDemo.Android
|
||||
{
|
||||
[Activity(Label = "SafeAreaDemo.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)]
|
||||
public class MainActivity : AvaloniaMainActivity<App>
|
||||
public class MainActivity : AvaloniaMainActivity
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<application android:label="SafeAreaDemo" android:icon="@drawable/Icon" />
|
||||
<application android:name=".Application" android:label="SafeAreaDemo" android:icon="@drawable/Icon" />
|
||||
</manifest>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto">
|
||||
<application android:label="SingleProjectSandbox.Android"></application>
|
||||
<application android:name=".Application" android:label="SingleProjectSandbox.Android"></application>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
</manifest>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using Android.App;
|
||||
using Android.Runtime;
|
||||
using Avalonia.Android;
|
||||
|
||||
namespace SingleProjectSandbox.Android
|
||||
{
|
||||
[Application]
|
||||
public class Application : AvaloniaAndroidApplication<App>
|
||||
{
|
||||
protected Application(nint javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,10 @@
|
||||
using Android.App;
|
||||
using Android.Content.PM;
|
||||
using Avalonia;
|
||||
using Avalonia.Android;
|
||||
|
||||
namespace SingleProjectSandbox;
|
||||
|
||||
[Activity(Label = "SingleProjectSandbox.Android", Theme = "@style/Theme.AppCompat.NoActionBar", MainLauncher = true, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
|
||||
public class MainActivity : AvaloniaMainActivity<App>
|
||||
{
|
||||
protected override AppBuilder CreateAppBuilder()
|
||||
public class MainActivity : AvaloniaMainActivity
|
||||
{
|
||||
return App.BuildAvaloniaApp().UseAndroid();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Android.Runtime;
|
||||
|
||||
namespace Avalonia.Android
|
||||
{
|
||||
internal interface IAndroidApplication
|
||||
{
|
||||
SingleViewLifetime? Lifetime { get; set; }
|
||||
}
|
||||
|
||||
public class AvaloniaAndroidApplication<TApp> : global::Android.App.Application, IAndroidApplication
|
||||
where TApp : Application, new()
|
||||
{
|
||||
SingleViewLifetime? IAndroidApplication.Lifetime { get; set; }
|
||||
|
||||
protected AvaloniaAndroidApplication(nint javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCreate()
|
||||
{
|
||||
base.OnCreate();
|
||||
InitializeAppLifetime();
|
||||
}
|
||||
|
||||
private void InitializeAppLifetime()
|
||||
{
|
||||
var builder = CreateAppBuilder();
|
||||
builder = CustomizeAppBuilder(builder);
|
||||
|
||||
var lifetime = new SingleViewLifetime();
|
||||
|
||||
((IAndroidApplication)this).Lifetime = lifetime;
|
||||
|
||||
builder.SetupWithLifetime(lifetime);
|
||||
}
|
||||
|
||||
protected virtual AppBuilder CreateAppBuilder() => AppBuilder.Configure<TApp>().UseAndroid();
|
||||
protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Avalonia.Android;
|
||||
|
||||
public class AvaloniaMainActivity<TApp> : AvaloniaMainActivity
|
||||
where TApp : Application, new()
|
||||
{
|
||||
protected override AppBuilder CreateAppBuilder() => AppBuilder.Configure<TApp>().UseAndroid();
|
||||
}
|
||||
@@ -8,41 +8,18 @@ namespace Avalonia.Android;
|
||||
|
||||
public class AvaloniaMainActivity : AvaloniaActivity
|
||||
{
|
||||
private protected static SingleViewLifetime? Lifetime;
|
||||
|
||||
private protected override void InitializeAvaloniaView(object? initialContent)
|
||||
{
|
||||
// Android can run OnCreate + InitializeAvaloniaView multiple times per process lifetime.
|
||||
// On each call we need to create new AvaloniaView, but we can't recreate Avalonia nor Avalonia controls.
|
||||
// So, if lifetime was already created previously - recreate AvaloniaView.
|
||||
// If not, initialize Avalonia, and create AvaloniaView inside of AfterSetup callback.
|
||||
// We need this AfterSetup callback to match iOS/Browser behavior and ensure that view/toplevel is available in custom AfterSetup calls.
|
||||
if (Lifetime is not null)
|
||||
if (Application is IAndroidApplication application && application.Lifetime is { } lifetime)
|
||||
{
|
||||
initialContent ??= Lifetime.MainView;
|
||||
initialContent ??= lifetime.MainView;
|
||||
|
||||
_view = new AvaloniaView(this) { Content = initialContent };
|
||||
Lifetime.Activity = this;
|
||||
lifetime.Activity = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
var builder = CreateAppBuilder();
|
||||
builder = CustomizeAppBuilder(builder);
|
||||
|
||||
Lifetime = new SingleViewLifetime();
|
||||
Lifetime.Activity = this;
|
||||
|
||||
builder
|
||||
.AfterApplicationSetup(_ =>
|
||||
{
|
||||
_view = new AvaloniaView(this) { Content = initialContent };
|
||||
})
|
||||
.SetupWithLifetime(Lifetime);
|
||||
|
||||
// AfterPlatformServicesSetup should always be called. If it wasn't, we have an unusual problem.
|
||||
if (_view is null)
|
||||
throw new InvalidOperationException("Unknown error: AvaloniaView initialization has failed.");
|
||||
}
|
||||
if (_view is null)
|
||||
throw new InvalidOperationException("Unknown error: AvaloniaView initialization has failed.");
|
||||
|
||||
if (Avalonia.Application.Current?.TryGetFeature<IActivatableLifetime>()
|
||||
is AndroidActivatableLifetime activatableLifetime)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using Android.App;
|
||||
using Android.Runtime;
|
||||
using Avalonia.Android;
|
||||
|
||||
namespace BuildTests.Android
|
||||
{
|
||||
[Application]
|
||||
public class Application : AvaloniaAndroidApplication<App>
|
||||
{
|
||||
protected Application(nint javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,4 @@ namespace BuildTests.Android;
|
||||
Label = "BuildTests.Android",
|
||||
MainLauncher = true,
|
||||
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)]
|
||||
public class MainActivity : AvaloniaMainActivity<App>;
|
||||
public class MainActivity : AvaloniaMainActivity;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<application android:label="BuildTests" />
|
||||
<application android:name=".Application"
|
||||
android:label="BuildTests" />
|
||||
</manifest>
|
||||
|
||||
Reference in New Issue
Block a user