diff --git a/System.Application.SteamTools.Client.Desktop.Avalonia.App/Startup.cs b/System.Application.SteamTools.Client.Desktop.Avalonia.App/Startup.cs index 6f6f89cb8..6a0556711 100644 --- a/System.Application.SteamTools.Client.Desktop.Avalonia.App/Startup.cs +++ b/System.Application.SteamTools.Client.Desktop.Avalonia.App/Startup.cs @@ -42,17 +42,9 @@ namespace System.Application.UI // 桌面平台服务 此项放在其他通用业务实现服务之前 services.AddDesktopPlatformService(); - // 本地化服务 - services.AddLocalizationService(); - // 主线程助手类(MainThreadDesktop) services.AddMainThreadPlatformService(); - // 模型视图组 - services.AddViewModelCollectionService(); - services.AddViewModel(); - services.AddViewModel(); - // 业务用户配置文件服务 services.AddConfigFileService(); @@ -73,6 +65,9 @@ namespace System.Application.UI // Steamworks WebApi Service services.AddSteamworksWebApiService(); + + // 应用程序更新服务 + services.AddAppUpdateService(); } } } \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop.Avalonia.App/ViewModels/MainWindowViewModel.cs b/System.Application.SteamTools.Client.Desktop.Avalonia.App/ViewModels/MainWindowViewModel.cs index b2a5b5f4a..9a157bc94 100644 --- a/System.Application.SteamTools.Client.Desktop.Avalonia.App/ViewModels/MainWindowViewModel.cs +++ b/System.Application.SteamTools.Client.Desktop.Avalonia.App/ViewModels/MainWindowViewModel.cs @@ -19,82 +19,6 @@ namespace System.Application.UI.ViewModels SettingsPage = new SettingsPageViewModel(); } - //#region ResStrings - - //string mWelcome = string.Empty; - - //[ResString] - //public string Welcome - //{ - // get => mWelcome; - // set => this.RaiseAndSetIfChanged(ref mWelcome, value); - //} - - //string mCommunityFix = string.Empty; - - //[ResString] - //public string CommunityFix - //{ - // get => mCommunityFix; - // set => this.RaiseAndSetIfChanged(ref mCommunityFix, value); - //} - - //string mUserFastChange = string.Empty; - - //[ResString] - //public string UserFastChange - //{ - // get => mUserFastChange; - // set => this.RaiseAndSetIfChanged(ref mUserFastChange, value); - //} - - //string mSteamApps = string.Empty; - - //[ResString] - //public string SteamApps - //{ - // get => mSteamApps; - // set => this.RaiseAndSetIfChanged(ref mSteamApps, value); - //} - - //string mSteamAuth = string.Empty; - - //[ResString] - //public string SteamAuth - //{ - // get => mSteamAuth; - // set => this.RaiseAndSetIfChanged(ref mSteamAuth, value); - //} - - //string mGameRelated = string.Empty; - - //[ResString] - //public string GameRelated - //{ - // get => mGameRelated; - // set => this.RaiseAndSetIfChanged(ref mGameRelated, value); - //} - - //string mSettings = string.Empty; - - //[ResString] - //public string Settings - //{ - // get => mSettings; - // set => this.RaiseAndSetIfChanged(ref mSettings, value); - //} - - //string mAbout = string.Empty; - - //[ResString] - //public string About - //{ - // get => mAbout; - // set => this.RaiseAndSetIfChanged(ref mAbout, value); - //} - - //#endregion - public SettingsPageViewModel SettingsPage { get; } } } \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop.Avalonia.App/ViewModels/SettingsPageViewModel.cs b/System.Application.SteamTools.Client.Desktop.Avalonia.App/ViewModels/SettingsPageViewModel.cs index d3fa577db..7d4a79f91 100644 --- a/System.Application.SteamTools.Client.Desktop.Avalonia.App/ViewModels/SettingsPageViewModel.cs +++ b/System.Application.SteamTools.Client.Desktop.Avalonia.App/ViewModels/SettingsPageViewModel.cs @@ -1,45 +1,13 @@ using Avalonia.Controls; using ReactiveUI; -using System.Application.Services; using System.Application.UI.Resx; using System.Collections.Generic; using System.Linq; namespace System.Application.UI.ViewModels { - public class SettingsPageViewModel : ViewModelBase, ILocalizationViewModel + public class SettingsPageViewModel : ViewModelBase { - //#region ResStrings - - //string mSettings_General = string.Empty; - - //[ResString] - //public string Settings_General - //{ - // get => mSettings_General; - // set => this.RaiseAndSetIfChanged(ref mSettings_General, value); - //} - - //string mSettings_Auth = string.Empty; - - //[ResString] - //public string Settings_Auth - //{ - // get => mSettings_Auth; - // set => this.RaiseAndSetIfChanged(ref mSettings_Auth, value); - //} - - //string mLanguage = string.Empty; - - //[ResString] - //public string Language - //{ - // get => mLanguage; - // set => this.RaiseAndSetIfChanged(ref mLanguage, value); - //} - - //#endregion - public SettingsPageViewModel() { Languages = R.Languages.Select(Convert).ToList(); diff --git a/System.Application.SteamTools.Client.Desktop.Avalonia/Application/Services/Implementation/AvaloniaDesktopAppUpdateServiceImpl.cs b/System.Application.SteamTools.Client.Desktop.Avalonia/Application/Services/Implementation/AvaloniaDesktopAppUpdateServiceImpl.cs new file mode 100644 index 000000000..7b0bb89f9 --- /dev/null +++ b/System.Application.SteamTools.Client.Desktop.Avalonia/Application/Services/Implementation/AvaloniaDesktopAppUpdateServiceImpl.cs @@ -0,0 +1,25 @@ +using Microsoft.Extensions.Options; +using System.Application.Models; + +namespace System.Application.Services.Implementation +{ + internal sealed class AvaloniaDesktopAppUpdateServiceImpl : DesktopAppUpdateServiceImpl + { + public AvaloniaDesktopAppUpdateServiceImpl(IToast toast, ICloudServiceClient client, IOptions options) : base(toast, client, options) + { + } + + protected override void OnExistNewVersion() + { + //if (WindowService.Current.MainWindow.Dialog($"检测到新版本更新内容:{UpdateInfo.body}{Environment.NewLine}是否立即下载更新?", $"{ProductInfo.Title} | 更新提示") == true) + //{ + // DownloadUpdate(); + //} + } + + protected override void OnExit() + { + // App.Instance.compositeDisposable.Dispose(); + } + } +} \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop.Avalonia/ServiceCollectionExtensions.cs b/System.Application.SteamTools.Client.Desktop.Avalonia/ServiceCollectionExtensions.cs index ad9eab39c..597fb9856 100644 --- a/System.Application.SteamTools.Client.Desktop.Avalonia/ServiceCollectionExtensions.cs +++ b/System.Application.SteamTools.Client.Desktop.Avalonia/ServiceCollectionExtensions.cs @@ -16,5 +16,16 @@ namespace Microsoft.Extensions.DependencyInjection services.AddSingleton(); return services; } + + /// + /// 添加应用程序更新服务 + /// + /// + /// + public static IServiceCollection AddAppUpdateService(this IServiceCollection services) + { + services.AddSingleton(); + return services; + } } } \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop.Mac/System.Application.SteamTools.Client.Desktop.Mac.csproj b/System.Application.SteamTools.Client.Desktop.Mac/System.Application.SteamTools.Client.Desktop.Mac.csproj index 96ead85d2..302244ea7 100644 --- a/System.Application.SteamTools.Client.Desktop.Mac/System.Application.SteamTools.Client.Desktop.Mac.csproj +++ b/System.Application.SteamTools.Client.Desktop.Mac/System.Application.SteamTools.Client.Desktop.Mac.csproj @@ -15,4 +15,10 @@ + + + $(MSBuildExtensionsPath)\..\Common7\IDE\ReferenceAssemblies\Microsoft\Framework\Xamarin.Mac\v2.0\Xamarin.Mac.dll + + + diff --git a/System.Application.SteamTools.Client.Desktop/Properties/SR.Designer.cs b/System.Application.SteamTools.Client.Desktop/Properties/SR.Designer.cs new file mode 100644 index 000000000..6f612b1b4 --- /dev/null +++ b/System.Application.SteamTools.Client.Desktop/Properties/SR.Designer.cs @@ -0,0 +1,86 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace System.Application.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class SR { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal SR() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Application.Properties.SR", typeof(SR).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 echo "Steam++替换升级中..." + ///taskkill /f /im "{0}" + ///xcopy /y /c /h /r /s "{1}\*.*" "{2}" + ///rmdir /s /q "{1}" + ///"{3}" + ///del %0 的本地化字符串。 + /// + internal static string ProgramUpdateCmd_ { + get { + return ResourceManager.GetString("ProgramUpdateCmd_", resourceCulture); + } + } + + /// + /// 查找类似 更新包解压失败,请打开官网手动更新 的本地化字符串。 + /// + internal static string UpdateUnpackFail { + get { + return ResourceManager.GetString("UpdateUnpackFail", resourceCulture); + } + } + } +} diff --git a/System.Application.SteamTools.Client.Desktop/Properties/SR.resx b/System.Application.SteamTools.Client.Desktop/Properties/SR.resx new file mode 100644 index 000000000..c9e16a1e2 --- /dev/null +++ b/System.Application.SteamTools.Client.Desktop/Properties/SR.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + echo "Steam++替换升级中..." +taskkill /f /im "{0}" +xcopy /y /c /h /r /s "{1}\*.*" "{2}" +rmdir /s /q "{1}" +"{3}" +del %0 + 升级程序CMD命令行 + + + 更新包解压失败,请打开官网手动更新 + + \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop/ServiceCollectionExtensions.cs b/System.Application.SteamTools.Client.Desktop/ServiceCollectionExtensions.cs index bc19fa2f0..7382dcd3f 100644 --- a/System.Application.SteamTools.Client.Desktop/ServiceCollectionExtensions.cs +++ b/System.Application.SteamTools.Client.Desktop/ServiceCollectionExtensions.cs @@ -1,53 +1,13 @@ using Microsoft.Extensions.DependencyInjection.Extensions; -using System; using System.Application.Services; using System.Application.Services.Implementation; using System.Application.UI; -using System.Application.UI.ViewModels; // ReSharper disable once CheckNamespace namespace Microsoft.Extensions.DependencyInjection { public static class ServiceCollectionExtensions { - /// - /// 添加本地化、多语言服务 - /// - /// - /// - [Obsolete("Languages")] - public static IServiceCollection AddLocalizationService(this IServiceCollection services) - { - services.AddSingleton(); - return services; - } - - /// - /// 添加视图模型组服务 - /// - /// - /// - [Obsolete("Languages")] - public static IServiceCollection AddViewModelCollectionService(this IServiceCollection services) - { - services.AddSingleton(new ViewModelCollectionServiceImpl(services)); - services.AddSingleton(s => s.GetRequiredService()); - return services; - } - - /// - /// 添加视图模型 - /// - /// - /// - /// - [Obsolete("Languages")] - public static IServiceCollection AddViewModel(this IServiceCollection services) where TViewModel : ViewModelBase - { - services.AddSingleton(); - return services; - } - /// /// 添加桌面端日志实现 /// diff --git a/System.Application.SteamTools.Client.Desktop/Services/ILocalizationService.cs b/System.Application.SteamTools.Client.Desktop/Services/ILocalizationService.cs deleted file mode 100644 index 8854e1e6f..000000000 --- a/System.Application.SteamTools.Client.Desktop/Services/ILocalizationService.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Application.UI.ViewModels; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; - -namespace System.Application.Services -{ - /// - /// 本地化、多语言服务 - /// - [Obsolete("Languages")] - public interface ILocalizationService - { - CultureInfo DefaultCurrentUICulture { get; } - - /// - /// 更改语言 - /// - /// - void ChangeLanguage(string cultureName); - - /// - /// 绑定资源字符串的视图模型组 - /// - ILocalizationViewModel[] ViewModels { get; } - - public static readonly IList> Languages; - - static ILocalizationService() - { - Languages = new Dictionary() - { - { "", "Auto" }, - { "zh-Hans", "Chinese(Simplified)" }, - { "zh-Hant", "Chinese(Traditional)" }, - { "ja-JP", "Japanese" }, - { "ru", "Russian" }, - }.ToList(); - } - } -} \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop/Services/IViewModelCollectionService.cs b/System.Application.SteamTools.Client.Desktop/Services/IViewModelCollectionService.cs deleted file mode 100644 index 48a8639ff..000000000 --- a/System.Application.SteamTools.Client.Desktop/Services/IViewModelCollectionService.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Application.UI.ViewModels; -using System.Collections.Generic; - -namespace System.Application.Services -{ - /// - /// 视图模型组服务 - /// - [Obsolete("Languages")] - public interface IViewModelCollectionService - { - /// - /// 获取所有的视图模型 - /// - IEnumerable ViewModels { get; } - - /// - /// 根据类型过滤获取视图模型 - /// - /// - /// - IEnumerable GetViewModels(); - } -} \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop/Services/Implementation/DesktopAppUpdateServiceImpl.cs b/System.Application.SteamTools.Client.Desktop/Services/Implementation/DesktopAppUpdateServiceImpl.cs new file mode 100644 index 000000000..49c0be143 --- /dev/null +++ b/System.Application.SteamTools.Client.Desktop/Services/Implementation/DesktopAppUpdateServiceImpl.cs @@ -0,0 +1,71 @@ +using Microsoft.Extensions.Options; +using System.Application.Models; +using System.Application.Properties; +using System.Application.UI; +using System.Diagnostics; +using System.IO; +using System.Text; +using static System.Application.Services.IAppUpdateService; + +namespace System.Application.Services.Implementation +{ + public abstract class DesktopAppUpdateServiceImpl : AppUpdateServiceImpl + { + public DesktopAppUpdateServiceImpl(IToast toast, ICloudServiceClient client, IOptions options) : base(toast, client, options) + { + } + + protected override Version OSVersion => Environment.OSVersion.Version; + + protected sealed override void OpenInAppStore() => throw new NotImplementedException(); + + protected abstract void OnExit(); + + protected override void OverwriteUpgrade(string fileName) + { + var dirPath = Path.Combine(AppContext.BaseDirectory, Path.GetFileNameWithoutExtension(fileName)); + + if (Directory.Exists(dirPath)) + { + Directory.Delete(dirPath, true); + } + + CurrentProgressValue = 0; + + void OnReport(float value) => CurrentProgressValue = value; + + if (TarGZipHelper.Unpack(fileName, dirPath, progress: new Progress(OnReport))) + { + ClearProgressValue(MaxProgressValue); + + IOPath.FileTryDelete(fileName); + + OnExit(); + + var updateCommandPath = Path.Combine(AppContext.BaseDirectory, "update.cmd"); + IOPath.FileIfExistsItDelete(updateCommandPath); + + var updateCommand = string.Format( + SR.ProgramUpdateCmd_, + AppHelper.ProgramName, + dirPath, + AppContext.BaseDirectory, + AppHelper.ProgramName); + + File.WriteAllText(updateCommandPath, updateCommand, Encoding.Default); + + using var p = new Process(); + p.StartInfo.FileName = updateCommandPath; + p.StartInfo.UseShellExecute = false; + p.StartInfo.CreateNoWindow = true; // 不显示程序窗口 + p.StartInfo.Verb = "runas"; // 管理员权限运行 + p.Start(); // 启动程序 + } + else + { + toast.Show(SR.UpdateUnpackFail); + ClearProgressValue(MaxProgressValue); + } + } + } +} \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop/Services/Implementation/LocalizationServiceImpl.cs b/System.Application.SteamTools.Client.Desktop/Services/Implementation/LocalizationServiceImpl.cs deleted file mode 100644 index c5728046f..000000000 --- a/System.Application.SteamTools.Client.Desktop/Services/Implementation/LocalizationServiceImpl.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Application.UI.ViewModels; -using System.Globalization; -using System.Linq; - -namespace System.Application.Services.Implementation -{ - [Obsolete("Languages")] - internal sealed class LocalizationServiceImpl : ILocalizationService - { - readonly IViewModelCollectionService vmss; - - public LocalizationServiceImpl(IViewModelCollectionService vmss) - { - this.vmss = vmss; - DefaultCurrentUICulture = CultureInfo.CurrentUICulture; - } - - public CultureInfo DefaultCurrentUICulture { get; } - - ILocalizationViewModel[]? viewModels; - - public ILocalizationViewModel[] ViewModels - { - get - { - if (viewModels == null) - { - viewModels = vmss.GetViewModels().ToArray(); - } - return viewModels; - } - } - - static bool IsMatch(CultureInfo cultureInfo, string cultureName) - { - if (string.IsNullOrWhiteSpace(cultureInfo.Name)) - { - return false; - } - if (cultureInfo.Name == cultureName) - { - return true; - } - else - { - return IsMatch(cultureInfo.Parent, cultureName); - } - } - - public void ChangeLanguage(string cultureName) - { - if (IsMatch(CultureInfo.CurrentUICulture, cultureName)) return; - if (!ViewModels.Any()) return; - void ChangeLanguage() - { - CultureInfo.CurrentUICulture = new CultureInfo(cultureName); - foreach (var viewModel in ViewModels) - { - viewModel.OnChangeLanguage(); - } - } - MainThreadDesktop.BeginInvokeOnMainThread(ChangeLanguage); - } - } -} \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop/Services/Implementation/ViewModelCollectionServiceImpl.cs b/System.Application.SteamTools.Client.Desktop/Services/Implementation/ViewModelCollectionServiceImpl.cs deleted file mode 100644 index a8354de89..000000000 --- a/System.Application.SteamTools.Client.Desktop/Services/Implementation/ViewModelCollectionServiceImpl.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using System.Application.UI.ViewModels; -using System.Collections.Generic; -using System.Linq; - -namespace System.Application.Services.Implementation -{ - [Obsolete("Languages")] - internal sealed class ViewModelCollectionServiceImpl : IViewModelCollectionService - { - readonly IEnumerable services; - - public ViewModelCollectionServiceImpl(IEnumerable services) - { - this.services = services; - } - - public IEnumerable ViewModels => GetViewModels(); - - public IEnumerable GetViewModels() - { - var tType = typeof(T); - var vType = typeof(ViewModelBase); - var tIsvType = tType == vType; - return from s in services - where s.ServiceType == s.ImplementationType && - s.Lifetime == ServiceLifetime.Singleton && - tType.IsAssignableFrom(s.ImplementationType) && - (tIsvType || vType.IsAssignableFrom(s.ImplementationType)) - select (T)DI.Get(s.ServiceType); - } - } -} \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop/System.Application.SteamTools.Client.Desktop.csproj b/System.Application.SteamTools.Client.Desktop/System.Application.SteamTools.Client.Desktop.csproj index 0e36f8b2d..c8d0f3416 100644 --- a/System.Application.SteamTools.Client.Desktop/System.Application.SteamTools.Client.Desktop.csproj +++ b/System.Application.SteamTools.Client.Desktop/System.Application.SteamTools.Client.Desktop.csproj @@ -29,6 +29,11 @@ + + True + True + SR.resx + True True @@ -37,6 +42,10 @@ + + ResXFileCodeGenerator + SR.Designer.cs + PublicResXFileCodeGenerator AppResources.Designer.cs diff --git a/System.Application.SteamTools.Client.Desktop/UI/Resx/ResourceService.cs b/System.Application.SteamTools.Client.Desktop/UI/Resx/ResourceService.cs deleted file mode 100644 index d540430c6..000000000 --- a/System.Application.SteamTools.Client.Desktop/UI/Resx/ResourceService.cs +++ /dev/null @@ -1,90 +0,0 @@ -using ReactiveUI; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; - -namespace System.Application.UI.Resx -{ - /// - /// 提供对多语言资源的访问。 - /// - [Obsolete("Languages")] - public class ResourceService : ReactiveObject - { - // singleton - public static ResourceService Current => new ResourceService(); - - /// - /// 支持的语言简写 - /// - private readonly string[] supportedCultureNames = - { - "zh-Hans", // Resources.resx - "zh-Hant", - "en", - "ja", - "ru", - }; - - /// - /// 支持的语言Steam平台名称 - /// - private readonly Dictionary supportedCultureSteamNames = new Dictionary - { - { "zh-Hans","schinese"}, - { "zh-Hant","tchinese"}, - { "en","english"}, - { "ko","koreana"}, - { "ja","japanese"}, - { "ru","russian"}, - }; - - /// - /// 获取多语种资源。 - /// - public System.Application.UI.Resx.AppResources Resources { get; } - - /// - /// 获得受支持的文化。 - /// - public IReadOnlyCollection SupportedCultures { get; } - - private ResourceService() - { - this.Resources = new System.Application.UI.Resx.AppResources(); -#pragma warning disable CS8619 // 值中的引用类型的为 Null 性与目标类型不匹配。 - this.SupportedCultures = this.supportedCultureNames - .Select(x => - { - try - { - return CultureInfo.GetCultureInfo(x); - } - catch (CultureNotFoundException) - { - return null; - } - }) - .Where(x => x != null) - .ToList(); -#pragma warning restore CS8619 // 值中的引用类型的为 Null 性与目标类型不匹配。 - System.Application.UI.Resx.AppResources.Culture = this.SupportedCultures.First(); - } - - /// - /// 使用指定的区域性名称更改资源的区域性。 - /// - /// - public void ChangeCulture(string name) - { - System.Application.UI.Resx.AppResources.Culture = this.SupportedCultures.SingleOrDefault(x => x.Name == name); - //GeneralSettings.Culture.Value = SteamTools.Properties.Resources.Culture?.Name; - this.RaisePropertyChanged(nameof(Resources)); - } - - public string GetCurrentCultureSteamLanguageName() - { - return UI.Resx.AppResources.Culture == null ? supportedCultureSteamNames.First().Value : supportedCultureSteamNames[UI.Resx.AppResources.Culture.Name]; - } - } -} \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop/UI/ViewModels/ILocalizationViewModel.cs b/System.Application.SteamTools.Client.Desktop/UI/ViewModels/ILocalizationViewModel.cs deleted file mode 100644 index b93266ba3..000000000 --- a/System.Application.SteamTools.Client.Desktop/UI/ViewModels/ILocalizationViewModel.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Application.UI.Resx; -using System.Linq; -using System.Reflection; - -namespace System.Application.UI.ViewModels -{ - /// - /// 本地化、多语言视图模型 - /// - [Obsolete("Languages")] - public interface ILocalizationViewModel - { - public void OnChangeLanguage() - { - var properties = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); - var query = from p in properties - let attr = p.GetCustomAttribute() - where p.PropertyType == typeof(string) && attr != null - select (p, attr.ResId); - foreach (var (p, ResId) in query) - { - var resId = ResId ?? p.Name; - var value = AppResources.ResourceManager.GetString(resId); - p.SetValue(this, value); - } - } - } -} \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop/UI/ViewModels/ResStringAttribute.cs b/System.Application.SteamTools.Client.Desktop/UI/ViewModels/ResStringAttribute.cs deleted file mode 100644 index 2332b5540..000000000 --- a/System.Application.SteamTools.Client.Desktop/UI/ViewModels/ResStringAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace System.Application.UI.ViewModels -{ - [Obsolete("Languages")] - [AttributeUsage(AttributeTargets.Property)] - public sealed class ResStringAttribute : Attribute - { - public string? ResId { get; } - - public ResStringAttribute(string? resId = null) - { - ResId = resId; - } - } -} \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop/UI/ViewModels/ViewModelBase.cs b/System.Application.SteamTools.Client.Desktop/UI/ViewModels/ViewModelBase.cs index 83eb0f9d3..372e04b2c 100644 --- a/System.Application.SteamTools.Client.Desktop/UI/ViewModels/ViewModelBase.cs +++ b/System.Application.SteamTools.Client.Desktop/UI/ViewModels/ViewModelBase.cs @@ -3,25 +3,19 @@ using System.Application.Mvvm; namespace System.Application.UI.ViewModels { - public class ViewModelBase : ReactiveObject, IDisposable, ILocalizationViewModel + public class ViewModelBase : ReactiveObject, IDisposable { [NonSerialized] CompositeDisposable? _compositeDisposable; [NonSerialized] bool _disposed; - public ViewModelBase() - { - ILocalizationViewModel localization = this; - localization.OnChangeLanguage(); - } - /// /// 是否在设计器的上下文中运行 /// public static bool IsInDesignMode { get; set; } /// - /// 释放该实例使用的所有资源 + /// 释放该实例使用的所有资源 /// public void Dispose() { diff --git a/System.Application.SteamTools.Client/Properties/SR.Designer.cs b/System.Application.SteamTools.Client/Properties/SR.Designer.cs index 98aeae45b..f725473b0 100644 --- a/System.Application.SteamTools.Client/Properties/SR.Designer.cs +++ b/System.Application.SteamTools.Client/Properties/SR.Designer.cs @@ -78,6 +78,24 @@ namespace System.Application.Properties { } } + /// + /// 查找类似 下载更新包失败,请稍后重试 的本地化字符串。 + /// + internal static string DownloadUpdateFail { + get { + return ResourceManager.GetString("DownloadUpdateFail", resourceCulture); + } + } + + /// + /// 查找类似 当前已是最新版本 的本地化字符串。 + /// + internal static string IsExistUpdateFalse { + get { + return ResourceManager.GetString("IsExistUpdateFalse", resourceCulture); + } + } + /// /// 查找类似 新版本更新 的本地化字符串。 /// @@ -114,5 +132,14 @@ namespace System.Application.Properties { return ((byte[])(obj)); } } + + /// + /// 查找类似 文件校验失败,请打开官网手动更新 的本地化字符串。 + /// + internal static string UpdatePackVerificationFail { + get { + return ResourceManager.GetString("UpdatePackVerificationFail", resourceCulture); + } + } } } diff --git a/System.Application.SteamTools.Client/Properties/SR.resx b/System.Application.SteamTools.Client/Properties/SR.resx index 8248a9308..b961a55b9 100644 --- a/System.Application.SteamTools.Client/Properties/SR.resx +++ b/System.Application.SteamTools.Client/Properties/SR.resx @@ -123,6 +123,12 @@ 正在下载:{0}% + + 下载更新包失败,请稍后重试 + + + 当前已是最新版本 + 新版本更新 @@ -136,4 +142,7 @@ ..\..\Resources\OpenSourceLibraryList.mpo;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 文件校验失败,请打开官网手动更新 + \ No newline at end of file diff --git a/System.Application.SteamTools.Client/Services/IAppUpdateService.cs b/System.Application.SteamTools.Client/Services/IAppUpdateService.cs new file mode 100644 index 000000000..339eff9fa --- /dev/null +++ b/System.Application.SteamTools.Client/Services/IAppUpdateService.cs @@ -0,0 +1,66 @@ +using System.Application.Models; + +namespace System.Application.Services +{ + /// + /// 应用程序更新服务 + /// + public interface IAppUpdateService + { + public static IAppUpdateService Instance => DI.Get(); + + public const float MaxProgressValue = 100f; + + /// + /// 当前进度值 + /// + float CurrentProgressValue { get; } + + /// + /// 总进度值 + /// + float TotalProgressValue { get; } + + /// + /// 是否支持服务端分发,如果返回 将不能使用 DownloadAsync 与 OverwriteUpgradeAsync + /// 对于 iOS 平台,必须使用 App Store 分发 + /// + bool IsSupportedServerDistribution { get; } + + /// + /// 是否有更新 + /// + bool IsExistUpdate { get; } + + /// + /// 新版本信息 + /// + AppVersionDTO? NewVersionInfo { get; } + + /// + /// 检查更新,返回新版本信息 + /// + /// + void CheckUpdate(); + } + +#if DEBUG + + [Obsolete("use IAppUpdateService.Instance", true)] + public class AutoUpdateService + { + [Obsolete("use IAppUpdateService.Instance.CheckAsync", true)] + public void CheckUpdate() => throw new NotImplementedException(); + + [Obsolete("use IAppUpdateService.Instance.DownloadAsync", true)] + public void DownloadUpdate() => throw new NotImplementedException(); + + [Obsolete("use IAppUpdateService.Instance.OverwriteUpgradeAsync", true)] + public void OverwriteUpgrade(string zipFile) => throw new NotImplementedException(); + + [Obsolete("use IAppUpdateService.Instance.NewVersionInfo", true)] + public object? UpdateInfo { get; set; } + } + +#endif +} \ No newline at end of file diff --git a/System.Application.SteamTools.Client/Services/Implementation/AppUpdateServiceImpl.cs b/System.Application.SteamTools.Client/Services/Implementation/AppUpdateServiceImpl.cs new file mode 100644 index 000000000..3a282c3fa --- /dev/null +++ b/System.Application.SteamTools.Client/Services/Implementation/AppUpdateServiceImpl.cs @@ -0,0 +1,246 @@ +using Microsoft.Extensions.Options; +using System.Application.Models; +using System.Application.Properties; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using static System.Application.Services.IAppUpdateService; + +namespace System.Application.Services.Implementation +{ + public abstract class AppUpdateServiceImpl : IAppUpdateService + { + protected readonly ICloudServiceClient client; + protected readonly AppSettings settings; + protected readonly IToast toast; + + public AppUpdateServiceImpl( + IToast toast, + ICloudServiceClient client, + IOptions options) + { + this.toast = toast; + this.client = client; + settings = options.Value; + SupportedAbis = GetSupportedAbis(); + } + + protected virtual ArchitectureFlags GetSupportedAbis() + { + var architecture = RuntimeInformation.OSArchitecture; + return architecture switch + { + Architecture.Arm => ArchitectureFlags.Arm, + Architecture.Arm64 => ArchitectureFlags.Arm64 | ArchitectureFlags.Arm, + Architecture.X64 => ArchitectureFlags.X64 | ArchitectureFlags.X86, + Architecture.X86 => ArchitectureFlags.X86, + _ => throw new ArgumentOutOfRangeException(nameof(architecture), architecture, null), + }; + } + + protected ArchitectureFlags SupportedAbis { get; } + + protected abstract Version OSVersion { get; } + + public bool IsSupportedServerDistribution + { + get + { + if (DI.Platform == Platform.Apple && DI.DeviceIdiom != DeviceIdiom.Desktop) + { + return false; + } + return true; + } + } + + public bool IsExistUpdate { get; protected set; } + + public AppVersionDTO? NewVersionInfo { get; protected set; } + + /// + /// 当存在新的版本时,重写此方法实现弹窗提示用户 + /// + protected abstract void OnExistNewVersion(); + + static bool isCheckUpdateing; + + public async void CheckUpdate() + { + if (isCheckUpdateing) return; + isCheckUpdateing = true; + + var id = settings.AppVersion; + var platform = DI.Platform; + var deviceIdiom = DI.DeviceIdiom; + if (deviceIdiom == DeviceIdiom.Tablet && platform == Platform.Apple) + { + deviceIdiom = DeviceIdiom.Phone; + } + var supportedAbis = SupportedAbis; + var osVersion = OSVersion; + var rsp = await client.Version.CheckUpdate(id, + platform, deviceIdiom, + supportedAbis, + osVersion); + if (rsp.IsSuccess) + { + if (!rsp.Content.HasValue()) + { + IsExistUpdate = false; + toast.Show(SR.IsExistUpdateFalse); + } + else + { + IsExistUpdate = true; + NewVersionInfo = rsp.Content; + OnExistNewVersion(); + } + } + else + { + // ServerApiClient 错误已由全局处理 + } + + isCheckUpdateing = false; + } + + /// + /// 升级包存放文件夹名称 + /// + const string PackDirName = "-Pack"; + + const string FileExDownloadCache = ".download_cache"; + + /// + /// 根据新版本信息获取升级包文件名 + /// + /// + /// + static string GetPackFileName(AppVersionDTO m) + => $"{m.Version}@{Hashs.String.Crc32(m.Id.ToStringN())}.{FileEx.TAR_GZ}"; + + /// + /// 获取存放升级包缓存文件夹的目录 + /// + /// 是否需要清理之前的缓存 + /// + static string GetPackCacheDirPath(bool clear) + { + var dirPath = Path.Combine(IOPath.CacheDirectory, PackDirName); + if (!Directory.Exists(dirPath)) + { + Directory.CreateDirectory(dirPath); + } + else if (clear) + { + var files = Directory.GetFiles("*" + FileExDownloadCache); + foreach (var item in files) + { + IOPath.FileTryDelete(item); + } + } + return dirPath; + } + + static bool isDownloading; + + /// + /// 是否支持断点续传 + /// + const bool isSupportedResume = false; + + public float CurrentProgressValue { get; protected set; } + + public float TotalProgressValue { get; protected set; } + + protected void ClearProgressValue(float value = 0f) + { + CurrentProgressValue = value; + TotalProgressValue = value; + } + + bool UpdatePackVerification(string filePath, string sha256) + { + var sha256_ = Hashs.String.SHA256(File.OpenRead(filePath)); // 改为带进度的哈希计算 + if (TotalProgressValue < MaxProgressValue) TotalProgressValue += 10; + return string.Equals(sha256_, sha256, StringComparison.OrdinalIgnoreCase); + } + + /// + /// 下载更新包文件到本地缓存 + /// + protected async void DownloadUpdate() + { + if (isDownloading) return; + + if (!IsSupportedServerDistribution) throw new PlatformNotSupportedException(); + + isDownloading = true; + + ClearProgressValue(); + + var newVersionInfo = NewVersionInfo; + if (newVersionInfo.HasValue() && newVersionInfo?.DownloadLink != null && newVersionInfo.SHA256 != null) + { + var packFileName = GetPackFileName(newVersionInfo); + var packFilePath = Path.Combine(GetPackCacheDirPath(!isSupportedResume), packFileName); + if (File.Exists(packFilePath)) // 存在安装包文件 + { + if (UpdatePackVerification(packFilePath, newVersionInfo.SHA256)) // (已有文件)哈希验证成功,进行覆盖安装 + { + OverwriteUpgrade(packFilePath); + goto end; + } + else // 验证文件失败,删除源文件,将会重新下载 --提示用户,然后结束本次逻辑 + { + File.Delete(packFilePath); + } + } + + var cacheFileName = packFileName + FileExDownloadCache; + + CurrentProgressValue = 0; + + var rsp = await client.Download(newVersionInfo.DownloadLink, cacheFileName, new Progress(OnReport)); + + void OnReport(float value) => CurrentProgressValue = value; + + if (rsp.IsSuccess) + { + File.Move(cacheFileName, packFileName); + + if (UpdatePackVerification(packFilePath, newVersionInfo.SHA256)) // (下载文件)哈希验证成功,进行覆盖安装 + { + OverwriteUpgrade(packFileName); + OnReport(MaxProgressValue); + if (TotalProgressValue < 50) TotalProgressValue = 50; + } + else + { + toast.Show(SR.UpdatePackVerificationFail); + ClearProgressValue(MaxProgressValue); + } + } + else // 下载失败,进度条填满,可能服务器崩了 + { + toast.Show(SR.DownloadUpdateFail); + ClearProgressValue(MaxProgressValue); + } + } + + end: isDownloading = false; + } + + /// + /// 覆盖更新,根据更新包解压覆盖文件 + /// Android:打开apk包即可 + /// + protected abstract void OverwriteUpgrade(string fileName); + + /// + /// 在应用商店中打开 + /// + protected abstract void OpenInAppStore(); + } +} \ No newline at end of file diff --git a/System.Application.SteamTools.Services.CloudService.Models/Models/ApiResponseCode.cs b/System.Application.SteamTools.Services.CloudService.Models/Models/ApiResponseCode.cs index 1bed3322f..c3b034cc6 100644 --- a/System.Application.SteamTools.Services.CloudService.Models/Models/ApiResponseCode.cs +++ b/System.Application.SteamTools.Services.CloudService.Models/Models/ApiResponseCode.cs @@ -17,6 +17,8 @@ namespace System.Application.Models Unauthorized = HttpStatusCode.Unauthorized, + NotFound = HttpStatusCode.NotFound, + #endregion #region CustomCode 自定义响应码,1000~? diff --git a/System.Application.SteamTools.Services.CloudService.Models/Models/AppVersionInfoDTO.cs b/System.Application.SteamTools.Services.CloudService.Models/Models/AppVersionDTO.cs similarity index 89% rename from System.Application.SteamTools.Services.CloudService.Models/Models/AppVersionInfoDTO.cs rename to System.Application.SteamTools.Services.CloudService.Models/Models/AppVersionDTO.cs index 42ffeb781..ab081446b 100644 --- a/System.Application.SteamTools.Services.CloudService.Models/Models/AppVersionInfoDTO.cs +++ b/System.Application.SteamTools.Services.CloudService.Models/Models/AppVersionDTO.cs @@ -5,7 +5,7 @@ using MPObject = MessagePack.MessagePackObjectAttribute; namespace System.Application.Models { [MPObject] - public class AppVersionInfoDTO : IEntity, IExplicitHasValue + public class AppVersionDTO : IEntity, IExplicitHasValue { [MPKey(0)] public Guid Id { get; set; } @@ -37,6 +37,7 @@ namespace System.Application.Models bool IExplicitHasValue.ExplicitHasValue() { return !string.IsNullOrWhiteSpace(Version) && + !string.IsNullOrWhiteSpace(SHA256) && !string.IsNullOrWhiteSpace(Description) && !string.IsNullOrWhiteSpace(DownloadLink); } diff --git a/System.Application.SteamTools.Services.CloudService/Services/CloudService/Clients/Abstractions/IVersionClient.cs b/System.Application.SteamTools.Services.CloudService/Services/CloudService/Clients/Abstractions/IVersionClient.cs index 9b61a609e..2f324914c 100644 --- a/System.Application.SteamTools.Services.CloudService/Services/CloudService/Clients/Abstractions/IVersionClient.cs +++ b/System.Application.SteamTools.Services.CloudService/Services/CloudService/Clients/Abstractions/IVersionClient.cs @@ -8,18 +8,16 @@ namespace System.Application.Services.CloudService.Clients.Abstractions /// /// 检查更新 /// - /// - /// - /// - /// - /// - /// + /// 当前应用版本Id + /// (单选)当前设备所属平台 + /// (单选)当前设备所属类型 + /// (多选)当前设备支持的CPU架构 + /// 当前设备运行的操作系统版本号 /// - Task> CheckUpdate(Guid id, + Task> CheckUpdate(Guid id, Platform platform, DeviceIdiom deviceIdiom, - CPUABI.Value supportedAbis, - int major, - int minor); + ArchitectureFlags supportedAbis, + Version osVersion); } } \ No newline at end of file diff --git a/System.Application.SteamTools.Services.CloudService/Services/CloudService/Clients/VersionClient.cs b/System.Application.SteamTools.Services.CloudService/Services/CloudService/Clients/VersionClient.cs index d6cf9df5c..a46dcf8a8 100644 --- a/System.Application.SteamTools.Services.CloudService/Services/CloudService/Clients/VersionClient.cs +++ b/System.Application.SteamTools.Services.CloudService/Services/CloudService/Clients/VersionClient.cs @@ -11,17 +11,17 @@ namespace System.Application.Services.CloudService.Clients { } - public Task> CheckUpdate( + public Task> CheckUpdate( Guid id, Platform platform, DeviceIdiom deviceIdiom, - CPUABI.Value supportedAbis, - int major, - int minor) + ArchitectureFlags supportedAbis, + Version osVersion) { var url = - $"/version/{id}/{(int)platform}/{(int)deviceIdiom}/{(int)supportedAbis}/{major}/{minor}"; - return conn.SendAsync( + $"api/version/checkupdate/{id}/{(int)platform}/{(int)deviceIdiom}" + + $"/{(int)supportedAbis}/{osVersion.Major}/{osVersion.Minor}"; + return conn.SendAsync( method: HttpMethod.Get, requestUri: url, cancellationToken: default, diff --git a/System.Application.SteamTools.Services.CloudService/Services/CloudService/CloudServiceClientBase.cs b/System.Application.SteamTools.Services.CloudService/Services/CloudService/CloudServiceClientBase.cs index 81e6d2067..6bd43a98a 100644 --- a/System.Application.SteamTools.Services.CloudService/Services/CloudService/CloudServiceClientBase.cs +++ b/System.Application.SteamTools.Services.CloudService/Services/CloudService/CloudServiceClientBase.cs @@ -5,6 +5,7 @@ using System.Application.Models; using System.Application.Services.CloudService.Clients; using System.Application.Services.CloudService.Clients.Abstractions; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; namespace System.Application.Services.CloudService @@ -67,5 +68,9 @@ namespace System.Application.Services.CloudService public abstract void ShowResponseErrorMessage(string message); HttpClient IApiConnectionPlatformHelper.CreateClient() => CreateClient(); + + Task ICloudServiceClient.Download(string requestUri, + string cacheFilePath, IProgress progress, CancellationToken cancellationToken) + => connection.DownloadAsync(cancellationToken, requestUri, cacheFilePath, progress); } } \ No newline at end of file diff --git a/System.Application.SteamTools.Services.CloudService/Services/ICloudServiceClient.cs b/System.Application.SteamTools.Services.CloudService/Services/ICloudServiceClient.cs index 61532c4ff..a740e4210 100644 --- a/System.Application.SteamTools.Services.CloudService/Services/ICloudServiceClient.cs +++ b/System.Application.SteamTools.Services.CloudService/Services/ICloudServiceClient.cs @@ -1,4 +1,8 @@ -using System.Application.Services.CloudService.Clients.Abstractions; +using System.Application.Models; +using System.Application.Services.CloudService; +using System.Application.Services.CloudService.Clients.Abstractions; +using System.Threading; +using System.Threading.Tasks; namespace System.Application.Services { @@ -10,6 +14,9 @@ namespace System.Application.Services IVersionClient Version { get; } + /// + Task Download(string requestUri, string cacheFilePath, IProgress progress, CancellationToken cancellationToken = default); + public static ICloudServiceClient Instance => DI.Get(); } } \ No newline at end of file diff --git a/System.Common.CoreLib/ArchitectureFlags.cs b/System.Common.CoreLib/ArchitectureFlags.cs new file mode 100644 index 000000000..e66297147 --- /dev/null +++ b/System.Common.CoreLib/ArchitectureFlags.cs @@ -0,0 +1,35 @@ +using System.Runtime.InteropServices; + +namespace System +{ + /// + /// CPU 构架,例如 x86、Arm 等 + /// 例如不同的 Android 设备使用不同的 CPU,而不同的 CPU 支持不同的指令集。 + /// CPU 与指令集的每种组合都有专属的应用二进制接口 (ABI)。 + /// https://developer.android.google.cn/ndk/guides/abis.html + /// 在比较版本支持的值与设备支持的值中的交集中,值越大的,优先选取 + /// + [Flags] + public enum ArchitectureFlags + { + /// + /// https://developer.android.google.cn/ndk/guides/abis.html#x86 + /// + X86 = 16, + + /// + /// https://developer.android.google.cn/ndk/guides/abis.html#v7a + /// + Arm = 32, + + /// + /// https://developer.android.google.cn/ndk/guides/abis.html#86-64 + /// + X64 = 128, + + /// + /// https://developer.android.google.cn/ndk/guides/abis.html#arm64-v8a + /// + Arm64 = 256, + } +} \ No newline at end of file diff --git a/System.Common.CoreLib/CPUABI.cs b/System.Common.CoreLib/CPUABI.cs deleted file mode 100644 index eb7bf595f..000000000 --- a/System.Common.CoreLib/CPUABI.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace System -{ - /// - /// CPU 构架,例如 x86、arm 等 - /// 例如不同的 Android 设备使用不同的 CPU,而不同的 CPU 支持不同的指令集。 - /// CPU 与指令集的每种组合都有专属的应用二进制接口 (ABI)。 - /// https://developer.android.google.cn/ndk/guides/abis.html - /// 在比较版本支持的值与设备支持的值中的交集中,值越大的,优先选取。 - /// - public static class CPUABI - { - /// - [Flags] - public enum Value - { - [Obsolete("Deprecated")] - ARM = 4, - - [Obsolete("Deprecated")] - MIPS = 8, - - /// - /// https://developer.android.google.cn/ndk/guides/abis.html#x86 - /// - X86 = 16, - - /// - /// https://developer.android.google.cn/ndk/guides/abis.html#v7a - /// - ARM32 = 32, - - [Obsolete("Deprecated")] - MIPS64 = 64, - - /// - /// https://developer.android.google.cn/ndk/guides/abis.html#86-64 - /// - X64 = 128, - - /// - /// https://developer.android.google.cn/ndk/guides/abis.html#arm64-v8a - /// - ARM64 = 256, - } - - static readonly IReadOnlyDictionary mapping = new Dictionary - { - { "armeabi-v7a", Value.ARM32 }, - { "arm64-v8a", Value.ARM64 }, - { "x86", Value.X86 }, - { "x86_64", Value.X64 }, -#pragma warning disable CS0618 // 类型或成员已过时 - { "armeabi", Value.ARM }, - { "mips", Value.MIPS }, - { "mips64", Value.MIPS64 }, -#pragma warning restore CS0618 // 类型或成员已过时 - }; - - static readonly Lazy> mapping_reverse - = new Lazy>(() => mapping.ReverseKeyValue()); - - /// - /// 将多个 AndroidABI 字符串 转换为 枚举 - /// - /// - /// - public static Value Convert(IEnumerable? abis) - { - Value value = 0; - if (abis != null) - { - foreach (var abi in abis) - { - if (!string.IsNullOrWhiteSpace(abi) && mapping.ContainsKey(abi)) - { - value |= mapping[abi]; - } - } - } - return value; - } - - /// - /// 将多个 AndroidABI 字符串 转换为 枚举 - /// - /// - /// - public static Value Convert(params string[] abis) => Convert(abis.AsEnumerable()); - - /// - /// 将 AndroidABI 枚举(单值) 转换为字符串 - /// - /// - /// - public static string ToStringSingle(this Value value) - { - var pairs = mapping_reverse.Value; - if (pairs.ContainsKey(value)) return pairs[value]; - return value.ToString(); - } - - /// - /// 将 AndroidABI 枚举(多值) 转换为字符串 - /// - /// - /// - /// - public static string ToStringMultiple(this Value value, string separator = ", ") - { - var enumType = typeof(Value); - if (Enum.IsDefined(enumType, value)) return ToStringSingle(value); - var all_values = (Value[])Enum.GetValues(enumType); - var values = from x in all_values let has = value.HasFlag(x) where has select ToStringSingle(x); - return string.Join(separator, value); - } - } -} \ No newline at end of file