// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Reflection; using System.Text.Json; using Microsoft.Maui.ApplicationModel; namespace Microsoft.Maui.Platform.Linux.Services; /// /// Linux version tracking implementation. /// public class VersionTrackingService : IVersionTracking { private const string VersionTrackingFile = ".maui-version-tracking.json"; private readonly string _trackingFilePath; private VersionTrackingData _data; private bool _isInitialized; public VersionTrackingService() { _trackingFilePath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), VersionTrackingFile); _data = new VersionTrackingData(); } private void EnsureInitialized() { if (_isInitialized) return; _isInitialized = true; LoadTrackingData(); UpdateTrackingData(); } private void LoadTrackingData() { try { if (File.Exists(_trackingFilePath)) { var json = File.ReadAllText(_trackingFilePath); _data = JsonSerializer.Deserialize(json) ?? new VersionTrackingData(); } } catch { _data = new VersionTrackingData(); } } private void UpdateTrackingData() { var currentVersion = CurrentVersion; var currentBuild = CurrentBuild; // Check if this is a new version if (_data.PreviousVersion != currentVersion || _data.PreviousBuild != currentBuild) { // Store previous version info if (!string.IsNullOrEmpty(_data.CurrentVersion)) { _data.PreviousVersion = _data.CurrentVersion; _data.PreviousBuild = _data.CurrentBuild; } _data.CurrentVersion = currentVersion; _data.CurrentBuild = currentBuild; // Add to version history if (!_data.VersionHistory.Contains(currentVersion)) { _data.VersionHistory.Add(currentVersion); } // Add to build history if (!_data.BuildHistory.Contains(currentBuild)) { _data.BuildHistory.Add(currentBuild); } } // Track first launch if (_data.FirstInstalledVersion == null) { _data.FirstInstalledVersion = currentVersion; _data.FirstInstalledBuild = currentBuild; _data.IsFirstLaunchEver = true; } else { _data.IsFirstLaunchEver = false; } // Check if first launch for current version _data.IsFirstLaunchForCurrentVersion = _data.PreviousVersion != currentVersion; _data.IsFirstLaunchForCurrentBuild = _data.PreviousBuild != currentBuild; SaveTrackingData(); } private void SaveTrackingData() { try { var directory = Path.GetDirectoryName(_trackingFilePath); if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) { Directory.CreateDirectory(directory); } var json = JsonSerializer.Serialize(_data, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(_trackingFilePath, json); } catch { // Silently fail if we can't save } } public bool IsFirstLaunchEver { get { EnsureInitialized(); return _data.IsFirstLaunchEver; } } public bool IsFirstLaunchForCurrentVersion { get { EnsureInitialized(); return _data.IsFirstLaunchForCurrentVersion; } } public bool IsFirstLaunchForCurrentBuild { get { EnsureInitialized(); return _data.IsFirstLaunchForCurrentBuild; } } public string CurrentVersion => GetAssemblyVersion(); public string CurrentBuild => GetAssemblyBuild(); public string? PreviousVersion { get { EnsureInitialized(); return _data.PreviousVersion; } } public string? PreviousBuild { get { EnsureInitialized(); return _data.PreviousBuild; } } public string? FirstInstalledVersion { get { EnsureInitialized(); return _data.FirstInstalledVersion; } } public string? FirstInstalledBuild { get { EnsureInitialized(); return _data.FirstInstalledBuild; } } public IReadOnlyList VersionHistory { get { EnsureInitialized(); return _data.VersionHistory.AsReadOnly(); } } public IReadOnlyList BuildHistory { get { EnsureInitialized(); return _data.BuildHistory.AsReadOnly(); } } public bool IsFirstLaunchForVersion(string version) { EnsureInitialized(); return !_data.VersionHistory.Contains(version); } public bool IsFirstLaunchForBuild(string build) { EnsureInitialized(); return !_data.BuildHistory.Contains(build); } public void Track() { EnsureInitialized(); } private static string GetAssemblyVersion() { var assembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly(); var version = assembly.GetName().Version; return version != null ? $"{version.Major}.{version.Minor}.{version.Build}" : "1.0.0"; } private static string GetAssemblyBuild() { var assembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly(); var version = assembly.GetName().Version; return version?.Revision.ToString() ?? "0"; } private class VersionTrackingData { public string? CurrentVersion { get; set; } public string? CurrentBuild { get; set; } public string? PreviousVersion { get; set; } public string? PreviousBuild { get; set; } public string? FirstInstalledVersion { get; set; } public string? FirstInstalledBuild { get; set; } public List VersionHistory { get; set; } = new(); public List BuildHistory { get; set; } = new(); public bool IsFirstLaunchEver { get; set; } public bool IsFirstLaunchForCurrentVersion { get; set; } public bool IsFirstLaunchForCurrentBuild { get; set; } } }