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 @@ ![Aptabase](https://raw.githubusercontent.com/aptabase/aptabase-com/main/public/og.png) -# MAUI SDK for Aptabase +# .NET SDK for Aptabase [![NuGet](https://img.shields.io/nuget/v/Aptabase.Maui)](https://www.nuget.org/packages/Aptabase.Maui) [![GitHub](https://img.shields.io/github/license/aptabase/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