diff --git a/.github/workflows/nuget_push.yml b/.github/workflows/nuget_push.yml
index a8669aa..b19c4af 100644
--- a/.github/workflows/nuget_push.yml
+++ b/.github/workflows/nuget_push.yml
@@ -4,34 +4,60 @@ on:
workflow_dispatch:
jobs:
- build:
+ build-maui:
runs-on: windows-latest
- name: Update NuGet package
+ name: Update Aptabase.Maui NuGet package
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup .NET Core @ Latest
uses: actions/setup-dotnet@v4
-
+
- name: Setup JDK 11
uses: actions/setup-java@v2
with:
- java-version: '11'
- distribution: 'adopt'
-
+ java-version: '11'
+ distribution: 'adopt'
+
- name: Extract Version from csproj
id: get_version
run: |
- $version = Select-String -Path "./src/Aptabase.Maui.csproj" -Pattern '(.*)' | ForEach-Object { $_.Matches.Groups[1].Value }
+ $version = Select-String -Path "./src/Aptabase.Maui/Aptabase.Maui.csproj" -Pattern '(.*)' | ForEach-Object { $_.Matches.Groups[1].Value }
echo "PackageVersion=$version" | Out-File -Append -FilePath $env:GITHUB_ENV
shell: powershell
- - name: Build and Publish
+ - name: Build and Publish Aptabase.Maui
run: |
- cd ./src/
+ cd ./src/Aptabase.Maui
dotnet restore Aptabase.Maui.csproj
dotnet pack Aptabase.Maui.csproj -c Release -o artifacts -p:PackageVersion=${{ env.PackageVersion }}
- - name: Push
- run: dotnet nuget push ./src/artifacts/Aptabase.Maui.${{ env.PackageVersion }}.nupkg -k ${{ secrets.NUGET_APIKEY }} -s https://api.nuget.org/v3/index.json
\ No newline at end of file
+ - name: Push Aptabase.Maui Package
+ run: dotnet nuget push ./src/Aptabase.Maui/artifacts/Aptabase.Maui.${{ env.PackageVersion }}.nupkg -k ${{ secrets.NUGET_APIKEY }} -s https://api.nuget.org/v3/index.json
+
+ build-core:
+ runs-on: ubuntu-latest
+ name: Build and Publish Aptabase.Core NuGet package
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup .NET Core @ Latest
+ uses: actions/setup-dotnet@v4
+
+ - name: Extract Version from csproj
+ id: get_version_core
+ run: |
+ $version = Select-String -Path "./src/Aptabase.Core/Aptabase.Core.csproj" -Pattern '(.*)' | ForEach-Object { $_.Matches.Groups[1].Value }
+ echo "PackageVersion=$version" | Out-File -Append -FilePath $env:GITHUB_ENV
+ shell: bash
+
+ - name: Build and Publish Aptabase.Core
+ run: |
+ cd ./src/Aptabase.Core
+ dotnet restore Aptabase.Core.csproj
+ dotnet pack Aptabase.Core.csproj -c Release -o artifacts -p:PackageVersion=${{ env.PackageVersion }}
+
+ - name: Push Aptabase.Core Package
+ run: dotnet nuget push ./src/Aptabase.Core/artifacts/Aptabase.Core.${{ env.PackageVersion }}.nupkg -k ${{ secrets.NUGET_APIKEY }} -s https://api.nuget.org/v3/index.json
diff --git a/Aptabase.MAUI.sln b/Aptabase.NET.sln
similarity index 58%
rename from Aptabase.MAUI.sln
rename to Aptabase.NET.sln
index 4711026..e30cbee 100644
--- a/Aptabase.MAUI.sln
+++ b/Aptabase.NET.sln
@@ -1,40 +1,54 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.5.33530.505
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aptabase.Maui", "src\Aptabase.Maui.csproj", "{CDBA84BA-F326-4162-B509-31892E9CAF2E}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HelloWorld", "test\HelloWorld\HelloWorld.csproj", "{E9D22AE5-0B04-4ED6-B1BC-4242BE3D321E}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8BFFEF39-1EA0-4D6D-9988-322A74A6558D}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{19E5FDF2-25B6-48AA-BB1B-8BBA9E8323E1}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {CDBA84BA-F326-4162-B509-31892E9CAF2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CDBA84BA-F326-4162-B509-31892E9CAF2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CDBA84BA-F326-4162-B509-31892E9CAF2E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CDBA84BA-F326-4162-B509-31892E9CAF2E}.Release|Any CPU.Build.0 = Release|Any CPU
- {E9D22AE5-0B04-4ED6-B1BC-4242BE3D321E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {E9D22AE5-0B04-4ED6-B1BC-4242BE3D321E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E9D22AE5-0B04-4ED6-B1BC-4242BE3D321E}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
- {E9D22AE5-0B04-4ED6-B1BC-4242BE3D321E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {E9D22AE5-0B04-4ED6-B1BC-4242BE3D321E}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {CDBA84BA-F326-4162-B509-31892E9CAF2E} = {8BFFEF39-1EA0-4D6D-9988-322A74A6558D}
- {E9D22AE5-0B04-4ED6-B1BC-4242BE3D321E} = {19E5FDF2-25B6-48AA-BB1B-8BBA9E8323E1}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {C696E39C-8A3B-4FE3-BBB1-95FE24E67DCD}
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.33530.505
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aptabase.Maui", "src\Aptabase.Maui\Aptabase.Maui.csproj", "{CDBA84BA-F326-4162-B509-31892E9CAF2E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aptabase.Core", "src\Aptabase.Core\Aptabase.Core.csproj", "{D6DE2CBA-F461-4E04-9A96-6C0EE7E32B9E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HelloMaui", "test\HelloMaui\HelloMaui.csproj", "{E9D22AE5-0B04-4ED6-B1BC-4242BE3D321E}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8BFFEF39-1EA0-4D6D-9988-322A74A6558D}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{19E5FDF2-25B6-48AA-BB1B-8BBA9E8323E1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloWorld", "test\HelloWorld\HelloWorld.csproj", "{E6EB4FC9-CF24-4735-B8DB-EC78674A94F1}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CDBA84BA-F326-4162-B509-31892E9CAF2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CDBA84BA-F326-4162-B509-31892E9CAF2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CDBA84BA-F326-4162-B509-31892E9CAF2E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CDBA84BA-F326-4162-B509-31892E9CAF2E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E9D22AE5-0B04-4ED6-B1BC-4242BE3D321E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E9D22AE5-0B04-4ED6-B1BC-4242BE3D321E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E9D22AE5-0B04-4ED6-B1BC-4242BE3D321E}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {E9D22AE5-0B04-4ED6-B1BC-4242BE3D321E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E9D22AE5-0B04-4ED6-B1BC-4242BE3D321E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D6DE2CBA-F461-4E04-9A96-6C0EE7E32B9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D6DE2CBA-F461-4E04-9A96-6C0EE7E32B9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D6DE2CBA-F461-4E04-9A96-6C0EE7E32B9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D6DE2CBA-F461-4E04-9A96-6C0EE7E32B9E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E6EB4FC9-CF24-4735-B8DB-EC78674A94F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E6EB4FC9-CF24-4735-B8DB-EC78674A94F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E6EB4FC9-CF24-4735-B8DB-EC78674A94F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E6EB4FC9-CF24-4735-B8DB-EC78674A94F1}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {CDBA84BA-F326-4162-B509-31892E9CAF2E} = {8BFFEF39-1EA0-4D6D-9988-322A74A6558D}
+ {E9D22AE5-0B04-4ED6-B1BC-4242BE3D321E} = {19E5FDF2-25B6-48AA-BB1B-8BBA9E8323E1}
+ {D6DE2CBA-F461-4E04-9A96-6C0EE7E32B9E} = {8BFFEF39-1EA0-4D6D-9988-322A74A6558D}
+ {E6EB4FC9-CF24-4735-B8DB-EC78674A94F1} = {19E5FDF2-25B6-48AA-BB1B-8BBA9E8323E1}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C696E39C-8A3B-4FE3-BBB1-95FE24E67DCD}
+ EndGlobalSection
+EndGlobal
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f4c51a3..18192e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,15 @@
+## 0.2.2
+
+* Fix JsonElement not being included in AptabaseContext for NativeAOT.
+
+## 0.2.1
+
+* Add NativeAOT support by using JsonSerializer source generator API.
+
+## 0.2.0
+
+* Add `Aptabase.Core` for generic `.NET 8` support (Does not support crash reporter)
+
## 0.1.0
* Add `EnablePersistence` to persist events on disk before sending them to the server
diff --git a/README.md b/README.md
index 182666b..bf14c78 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@

-# MAUI SDK for Aptabase
+# .NET SDK for Aptabase
[](https://www.nuget.org/packages/Aptabase.Maui)
[](https://github.com/aptabase/aptabase-maui/blob/main/LICENSE)
@@ -12,10 +12,51 @@ Instrument your apps with Aptabase, an Open Source, Privacy-First and, Simple An
Start by adding the Aptabase NuGet package to your .csproj:
```xml
-
+
```
-## Usage
+Or, if you're using MAUI
+
+```xml
+
+```
+
+## Usage (.NET)
+
+First, you need to get your `App Key` from Aptabase, you can find it in the `Instructions` menu on the left side menu.
+
+Change your `Program.cs` to add Aptabase:
+
+```csharp
+// Create a ServiceCollection
+var services = new ServiceCollection();
+
+services.AddLogging(); // If you haven't registered a logger yet
+
+// Add Aptabase to the service collection
+services.UseAptabase("", new AptabaseOptions
+{
+#if DEBUG
+ IsDebugMode = true,
+#else
+ IsDebugMode = false,
+#endif
+ EnableCrashReporting = false, // ❌ Not supported with Aptabase.Core, only Aptabase.Maui
+ EnablePersistence = true,
+});
+
+// ... Register other services you need ...
+
+// Build the service provider
+var serviceProvider = services.BuildServiceProvider();
+
+// Get an instance of the Aptabase service (if you need it directly)
+var aptabaseClient = serviceProvider.GetRequiredService();
+...
+}
+```
+
+## Usage (Maui)
First, you need to get your `App Key` from Aptabase, you can find it in the `Instructions` menu on the left side menu.
diff --git a/src/Aptabase.Core/Aptabase.Core.csproj b/src/Aptabase.Core/Aptabase.Core.csproj
new file mode 100644
index 0000000..c4c30c5
--- /dev/null
+++ b/src/Aptabase.Core/Aptabase.Core.csproj
@@ -0,0 +1,33 @@
+
+
+
+ Aptabase.Core
+ 0.2.2
+ $(Version)
+ $(Version)
+ net8.0
+ Dotnet SDK for Aptabase: Open Source, Privacy-First and Simple Analytics for Mobile, Desktop and Web Apps
+ Aptabase Team
+ Aptabase
+ https://aptabase.com
+ README.md
+ logo.png
+ https://github.com/aptabase/aptabase-dotnet
+ git
+ analytics, privacy, dotnet, sdk, telemetry
+ MIT
+ true
+ snupkg
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AptabaseClient.cs b/src/Aptabase.Core/AptabaseClient.cs
similarity index 98%
rename from src/AptabaseClient.cs
rename to src/Aptabase.Core/AptabaseClient.cs
index dfbfdac..91aacf6 100644
--- a/src/AptabaseClient.cs
+++ b/src/Aptabase.Core/AptabaseClient.cs
@@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging;
using System.Threading.Channels;
-namespace Aptabase.Maui;
+namespace Aptabase.Core;
public class AptabaseClient : IAptabaseClient
{
diff --git a/src/AptabaseClientBase.cs b/src/Aptabase.Core/AptabaseClientBase.cs
similarity index 86%
rename from src/AptabaseClientBase.cs
rename to src/Aptabase.Core/AptabaseClientBase.cs
index 18495f3..c002c3f 100644
--- a/src/AptabaseClientBase.cs
+++ b/src/Aptabase.Core/AptabaseClientBase.cs
@@ -1,9 +1,9 @@
-using Microsoft.Extensions.Logging;
-using System.Net;
+using System.Net;
using System.Net.Http.Json;
using System.Reflection;
+using Microsoft.Extensions.Logging;
-namespace Aptabase.Maui;
+namespace Aptabase.Core;
internal class AptabaseClientBase : IAsyncDisposable
{
@@ -20,7 +20,7 @@ internal class AptabaseClientBase : IAsyncDisposable
{
{ "US", "https://us.aptabase.com" },
{ "EU", "https://eu.aptabase.com" },
- { "DEV", DeviceInfo.Platform == DevicePlatform.Android ? "https://10.0.2.2:3000" : "https://localhost:3000" },
+ { "DEV", OperatingSystem.IsAndroid() ? "https://10.0.2.2:3000" : "https://localhost:3000" },
{ "SH", "" },
};
@@ -65,7 +65,7 @@ internal async Task TrackEvent(EventData eventData)
eventData.SessionId = _sessionId;
eventData.SystemProps = _sysInfo;
- var body = JsonContent.Create(eventData);
+ var body = JsonContent.Create(eventData, AptabaseContext.Default.EventData);
var response = await _http.PostAsync("/api/v0/event", body);
@@ -81,7 +81,8 @@ internal async Task TrackEvent(EventData eventData)
var responseBody = await response.Content.ReadAsStringAsync();
- _logger?.LogError("Failed to perform TrackEvent due to {StatusCode} and response body {Body}", response.StatusCode, responseBody);
+ _logger?.LogError("Failed to perform TrackEvent due to {StatusCode} and response body {Body}",
+ response.StatusCode, responseBody);
}
}
@@ -119,7 +120,8 @@ private static string NewSessionId()
{
if (string.IsNullOrEmpty(options?.Host))
{
- _logger?.LogWarning("Host parameter must be defined when using Self-Hosted App Key. Tracking will be disabled.");
+ _logger?.LogWarning(
+ "Host parameter must be defined when using Self-Hosted App Key. Tracking will be disabled.");
return null;
}
@@ -129,4 +131,4 @@ private static string NewSessionId()
return _hosts[region];
}
-}
+}
\ No newline at end of file
diff --git a/src/Aptabase.Core/AptabaseExtensions.cs b/src/Aptabase.Core/AptabaseExtensions.cs
new file mode 100644
index 0000000..0d6dec2
--- /dev/null
+++ b/src/Aptabase.Core/AptabaseExtensions.cs
@@ -0,0 +1,38 @@
+
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace Aptabase.Core;
+
+
+public static class AptabaseExtensions
+{
+ public static IServiceCollection UseAptabase(this IServiceCollection services, string appKey, AptabaseOptions? options = null)
+ {
+ services.AddSingleton(serviceProvider =>
+ {
+ IAptabaseClient client;
+ var loggerFactory = serviceProvider.GetRequiredService();
+
+ if (options?.EnablePersistence != true)
+ {
+ client = new AptabaseClient(appKey, options, loggerFactory.CreateLogger());
+ }
+ else
+ {
+ client = new AptabasePersistentClient(appKey, options, loggerFactory.CreateLogger());
+ }
+
+ if (options?.EnableCrashReporting == true)
+ {
+ throw new NotImplementedException("Crash reporting is only for Aptabase.Maui");
+ }
+
+ return client;
+ });
+
+ return services;
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/AptabaseOptions.cs b/src/Aptabase.Core/AptabaseOptions.cs
similarity index 97%
rename from src/AptabaseOptions.cs
rename to src/Aptabase.Core/AptabaseOptions.cs
index 1ca0a0b..e6d63f3 100644
--- a/src/AptabaseOptions.cs
+++ b/src/Aptabase.Core/AptabaseOptions.cs
@@ -1,4 +1,4 @@
-namespace Aptabase.Maui;
+namespace Aptabase.Core;
///
/// Initialization options for the Aptabase Client
diff --git a/src/AptabasePersistentClient.cs b/src/Aptabase.Core/AptabasePersistentClient.cs
similarity index 56%
rename from src/AptabasePersistentClient.cs
rename to src/Aptabase.Core/AptabasePersistentClient.cs
index 57a2c6a..26640f3 100644
--- a/src/AptabasePersistentClient.cs
+++ b/src/Aptabase.Core/AptabasePersistentClient.cs
@@ -1,10 +1,39 @@
-using DotNext.Threading.Channels;
-using Microsoft.Extensions.Logging;
+using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
using System.Threading.Channels;
+using System.Xml;
+using DotNext.Threading.Channels;
+using Microsoft.Extensions.Logging;
-namespace Aptabase.Maui;
+namespace Aptabase.Core;
+
+[JsonSerializable(typeof(EventData))]
+[JsonSerializable(typeof(JsonElement))]
+[JsonSerializable(typeof(JsonNode))]
+[JsonSerializable(typeof(JsonDocument))]
+[JsonSerializable(typeof(XmlNode))]
+[JsonSerializable(typeof(XmlElement))]
+[JsonSerializable(typeof(XmlDocument))]
+[JsonSerializable(typeof(Dictionary))]
+[JsonSerializable(typeof(Dictionary))]
+[JsonSerializable(typeof(List))]
+[JsonSerializable(typeof(List>))]
+[JsonSerializable(typeof(object))]
+[JsonSerializable(typeof(string))]
+[JsonSerializable(typeof(Guid))]
+[JsonSerializable(typeof(DateTimeOffset))]
+[JsonSerializable(typeof(bool))]
+[JsonSerializable(typeof(int))]
+[JsonSerializable(typeof(double))]
+[JsonSerializable(typeof(long))]
+[JsonSerializable(typeof(Int128))]
+[JsonSerializable(typeof(ulong))]
+[JsonSerializable(typeof(uint))]
+[JsonSerializable(typeof(ushort))]
+internal partial class AptabaseContext : JsonSerializerContext;
public class AptabasePersistentClient : IAptabaseClient
{
@@ -26,13 +55,52 @@ public AptabasePersistentClient(string appKey, AptabaseOptions? options, ILogger
SingleReader = true,
ReliableEnumeration = true,
PartitionCapacity = _maxPersistedEvents,
- Location = Path.Combine(FileSystem.CacheDirectory, "EventData"),
+ Location = Path.Combine(CacheHome, "Aptabase", "EventData"),
});
_logger = logger;
_cts = new CancellationTokenSource();
_processingTask = Task.Run(ProcessEventsAsync);
}
+ private static string CacheHome =>
+ Environment.GetEnvironmentVariable("XDG_CACHE_HOME")
+ ?? GetCurrentPlatform() switch
+ {
+ var platform when platform == OSPlatform.Windows
+ => Environment.GetEnvironmentVariable("LOCALAPPDATA") is not null
+ ? Path.Combine(Environment.GetEnvironmentVariable("LOCALAPPDATA")!, "cache")
+ : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+ "cache"),
+ var platform when platform == OSPlatform.OSX
+ => Path.Combine(Home, "Library", "Caches"),
+ _ => Path.Combine(Home, ".cache") // Linux/FreeBSD
+ };
+
+ private static OSPlatform? GetCurrentPlatform()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return OSPlatform.Windows;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return OSPlatform.OSX;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return OSPlatform.Linux;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) return OSPlatform.FreeBSD;
+
+ return null;
+ }
+
+ private static string Home
+ {
+ get
+ {
+ var homeEnv = GetCurrentPlatform() switch
+ {
+ var platform when platform == OSPlatform.Windows => Environment.GetEnvironmentVariable("USERPROFILE") ??
+ Environment.GetFolderPath(Environment.SpecialFolder
+ .UserProfile),
+ _ => Environment.GetEnvironmentVariable("HOME") // Unix*
+ };
+ return homeEnv ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
+ }
+ }
+
public async Task TrackEvent(string eventName, Dictionary? props = null)
{
var eventData = new EventData(eventName, props);
@@ -67,8 +135,9 @@ private async ValueTask ProcessEventsAsync()
if (_channel.RemainingCount > _maxPersistedEvents)
{
- _logger?.LogError("ProcessEvents flushed {Name}@{Timestamp}", eventData.EventName, eventData.Timestamp);
-
+ _logger?.LogError("ProcessEvents flushed {Name}@{Timestamp}", eventData.EventName,
+ eventData.Timestamp);
+
continue;
}
@@ -101,7 +170,9 @@ public async ValueTask DisposeAsync()
{
_cts.Cancel();
}
- catch { }
+ catch
+ {
+ }
_channel.Writer.Complete();
@@ -127,7 +198,8 @@ protected override async ValueTask DeserializeAsync(Stream input, Can
{
try
{
- return JsonSerializer.Deserialize(await ExtractJsonObject(input, token), typeof(EventData)) as EventData ?? throw new NullReferenceException();
+ return JsonSerializer.Deserialize(await ExtractJsonObject(input, token),
+ AptabaseContext.Default.EventData) ?? throw new NullReferenceException();
}
catch
{
@@ -138,8 +210,8 @@ protected override async ValueTask DeserializeAsync(Stream input, Can
protected override ValueTask SerializeAsync(EventData input, Stream output, CancellationToken token)
{
- JsonSerializer.Serialize(output, input);
- output.WriteByte((byte)'\n'); // append jsonl/ndjson separator
+ JsonSerializer.Serialize(output, input, AptabaseContext.Default.EventData);
+ output.WriteByte((byte)'\n'); // append jsonl/ndjson separator
output.Flush();
return new ValueTask();
}
@@ -152,7 +224,8 @@ private async static Task ExtractJsonObject(Stream input, CancellationTo
{
sb.Append((char)b[0]);
}
+
return sb.ToString();
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Aptabase.Core/DeviceDetect.cs b/src/Aptabase.Core/DeviceDetect.cs
new file mode 100644
index 0000000..0bdd8fe
--- /dev/null
+++ b/src/Aptabase.Core/DeviceDetect.cs
@@ -0,0 +1,92 @@
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+#pragma warning disable CA1416 // I like to live dangerously.
+using Microsoft.Win32;
+
+namespace Aptabase.Core;
+
+internal static class DeviceDetect
+{
+ internal static string GetDeviceModel()
+ {
+ return RuntimeInformation.OSDescription.ToLower() switch
+ {
+ var os when OperatingSystem.IsWindows() => GetWindowsDeviceModel(),
+ var os when OperatingSystem.IsMacOS() => GetMacDeviceModel(),
+ var os when OperatingSystem.IsLinux() => GetLinuxDeviceModel(),
+ var os when OperatingSystem.IsFreeBSD() => GetFreeBSDDeviceModel(),
+ _ => "" // Unsupported Device Detection Platform
+ };
+ }
+
+ private static string GetWindowsDeviceModel()
+ {
+ const string registryKeyPath = @"HARDWARE\DESCRIPTION\System\BIOS";
+ const string registryValueName = "SystemProductName";
+ try
+ {
+ using var key = Registry.LocalMachine.OpenSubKey(registryKeyPath);
+ if (key == null) return "";
+
+ var model = key.GetValue(registryValueName) as string;
+
+ return !string.IsNullOrEmpty(model) ? model : "";
+ }
+ catch
+ {
+ return "";
+ }
+ }
+ private static string GetMacDeviceModel()
+ {
+ var processStartInfo = new ProcessStartInfo
+ {
+ FileName = "sysctl",
+ Arguments = "-n hw.model",
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ CreateNoWindow = true
+ };
+
+ using var process = Process.Start(processStartInfo);
+ if (process == null) return "";
+
+ var output = process.StandardOutput.ReadToEnd();
+ var modelIdentifier = output.Trim();
+
+ return string.IsNullOrEmpty(modelIdentifier) ? "" : modelIdentifier;
+ }
+
+ private static string GetLinuxDeviceModel()
+ {
+ try
+ {
+ var model = File.ReadAllText("/sys/class/dmi/id/product_name");
+ return model.Trim();
+ }
+ catch (IOException)
+ {
+ return "";
+ }
+ }
+
+ private static string GetFreeBSDDeviceModel()
+ {
+ var processStartInfo = new ProcessStartInfo
+ {
+ FileName = "kenv",
+ Arguments = "smbios.system.product",
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ CreateNoWindow = true
+ };
+
+ using var process = Process.Start(processStartInfo);
+ if (process == null) return "";
+
+ var output = process.StandardOutput.ReadToEnd();
+ var deviceModel = output.Trim();
+
+ return string.IsNullOrEmpty(deviceModel) ? "" : deviceModel;
+ }
+}
\ No newline at end of file
diff --git a/src/Aptabase.Core/DistroDetect.cs b/src/Aptabase.Core/DistroDetect.cs
new file mode 100644
index 0000000..78a19dc
--- /dev/null
+++ b/src/Aptabase.Core/DistroDetect.cs
@@ -0,0 +1,112 @@
+namespace Aptabase.Core;
+
+internal static class DistroDetect
+{
+ // Linux distributions also have /var/lib/lsb-release but I wanted to keep this simple.
+ public static (string Name, string Version) GetLinuxInfo()
+ {
+ const string osReleaseFile = "/etc/os-release";
+
+ // Try reading /etc/os-release
+ try
+ {
+ var data = File.ReadAllText(osReleaseFile);
+ return ParseOsRelease(data);
+ }
+ catch (IOException)
+ {
+ // Fallback to lsb_release if /etc/os-release is inaccessible (e.g., under firejail)
+ return ParseLsbReleaseOrFallback();
+ }
+ }
+ private static (string Name, string Version) ParseOsRelease(string data)
+ {
+ string name = string.Empty, version = string.Empty;
+
+ var lines = data.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
+
+ foreach (var line in lines)
+ {
+ if (line.StartsWith("NAME="))
+ {
+ name = TrimValue(line);
+ }
+ else if (line.StartsWith("VERSION_ID="))
+ {
+ version = TrimValue(line);
+ }
+ else if (line.StartsWith("VERSION=") && string.IsNullOrEmpty(version))
+ {
+ version = TrimValue(line);
+ }
+ else if (line.StartsWith("VERSION_CODENAME=") && string.IsNullOrEmpty(version))
+ {
+ version = TrimValue(line);
+ }
+ }
+
+ return (name, version);
+ }
+
+ private static (string Name, string Version) ParseLsbReleaseOrFallback()
+ {
+ // Fallback parsing if /etc/os-release is unavailable, using lsb_release command
+ // I know the file exists, but according to a StackOverflow wizard, it shouldn't be used.
+ try
+ {
+ var lsbReleaseOutput = ExecuteCommand("lsb_release", "-a");
+ string name = string.Empty, version = string.Empty;
+
+ foreach (var line in lsbReleaseOutput.Split('\n'))
+ {
+ if (line.StartsWith("Distributor ID:"))
+ {
+ name = line.Split(new[] { ':' }, 2)[1].Trim();
+ }
+ else if (line.StartsWith("Release:"))
+ {
+ version = line.Split(new[] { ':' }, 2)[1].Trim();
+ }
+ }
+
+ return (name, version);
+ }
+ catch (Exception)
+ {
+ return ("", "");
+ }
+ }
+
+ private static string TrimValue(string line)
+ {
+ // Extracts and trims the value from the line, removing the surrounding quotes
+ return line.Split(new[] { '=' }, 2)[1].Trim('\"');
+ }
+
+ private static string ExecuteCommand(string command, string arguments)
+ {
+ var startInfo = new System.Diagnostics.ProcessStartInfo
+ {
+ FileName = command,
+ Arguments = arguments,
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ CreateNoWindow = true
+ };
+
+ using var process = System.Diagnostics.Process.Start(startInfo);
+ if (process == null) return "";
+
+ return process.StandardOutput.ReadToEnd();
+ }
+ internal static string GetLinuxDistro()
+ {
+ var distroName = GetLinuxInfo().Name;
+ return string.IsNullOrWhiteSpace(distroName) ? "Linux" : distroName;
+ }
+ internal static string GetLinuxDistroVersion()
+ {
+ var distroVersion = GetLinuxInfo().Version;
+ return string.IsNullOrWhiteSpace(distroVersion) ? Environment.OSVersion.Version.ToString() : distroVersion;
+ }
+}
\ No newline at end of file
diff --git a/src/EventData.cs b/src/Aptabase.Core/EventData.cs
similarity index 94%
rename from src/EventData.cs
rename to src/Aptabase.Core/EventData.cs
index 1fd1bbd..f968c36 100644
--- a/src/EventData.cs
+++ b/src/Aptabase.Core/EventData.cs
@@ -1,4 +1,4 @@
-namespace Aptabase.Maui;
+namespace Aptabase.Core;
internal class EventData
{
diff --git a/src/IAptabaseClient.cs b/src/Aptabase.Core/IAptabaseClient.cs
similarity index 88%
rename from src/IAptabaseClient.cs
rename to src/Aptabase.Core/IAptabaseClient.cs
index 9b12214..bc35bd6 100644
--- a/src/IAptabaseClient.cs
+++ b/src/Aptabase.Core/IAptabaseClient.cs
@@ -1,4 +1,4 @@
-namespace Aptabase.Maui;
+namespace Aptabase.Core;
///
/// Aptabase client used for tracking events
diff --git a/src/Aptabase.Core/LocalHttpsClientHandler.cs b/src/Aptabase.Core/LocalHttpsClientHandler.cs
new file mode 100644
index 0000000..8fe1457
--- /dev/null
+++ b/src/Aptabase.Core/LocalHttpsClientHandler.cs
@@ -0,0 +1,95 @@
+using System.Net;
+using System.Net.Sockets;
+
+namespace Aptabase.Core;
+
+public class LocalHttpsClientHandler : HttpClientHandler
+{
+ public LocalHttpsClientHandler()
+ {
+ // Disable SSL verification
+ ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) =>
+ {
+ var requestUri = sender.RequestUri;
+ if (requestUri is null || requestUri.Scheme != "https") return false;
+ return IsLocalAddress(requestUri.Host);
+ };
+ }
+ ///
+ /// Checks if a given host string represents a local IP address.
+ /// This includes loopback and common private IP ranges.
+ ///
+ /// The host string (IP address or hostname).
+ /// True if the host is considered local, false otherwise.
+ private static bool IsLocalAddress(string host)
+ {
+ try
+ {
+ if (host.Equals("localhost", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ bool CheckIp(IPAddress ipAddr)
+ {
+ if (IPAddress.IsLoopback(ipAddr))
+ {
+ return true;
+ }
+
+ switch (ipAddr.AddressFamily)
+ {
+ case AddressFamily.InterNetwork:
+ {
+ var ipBytes = ipAddr.GetAddressBytes();
+ switch (ipBytes[0])
+ {
+ case 10:
+ case 172 when (ipBytes[1] >= 16 && ipBytes[1] <= 31):
+ return true;
+ }
+
+ if (ipBytes[0] == 192 && ipBytes[1] == 168) return true;
+ break;
+ }
+ case AddressFamily.InterNetworkV6:
+ {
+ var ipBytes = ipAddr.GetAddressBytes();
+ switch (ipBytes[0])
+ {
+ case 0xFC:
+ case 0xFD:
+ case 0xFE when (ipBytes[1] & 0xC0) == 0x80:
+ return true;
+ }
+
+ break;
+ }
+ }
+
+ return false;
+ }
+
+ if (IPAddress.TryParse(host, out var ipAddress))
+ {
+ if (CheckIp(ipAddress))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ var hostEntry = Dns.GetHostEntry(host);
+ if (hostEntry.AddressList.Any(CheckIp))
+ {
+ return true;
+ }
+ }
+ }
+ catch
+ {
+ // Ignore
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/SystemInfo.cs b/src/Aptabase.Core/SystemInfo.cs
similarity index 52%
rename from src/SystemInfo.cs
rename to src/Aptabase.Core/SystemInfo.cs
index 6e25c69..797e84d 100644
--- a/src/SystemInfo.cs
+++ b/src/Aptabase.Core/SystemInfo.cs
@@ -1,12 +1,13 @@
using System.Diagnostics;
using System.Reflection;
-namespace Aptabase.Maui;
+namespace Aptabase.Core;
internal class SystemInfo
{
- private static readonly string _pkgVersion = typeof(AptabaseClient).Assembly
- .GetCustomAttribute()!.Version;
+ private const string PkgName = "Aptabase.NET";
+ private static readonly Assembly PkgAssembly = Assembly.GetExecutingAssembly();
+ private static readonly string PkgVersion = PkgAssembly.GetCustomAttribute()!.Version;
public bool IsDebug { get; set; }
public string OsName { get; }
@@ -21,11 +22,11 @@ public SystemInfo()
{
OsName = GetOsName();
OsVersion = GetOsVersion();
- DeviceModel = DeviceInfo.Current.Model;
- SdkVersion = $"Aptabase.Maui@{_pkgVersion}";
+ DeviceModel = DeviceDetect.GetDeviceModel();
+ SdkVersion = $"{PkgName}@{PkgVersion}";
Locale = Thread.CurrentThread.CurrentCulture.Name;
- AppVersion = AppInfo.Current.VersionString;
- AppBuildNumber = AppInfo.Current.BuildString;
+ AppVersion = Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? string.Empty;
+ AppBuildNumber = Assembly.GetEntryAssembly()?.GetName().Version?.Build.ToString() ?? string.Empty;
}
internal static bool IsInDebugMode(Assembly? assembly)
@@ -46,35 +47,25 @@ internal static bool IsInDebugMode(Assembly? assembly)
}
private static string GetOsName()
- {
- var platform = DeviceInfo.Current.Platform;
- if (platform == DevicePlatform.Android)
- return "Android";
+ {
+ if (OperatingSystem.IsAndroid())
+ return "Android";
- if (platform == DevicePlatform.WinUI)
+ if (OperatingSystem.IsWindows())
return "Windows";
+
+
+ if (OperatingSystem.IsMacOS()) return "macOS";
+ if (OperatingSystem.IsTvOS()) return "tvOS";
+ if (OperatingSystem.IsWatchOS()) return "watchOS";
+ // iPadOS is bundled as iOS as well. It's hard to tell the difference from pure .NET
+ if (OperatingSystem.IsIOS()) return "iOS";
- if (platform == DevicePlatform.macOS || platform == DevicePlatform.MacCatalyst)
- return "macOS";
-
- if (platform == DevicePlatform.tvOS)
- return "tvOS";
-
- if (platform == DevicePlatform.watchOS)
- return "watchOS";
-
- if (platform == DevicePlatform.Tizen)
- return "Tizen";
-
- if (platform == DevicePlatform.iOS)
- {
- if (DeviceInfo.Current.Idiom == DeviceIdiom.Tablet && DeviceInfo.Current.Version.Major >= 13)
- return "iPadOS";
+ if (OperatingSystem.IsLinux()) return DistroDetect.GetLinuxDistro();
+ if (OperatingSystem.IsFreeBSD()) return "FreeBSD";
- return "iOS";
- }
- return "";
+ return string.Empty;
}
private static string GetOsVersion()
@@ -83,7 +74,7 @@ private static string GetOsVersion()
var osVersion = Foundation.NSProcessInfo.ProcessInfo.OperatingSystemVersion;
return $"{osVersion.Major}.{osVersion.Minor}.{osVersion.PatchVersion}";
#else
- return DeviceInfo.Current.VersionString;
+ return OperatingSystem.IsLinux() ? DistroDetect.GetLinuxDistroVersion() : Environment.OSVersion.Version.ToString();
#endif
}
}
diff --git a/src/Aptabase.Maui.csproj b/src/Aptabase.Maui/Aptabase.Maui.csproj
similarity index 88%
rename from src/Aptabase.Maui.csproj
rename to src/Aptabase.Maui/Aptabase.Maui.csproj
index 47ebb2d..717bfd6 100644
--- a/src/Aptabase.Maui.csproj
+++ b/src/Aptabase.Maui/Aptabase.Maui.csproj
@@ -2,7 +2,7 @@
Aptabase.Maui
- 0.1.0
+ 0.2.0
$(Version)
$(Version)
net8.0;net8.0-android;net8.0-ios;net8.0-maccatalyst
@@ -12,7 +12,7 @@
https://aptabase.com
README.md
logo.png
- https://github.com/aptabase/aptabase-maui
+ https://github.com/aptabase/aptabase-dotnet
git
analytics, privacy, maui, sdk, telemetry
MIT
@@ -42,12 +42,12 @@
false
-
-
+
+
-
-
+
+
diff --git a/src/AptabaseCrashReporter.cs b/src/Aptabase.Maui/AptabaseMauiCrashReporter.cs
similarity index 90%
rename from src/AptabaseCrashReporter.cs
rename to src/Aptabase.Maui/AptabaseMauiCrashReporter.cs
index d0d2d16..cc822aa 100644
--- a/src/AptabaseCrashReporter.cs
+++ b/src/Aptabase.Maui/AptabaseMauiCrashReporter.cs
@@ -1,18 +1,19 @@
-using Microsoft.Extensions.Logging;
+using Aptabase.Core;
+using Microsoft.Extensions.Logging;
namespace Aptabase.Maui;
-public class AptabaseCrashReporter
+public class AptabaseMauiCrashReporter
{
private readonly IAptabaseClient _client;
- private readonly ILogger? _logger;
+ private readonly ILogger? _logger;
#if ANDROID
// the UnhandledExceptionRaiser fires first, but others may fire redundantly soon after
private bool _nativeThrown;
#endif
- public AptabaseCrashReporter(IAptabaseClient client, ILogger? logger)
+ public AptabaseMauiCrashReporter(IAptabaseClient client, ILogger? logger)
{
_client = client;
_logger = logger;
diff --git a/src/AptabaseExtensions.cs b/src/Aptabase.Maui/MauiExtensions.cs
similarity index 86%
rename from src/AptabaseExtensions.cs
rename to src/Aptabase.Maui/MauiExtensions.cs
index 80d122f..e4037e3 100644
--- a/src/AptabaseExtensions.cs
+++ b/src/Aptabase.Maui/MauiExtensions.cs
@@ -1,4 +1,5 @@
-using Aptabase.Maui;
+using Aptabase.Core;
+using Aptabase.Maui;
using Microsoft.Extensions.Logging;
namespace Microsoft.Maui.Hosting;
@@ -6,7 +7,7 @@ namespace Microsoft.Maui.Hosting;
///
/// Aptabase extensions for .
///
-public static class AptabaseExtensions
+public static class MauiExtensions
{
///
/// Uses Aptabase integration.
@@ -34,7 +35,7 @@ public static MauiAppBuilder UseAptabase(this MauiAppBuilder builder, string app
if (options?.EnableCrashReporting == true)
{
- _ = new AptabaseCrashReporter(client, loggerFactory.CreateLogger());
+ _ = new AptabaseMauiCrashReporter(client, loggerFactory.CreateLogger());
}
return client;
diff --git a/src/LocalHttpsClientHandler.cs b/src/LocalHttpsClientHandler.cs
deleted file mode 100644
index d5e5088..0000000
--- a/src/LocalHttpsClientHandler.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-#if ANDROID
-using Xamarin.Android.Net;
-#endif
-#if IOS
-using Foundation;
-#endif
-
-namespace Aptabase.Maui;
-
-public class LocalHttpsClientHandler : DelegatingHandler
-{
- public LocalHttpsClientHandler()
- {
-#if ANDROID
- InnerHandler = new AndroidMessageHandler
- {
- ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
- {
- if (cert?.Issuer != null && cert.Issuer.Contains("CN=mkcert"))
- {
- return true;
- }
- return errors == System.Net.Security.SslPolicyErrors.None;
- }
- };
-#elif IOS
- InnerHandler = new NSUrlSessionHandler
- {
- TrustOverrideForUrl = (sender, url, trust) => url.StartsWith("https://localhost"),
- };
-#else
- InnerHandler = new HttpClientHandler();
-#endif
- }
-}
\ No newline at end of file
diff --git a/test/HelloWorld/App.xaml b/test/HelloMaui/App.xaml
similarity index 58%
rename from test/HelloWorld/App.xaml
rename to test/HelloMaui/App.xaml
index f3e53f8..2f2dbf7 100644
--- a/test/HelloWorld/App.xaml
+++ b/test/HelloMaui/App.xaml
@@ -1,8 +1,8 @@
-
-
+
+
diff --git a/test/HelloWorld/App.xaml.cs b/test/HelloMaui/App.xaml.cs
similarity index 80%
rename from test/HelloWorld/App.xaml.cs
rename to test/HelloMaui/App.xaml.cs
index 5633123..9fd6dcd 100644
--- a/test/HelloWorld/App.xaml.cs
+++ b/test/HelloMaui/App.xaml.cs
@@ -1,6 +1,6 @@
-using Aptabase.Maui;
+using Aptabase.Core;
-namespace HelloWorld;
+namespace HelloMaui;
public partial class App : Application
{
diff --git a/test/HelloWorld/AppShell.xaml b/test/HelloMaui/AppShell.xaml
similarity index 81%
rename from test/HelloWorld/AppShell.xaml
rename to test/HelloMaui/AppShell.xaml
index a328548..8a9bd39 100644
--- a/test/HelloWorld/AppShell.xaml
+++ b/test/HelloMaui/AppShell.xaml
@@ -1,9 +1,9 @@
+
+
+ net8.0-android;net8.0-ios;net8.0-maccatalyst
+ $(TargetFrameworks);net8.0-windows10.0.19041.0
+
+
+ Exe
+ HelloMaui
+ true
+ 8.0.70
+ true
+ enable
+
+
+ HelloMaui
+
+
+ com.companyname.hellomaui
+ 91c837fe-e892-450c-8365-a45127a53bc3
+
+
+ 1.0
+ 1
+
+ 11.0
+ 13.1
+ 21.0
+ 10.0.17763.0
+ 10.0.17763.0
+ 6.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/HelloWorld/MainPage.xaml b/test/HelloMaui/MainPage.xaml
similarity index 96%
rename from test/HelloWorld/MainPage.xaml
rename to test/HelloMaui/MainPage.xaml
index 8cabec1..a9e44fd 100644
--- a/test/HelloWorld/MainPage.xaml
+++ b/test/HelloMaui/MainPage.xaml
@@ -1,7 +1,7 @@
+ x:Class="HelloMaui.MainPage">
-
+
maui-appicon-placeholder
diff --git a/test/HelloWorld/Platforms/Windows/App.xaml b/test/HelloMaui/Platforms/Windows/App.xaml
similarity index 74%
rename from test/HelloWorld/Platforms/Windows/App.xaml
rename to test/HelloMaui/Platforms/Windows/App.xaml
index 9505b19..5b15940 100644
--- a/test/HelloWorld/Platforms/Windows/App.xaml
+++ b/test/HelloMaui/Platforms/Windows/App.xaml
@@ -1,9 +1,9 @@
+ xmlns:local="using:HelloMaui.WinUI">
diff --git a/test/HelloWorld/Platforms/Windows/App.xaml.cs b/test/HelloMaui/Platforms/Windows/App.xaml.cs
similarity index 83%
rename from test/HelloWorld/Platforms/Windows/App.xaml.cs
rename to test/HelloMaui/Platforms/Windows/App.xaml.cs
index 7f2488c..5b4f3fc 100644
--- a/test/HelloWorld/Platforms/Windows/App.xaml.cs
+++ b/test/HelloMaui/Platforms/Windows/App.xaml.cs
@@ -1,9 +1,7 @@
-using Microsoft.UI.Xaml;
-
-// To learn more about WinUI, the WinUI project structure,
+// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
-namespace HelloWorld.WinUI;
+namespace HelloMaui.WinUI;
///
/// Provides application-specific behavior to supplement the default Application class.
diff --git a/test/HelloWorld/Platforms/Windows/Package.appxmanifest b/test/HelloMaui/Platforms/Windows/Package.appxmanifest
similarity index 100%
rename from test/HelloWorld/Platforms/Windows/Package.appxmanifest
rename to test/HelloMaui/Platforms/Windows/Package.appxmanifest
diff --git a/test/HelloWorld/Platforms/Windows/app.manifest b/test/HelloMaui/Platforms/Windows/app.manifest
similarity index 90%
rename from test/HelloWorld/Platforms/Windows/app.manifest
rename to test/HelloMaui/Platforms/Windows/app.manifest
index 138fc59..173cb31 100644
--- a/test/HelloWorld/Platforms/Windows/app.manifest
+++ b/test/HelloMaui/Platforms/Windows/app.manifest
@@ -1,6 +1,6 @@
-
+
diff --git a/test/HelloWorld/Platforms/iOS/AppDelegate.cs b/test/HelloMaui/Platforms/iOS/AppDelegate.cs
similarity index 89%
rename from test/HelloWorld/Platforms/iOS/AppDelegate.cs
rename to test/HelloMaui/Platforms/iOS/AppDelegate.cs
index a01c355..7683572 100644
--- a/test/HelloWorld/Platforms/iOS/AppDelegate.cs
+++ b/test/HelloMaui/Platforms/iOS/AppDelegate.cs
@@ -1,6 +1,6 @@
using Foundation;
-namespace HelloWorld;
+namespace HelloMaui;
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
diff --git a/test/HelloWorld/Platforms/iOS/Info.plist b/test/HelloMaui/Platforms/iOS/Info.plist
similarity index 100%
rename from test/HelloWorld/Platforms/iOS/Info.plist
rename to test/HelloMaui/Platforms/iOS/Info.plist
diff --git a/test/HelloWorld/Platforms/MacCatalyst/Program.cs b/test/HelloMaui/Platforms/iOS/Program.cs
similarity index 93%
rename from test/HelloWorld/Platforms/MacCatalyst/Program.cs
rename to test/HelloMaui/Platforms/iOS/Program.cs
index 5559608..d88dd1d 100644
--- a/test/HelloWorld/Platforms/MacCatalyst/Program.cs
+++ b/test/HelloMaui/Platforms/iOS/Program.cs
@@ -1,7 +1,7 @@
using ObjCRuntime;
using UIKit;
-namespace HelloWorld;
+namespace HelloMaui;
public class Program
{
diff --git a/test/HelloWorld/Properties/launchSettings.json b/test/HelloMaui/Properties/launchSettings.json
similarity index 100%
rename from test/HelloWorld/Properties/launchSettings.json
rename to test/HelloMaui/Properties/launchSettings.json
diff --git a/test/HelloWorld/Resources/AppIcon/appicon.svg b/test/HelloMaui/Resources/AppIcon/appicon.svg
similarity index 100%
rename from test/HelloWorld/Resources/AppIcon/appicon.svg
rename to test/HelloMaui/Resources/AppIcon/appicon.svg
diff --git a/test/HelloWorld/Resources/AppIcon/appiconfg.svg b/test/HelloMaui/Resources/AppIcon/appiconfg.svg
similarity index 100%
rename from test/HelloWorld/Resources/AppIcon/appiconfg.svg
rename to test/HelloMaui/Resources/AppIcon/appiconfg.svg
diff --git a/test/HelloWorld/Resources/Fonts/OpenSans-Regular.ttf b/test/HelloMaui/Resources/Fonts/OpenSans-Regular.ttf
similarity index 100%
rename from test/HelloWorld/Resources/Fonts/OpenSans-Regular.ttf
rename to test/HelloMaui/Resources/Fonts/OpenSans-Regular.ttf
diff --git a/test/HelloWorld/Resources/Fonts/OpenSans-Semibold.ttf b/test/HelloMaui/Resources/Fonts/OpenSans-Semibold.ttf
similarity index 100%
rename from test/HelloWorld/Resources/Fonts/OpenSans-Semibold.ttf
rename to test/HelloMaui/Resources/Fonts/OpenSans-Semibold.ttf
diff --git a/test/HelloWorld/Resources/Images/dotnet_bot.svg b/test/HelloMaui/Resources/Images/dotnet_bot.svg
similarity index 100%
rename from test/HelloWorld/Resources/Images/dotnet_bot.svg
rename to test/HelloMaui/Resources/Images/dotnet_bot.svg
diff --git a/test/HelloWorld/Resources/Raw/AboutAssets.txt b/test/HelloMaui/Resources/Raw/AboutAssets.txt
similarity index 100%
rename from test/HelloWorld/Resources/Raw/AboutAssets.txt
rename to test/HelloMaui/Resources/Raw/AboutAssets.txt
diff --git a/test/HelloWorld/Resources/Splash/splash.svg b/test/HelloMaui/Resources/Splash/splash.svg
similarity index 100%
rename from test/HelloWorld/Resources/Splash/splash.svg
rename to test/HelloMaui/Resources/Splash/splash.svg
diff --git a/test/HelloWorld/Resources/Styles/Colors.xaml b/test/HelloMaui/Resources/Styles/Colors.xaml
similarity index 100%
rename from test/HelloWorld/Resources/Styles/Colors.xaml
rename to test/HelloMaui/Resources/Styles/Colors.xaml
diff --git a/test/HelloWorld/Resources/Styles/Styles.xaml b/test/HelloMaui/Resources/Styles/Styles.xaml
similarity index 100%
rename from test/HelloWorld/Resources/Styles/Styles.xaml
rename to test/HelloMaui/Resources/Styles/Styles.xaml
diff --git a/test/HelloWorld/HelloWorld.csproj b/test/HelloWorld/HelloWorld.csproj
index ce6ddb1..b19570d 100644
--- a/test/HelloWorld/HelloWorld.csproj
+++ b/test/HelloWorld/HelloWorld.csproj
@@ -1,61 +1,17 @@
-
- net8.0-android;net8.0-ios;net8.0-maccatalyst
- $(TargetFrameworks);net8.0-windows10.0.19041.0
-
-
- Exe
- HelloWorld
- true
- 8.0.70
- true
- enable
+
+ Exe
+ net8.0
+ enable
+ enable
+ true
+ true
+
+
+
+
+
+
-
- HelloWorld
-
-
- com.companyname.helloworld
- 91c837fe-e892-450c-8365-a45127a53bc3
-
-
- 1.0
- 1
-
- 11.0
- 13.1
- 21.0
- 10.0.17763.0
- 10.0.17763.0
- 6.5
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/test/HelloWorld/Program.cs b/test/HelloWorld/Program.cs
new file mode 100644
index 0000000..933556a
--- /dev/null
+++ b/test/HelloWorld/Program.cs
@@ -0,0 +1,54 @@
+using Aptabase.Core;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+var services = new ServiceCollection();
+
+services.AddLogging(builder =>
+{
+ builder.AddConsole();
+ builder.SetMinimumLevel(LogLevel.Debug);
+});
+
+services.UseAptabase("A-EU-1687478437", new AptabaseOptions
+{
+#if DEBUG
+ IsDebugMode = true,
+#else
+ IsDebugMode = false,
+#endif
+ EnableCrashReporting = false,
+ EnablePersistence = true,
+});
+
+var serviceProvider = services.BuildServiceProvider();
+
+// Get an instance of the Aptabase service
+var aptabaseClient = serviceProvider.GetRequiredService();
+
+
+Console.WriteLine("Hello, World!");
+// Track a sample event
+await aptabaseClient.TrackEvent("app_started");
+
+// OR
+Console.Write("What's your favorite color? ");
+
+var favoriteColor = Console.ReadLine()?.Trim().ToLowerInvariant();
+
+if (!string.IsNullOrWhiteSpace(favoriteColor))
+{
+ await aptabaseClient.TrackEvent("favorite_color", new Dictionary
+ {
+ { "color", favoriteColor }
+ });
+
+ Console.WriteLine("Thanks! Your response was logged.");
+}
+else
+{
+ Console.WriteLine("No input received.");
+}
+
+
+Console.ReadKey();
\ No newline at end of file