Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions api/Avalonia.Android.nupkg.xml
Original file line number Diff line number Diff line change
@@ -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>
Expand Down
30 changes: 30 additions & 0 deletions samples/ControlCatalog.Android/Application.cs
Original file line number Diff line number Diff line change
@@ -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();
});
}
}
}
15 changes: 3 additions & 12 deletions samples/ControlCatalog.Android/MainActivity.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using Android.App;
using Android.Content.PM;
using Android.OS;
using Avalonia;
using Avalonia.Android;
using static Android.Content.Intent;

Expand All @@ -13,25 +12,17 @@ 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>
/// Special activity to handle OpenUri activation.
/// `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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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 " />
Expand Down
14 changes: 14 additions & 0 deletions samples/SafeAreaDemo.Android/Application.cs
Original file line number Diff line number Diff line change
@@ -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)
{
}
}
}
2 changes: 1 addition & 1 deletion samples/SafeAreaDemo.Android/MainActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
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
{
}
}
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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>
14 changes: 14 additions & 0 deletions samples/SingleProjectSandbox/Platforms/Android/Application.cs
Original file line number Diff line number Diff line change
@@ -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)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
45 changes: 45 additions & 0 deletions src/Android/Avalonia.Android/AvaloniaAndroidApplication.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
7 changes: 0 additions & 7 deletions src/Android/Avalonia.Android/AvaloniaMainActivity.App.cs

This file was deleted.

33 changes: 5 additions & 28 deletions src/Android/Avalonia.Android/AvaloniaMainActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 14 additions & 0 deletions tests/BuildTests/BuildTests.Android/Application.cs
Original file line number Diff line number Diff line change
@@ -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)
{
}
}
}
2 changes: 1 addition & 1 deletion tests/BuildTests/BuildTests.Android/MainActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Original file line number Diff line number Diff line change
@@ -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>
Loading