Compare commits

..
Author SHA1 Message Date
Aigio Liu 82814649e3 misc 2022-10-13 14:36:22 +08:00
Aigio Liu 978411b677 restore sms 2022-09-22 19:10:30 +08:00
Aigio Liu 166f8732dc Misc 2022-09-22 11:17:58 +08:00
Aigio Liu 8c2a7068b9 fix Android reverseProxyService.ProxyIp & Misc 2022-08-31 11:16:04 +08:00
Aigio Liu b832f3c927 fix maui-android projects & update AndroidX packages 2022-08-27 20:00:21 +08:00
Aigio Liu c367cac992 fix maui csproj errors 2022-08-27 13:09:06 +08:00
dependabot[bot]andAigio L f78271c2f5 Bump AspNet.Security.OAuth.QQ from 6.0.6 to 6.0.9 (#1943)
Bumps [AspNet.Security.OAuth.QQ](https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers) from 6.0.6 to 6.0.9.
- [Release notes](https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers/releases)
- [Commits](https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers/compare/6.0.6...6.0.9)

---
updated-dependencies:
- dependency-name: AspNet.Security.OAuth.QQ
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aigio L <24450093+AigioL@users.noreply.github.com>
2022-08-26 15:00:42 +08:00
dependabot[bot] f2bdc19d74 Bump AspNet.Security.OAuth.Apple from 6.0.6 to 6.0.9 (#1942)
Bumps [AspNet.Security.OAuth.Apple](https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers) from 6.0.6 to 6.0.9.
- [Release notes](https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers/releases)
- [Commits](https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers/compare/6.0.6...6.0.9)

---
updated-dependencies:
- dependency-name: AspNet.Security.OAuth.Apple
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-26 14:59:54 +08:00
dependabot[bot] 0fde0eccba Bump Moq from 4.18.1 to 4.18.2 (#1817)
Bumps [Moq](https://github.com/moq/moq4) from 4.18.1 to 4.18.2.
- [Release notes](https://github.com/moq/moq4/releases)
- [Changelog](https://github.com/moq/moq4/blob/main/CHANGELOG.md)
- [Commits](https://github.com/moq/moq4/compare/v4.18.1...v4.18.2)

---
updated-dependencies:
- dependency-name: Moq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-26 14:59:30 +08:00
dependabot[bot]andAigio L 5c55fa97e7 Bump NLog.Web.AspNetCore from 5.1.0 to 5.1.1 (#1927)
Bumps [NLog.Web.AspNetCore](https://github.com/NLog/NLog.Web) from 5.1.0 to 5.1.1.
- [Release notes](https://github.com/NLog/NLog.Web/releases)
- [Changelog](https://github.com/NLog/NLog.Web/blob/master/CHANGELOG.MD)
- [Commits](https://github.com/NLog/NLog.Web/compare/v5.1.0...v5.1.1)

---
updated-dependencies:
- dependency-name: NLog.Web.AspNetCore
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aigio L <24450093+AigioL@users.noreply.github.com>
2022-08-26 14:59:01 +08:00
dependabot[bot] 323c7eb6c3 Bump DynamicData from 7.9.7 to 7.9.14 (#1925)
Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 7.9.7 to 7.9.14.
- [Release notes](https://github.com/reactiveui/DynamicData/releases)
- [Changelog](https://github.com/reactivemarbles/DynamicData/blob/main/ReleaseNotes.md)
- [Commits](https://github.com/reactiveui/DynamicData/compare/7.9.7...7.9.14)

---
updated-dependencies:
- dependency-name: DynamicData
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-26 14:58:22 +08:00
dependabot[bot] 42dea46260 Bump Z.EntityFramework.Plus.EFCore from 6.14.3 to 6.14.4 (#1915)
Bumps [Z.EntityFramework.Plus.EFCore](https://github.com/zzzprojects/EntityFramework-Plus) from 6.14.3 to 6.14.4.
- [Release notes](https://github.com/zzzprojects/EntityFramework-Plus/releases)
- [Commits](https://github.com/zzzprojects/EntityFramework-Plus/compare/6.14.3...6.14.4)

---
updated-dependencies:
- dependency-name: Z.EntityFramework.Plus.EFCore
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-26 14:58:08 +08:00
dependabot[bot]andAigio L 1bf65820c0 Bump NLog.Extensions.Logging from 5.0.1 to 5.0.2 (#1894)
Bumps [NLog.Extensions.Logging](https://github.com/NLog/NLog.Extensions.Logging) from 5.0.1 to 5.0.2.
- [Release notes](https://github.com/NLog/NLog.Extensions.Logging/releases)
- [Changelog](https://github.com/NLog/NLog.Extensions.Logging/blob/master/CHANGELOG.MD)
- [Commits](https://github.com/NLog/NLog.Extensions.Logging/compare/v5.0.1...v5.0.2)

---
updated-dependencies:
- dependency-name: NLog.Extensions.Logging
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Aigio L <24450093+AigioL@users.noreply.github.com>
2022-08-26 14:57:28 +08:00
dependabot[bot] ba1c5b6c8b Bump Microsoft.NET.Test.Sdk from 17.2.0 to 17.3.0 (#1870)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.2.0 to 17.3.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Commits](https://github.com/microsoft/vstest/compare/v17.2.0...v17.3.0)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-26 14:56:03 +08:00
dependabot[bot] deb6c4eaa1 Bump Microsoft.Extensions.Logging.AzureAppServices from 6.0.7 to 6.0.8 (#1857)
Bumps [Microsoft.Extensions.Logging.AzureAppServices](https://github.com/dotnet/aspnetcore) from 6.0.7 to 6.0.8.
- [Release notes](https://github.com/dotnet/aspnetcore/releases)
- [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md)
- [Commits](https://github.com/dotnet/aspnetcore/compare/v6.0.7...v6.0.8)

---
updated-dependencies:
- dependency-name: Microsoft.Extensions.Logging.AzureAppServices
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-26 14:55:27 +08:00
dependabot[bot] af337a8fbd Bump Octokit from 2.0.0 to 2.0.1 (#1892)
Bumps [Octokit](https://github.com/octokit/octokit.net) from 2.0.0 to 2.0.1.
- [Release notes](https://github.com/octokit/octokit.net/releases)
- [Changelog](https://github.com/octokit/octokit.net/blob/main/docs/releases.md)
- [Commits](https://github.com/octokit/octokit.net/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: Octokit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-26 14:54:46 +08:00
dependabot[bot] 80d14c6c24 Bump Microsoft.WindowsAppSDK from 1.1.3 to 1.1.4 (#1877)
Bumps [Microsoft.WindowsAppSDK](https://github.com/microsoft/windowsappsdk) from 1.1.3 to 1.1.4.
- [Release notes](https://github.com/microsoft/windowsappsdk/releases)
- [Commits](https://github.com/microsoft/windowsappsdk/commits)

---
updated-dependencies:
- dependency-name: Microsoft.WindowsAppSDK
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-26 14:54:24 +08:00
dependabot[bot] 07d82be4c7 Bump NLog from 5.0.1 to 5.0.2 (#1891)
Bumps [NLog](https://github.com/NLog/NLog) from 5.0.1 to 5.0.2.
- [Release notes](https://github.com/NLog/NLog/releases)
- [Changelog](https://github.com/NLog/NLog/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/NLog/NLog/compare/v5.0.1...v5.0.2)

---
updated-dependencies:
- dependency-name: NLog
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-26 14:53:59 +08:00
dependabot[bot] 1e261f1125 Bump HtmlAgilityPack from 1.11.43 to 1.11.45 (#1940)
Bumps [HtmlAgilityPack](https://github.com/zzzprojects/html-agility-pack) from 1.11.43 to 1.11.45.
- [Release notes](https://github.com/zzzprojects/html-agility-pack/releases)
- [Commits](https://github.com/zzzprojects/html-agility-pack/compare/v1.11.43...v1.11.45)

---
updated-dependencies:
- dependency-name: HtmlAgilityPack
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-26 14:52:55 +08:00
Aigio Liu 8a1bde0773 Fix
System.ComponentModel.Win32Exception: An error occurred trying to start process 'C:\WINDOWS\explorer.exe' with working directory 'AppContext.BaseDirectory'. 拒绝访问。
https://appcenter.ms/orgs/BeyondDimension/apps/Steam/crashes/errors/180265099u/overview
https://appcenter.ms/orgs/BeyondDimension/apps/Steam/crashes/errors/3342720977u/overview
https://appcenter.ms/orgs/BeyondDimension/apps/Steam/crashes/errors/799855238u/overview
https://appcenter.ms/orgs/BeyondDimension/apps/Steam/crashes/errors/18523660u/overview
https://appcenter.ms/orgs/BeyondDimension/apps/Steam/crashes/errors/118999695u/overview

Try Fix
System.IO.IOException: The process cannot access the file 'C:\Users\***\AppData\Local\Packages\4651ED44255E.47979655102CE_k6txddmbb6c52\LocalState\Config.mpo' because it is being used by another process.
https://appcenter.ms/orgs/BeyondDimension/apps/Steam/crashes/errors/2552968077u/overview

Try Fix
System.Runtime.InteropServices.COMException: 无法在设置线程模式后对其加以更改。 (0x80010106 (RPC_E_CHANGED_MODE))
https://appcenter.ms/orgs/BeyondDimension/apps/Steam/crashes/errors/1550045210u/overview
2022-08-24 14:28:20 +08:00
Aigio Liu 617b99c39d MSStore 2.8.4 2022-08-18 10:27:41 +08:00
RMBGAME 8b4680fcee ReverseProxyHttpClientHandler in Server fix 2022-08-17 23:14:50 +08:00
RMBGAME e27ae2b202 Update bug-report.md 2022-08-16 20:15:48 +08:00
Aigio Liu 5419530031 Bump v2.8.4 2022-08-16 17:49:12 +08:00
RMBGAME 415fd30cc5 Bump Avalonia to 0.10.18 2022-08-16 12:27:40 +08:00
88 changed files with 2303 additions and 1842 deletions
+2 -2
View File
@@ -8,10 +8,10 @@ assignees: ''
---
<!--
发布前请先尝试在 Issuse 内搜索一下你的问题是否已经被提出过,
发布前请先尝试在 Issues 内搜索一下你的问题是否已经被提出过,
详细地描述 bug,让大家都能理解,请一定确定你所要发布的内容是一个 Bug,如果不确定请发 Question
别忘了填写标题,标题要简短的描述问题
Before posting, please try searching in Issuse to see if your question has already been asked,
Before posting, please try searching in Issues to see if your question has already been asked,
Describe the bug in detail so that everyone can understand it
Don't forget to fill in the title, which should briefly describe the problem
-->
+23 -23
View File
@@ -24,7 +24,7 @@
<PackageVersion Include="Microsoft.Extensions.Http" Version="3.1.27" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="3.1.27" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.27" />
<PackageVersion Include="Microsoft.Extensions.Logging.AzureAppServices" Version="3.1.27" />
<PackageVersion Include="Microsoft.Extensions.Logging.AzureAppServices" Version="6.0.8" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="3.1.27" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.27" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="3.1.27" />
@@ -40,12 +40,12 @@
<ItemGroup Condition=" $(TargetFramework.StartsWith('net6.0')) ">
<!-- Entity Framework Plus -->
<!-- A FREE & Open Source library to enhance EF6 and EF Core -->
<PackageVersion Include="Z.EntityFramework.Plus.EFCore" Version="6.14.3" />
<PackageVersion Include="Z.EntityFramework.Plus.EFCore" Version="6.14.4" />
<!-- AspNetCore 6 OpenId/OAuth -->
<PackageVersion Include="AspNet.Security.OpenId.Steam" Version="6.0.0" />
<PackageVersion Include="AspNet.Security.OAuth.QQ" Version="6.0.6" />
<PackageVersion Include="AspNet.Security.OAuth.Apple" Version="6.0.6" />
<PackageVersion Include="AspNet.Security.OAuth.QQ" Version="6.0.9" />
<PackageVersion Include="AspNet.Security.OAuth.Apple" Version="6.0.9" />
</ItemGroup>
<ItemGroup>
</ItemGroup>
@@ -74,7 +74,7 @@
</ItemGroup>
<ItemGroup>
<!-- Microsoft.* -->
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.3.0" />
<!--<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="1.4.1" />-->
<!--<PackageVersion Include="Microsoft.Windows.SDK.NET.Ref" Version="10.0.19041.22" />-->
<!--<PackageVersion Include="Microsoft.Windows.SDK.Contracts" Version="10.0.19041.1" />-->
@@ -117,15 +117,15 @@
<PackageVersion Include="Moq" Version="4.18.2" />
<!-- NLog -->
<PackageVersion Include="NLog" Version="5.0.1" />
<PackageVersion Include="NLog.Extensions.Logging" Version="5.0.1" />
<PackageVersion Include="NLog.Web.AspNetCore" Version="5.1.0" />
<PackageVersion Include="NLog" Version="5.0.2" />
<PackageVersion Include="NLog.Extensions.Logging" Version="5.0.2" />
<PackageVersion Include="NLog.Web.AspNetCore" Version="5.1.1" />
<PackageVersion Include="AspNetCore2.NETStandard.NLog.Web.AspNetCore" Version="5.0.0" />
<!-- ReactiveUI -->
<PackageVersion Include="System.Reactive" Version="5.0.0" />
<PackageVersion Include="Splat" Version="14.4.1" />
<PackageVersion Include="DynamicData" Version="7.9.7" />
<PackageVersion Include="DynamicData" Version="7.9.14" />
<PackageVersion Include="ReactiveUI" Version="18.3.1" />
<PackageVersion Include="ReactiveUI.Fody" Version="18.3.1" />
<PackageVersion Include="ReactiveUI.XamForms" Version="18.3.1" />
@@ -193,7 +193,7 @@
<PackageVersion Include="NPOI" Version="2.5.6" />
<!-- Github API -->
<PackageVersion Include="Octokit" Version="2.0.0" />
<PackageVersion Include="Octokit" Version="2.0.1" />
<!-- ORM -->
<PackageVersion Include="sqlite-net-pcl" Version="1.8.116" />
@@ -211,7 +211,7 @@
<PackageVersion Include="Newtonsoft.Json" Version="13.0.1" />
<!-- Html Parser -->
<PackageVersion Include="HtmlAgilityPack" Version="1.11.43" />
<PackageVersion Include="HtmlAgilityPack" Version="1.11.45" />
<!-- Clipboard -->
<PackageVersion Include="TextCopy" Version="6.1.0" />
@@ -239,13 +239,13 @@
<PackageVersion Include="Polly" Version="7.2.3" />
<!--Avalonia Start-->
<PackageVersion Include="Avalonia" Version="0.10.17" />
<PackageVersion Include="Avalonia.Desktop" Version="0.10.17" />
<PackageVersion Include="Avalonia.Skia" Version="0.10.17" />
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.17" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="0.10.17" />
<PackageVersion Include="Avalonia" Version="0.10.18" />
<PackageVersion Include="Avalonia.Desktop" Version="0.10.18" />
<PackageVersion Include="Avalonia.Skia" Version="0.10.18" />
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.18" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="0.10.18" />
<!--<PackageVersion Include="Avalonia.Direct2D1" Version="0.10.16" />-->
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.17" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.18" />
<PackageVersion Include="Avalonia.Xaml.Behaviors" Version="0.10.17" />
<!--<PackageVersion Include="Avalonia.AvaloniaEdit" Version="0.10.12.2" />-->
<!--<PackageVersion Include="CefNet.Avalonia" Version="93.1.21240.857" />-->
@@ -253,17 +253,17 @@
<!--<PackageVersion Include="FluentAvaloniaUI" Version="1.4.1" />-->
<PackageVersion Include="LiveChartsCore.SkiaSharpView.Avalonia" Version="2.0.0-beta.350" />
<PackageVersion Include="JumpLists.Win32.Avalonia" Version="1.0.0" />
<PackageVersion Include="Avalonia.Native" Version="0.10.17" />
<PackageVersion Include="Avalonia.Win32" Version="0.10.17" />
<PackageVersion Include="Avalonia.X11" Version="0.10.17" />
<PackageVersion Include="Avalonia.Native" Version="0.10.18" />
<PackageVersion Include="Avalonia.Win32" Version="0.10.18" />
<PackageVersion Include="Avalonia.X11" Version="0.10.18" />
<!--<PackageVersion Include="Avalonia.HtmlRenderer" Version="0.10.0" />-->
<PackageVersion Include="WebView2.Avalonia" Version="1.0.1264.42-preview.220713.10" />
<!--Avalonia End-->
<!-- Xamarin.Android -->
<PackageVersion Include="Xamarin.Build.Download" Version="0.11.3" />
<PackageVersion Include="Xamarin.Google.Dagger" Version="2.40.5" />
<PackageVersion Include="Xamarin.Google.MLKit.BarcodeScanning" Version="116.1.2.5" />
<PackageVersion Include="Xamarin.Google.Dagger" Version="2.41.0.2" />
<PackageVersion Include="Xamarin.Google.MLKit.BarcodeScanning" Version="117.0.2.2" />
<PackageVersion Include="Plugin.CurrentActivity" Version="2.1.0.4" />
<PackageVersion Include="de.hdodenhof.circleimageview" Version="3.1.0.4" />
<PackageVersion Include="Square.OkHttp3" Version="4.9.3" />
@@ -307,7 +307,7 @@
<!-- UWP -->
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.1.3" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.1.4" />
<!-- Code Style -->
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.435" />
+2 -2
View File
@@ -17,7 +17,7 @@ English | [简体中文](./README.md)
[![C# 10.0](https://img.shields.io/badge/c%23-10.0-green.svg?style=flat-square&color=6da86a)](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10)
[![Desktop UI](https://img.shields.io/badge/ui@desktop-AvaloniaUI-purple.svg?style=flat-square&color=8c45ab)](https://github.com/AvaloniaUI/Avalonia)
[![Mobile GUI](https://img.shields.io/badge/gui@mobile-Xamarin.Forms-blue.svg?style=flat-square&color=3498db)](https://github.com/xamarin/Xamarin.Forms)
[![Mobile UI](https://img.shields.io/badge/ui@mobile-MAUI-blue.svg?style=flat-square&color=512BD4)](https://github.com/dotnet/maui)
[![Official WebSite](https://img.shields.io/badge/website@official-Ant%20Design%20of%20React-blue.svg?style=flat-square&color=61dafb)](https://github.com/ant-design/ant-design)
[![BackManage WebSite](https://img.shields.io/badge/website@back_manage-Ant%20Design%20of%20Blazor-purple.svg?style=flat-square&color=512bd4)](https://github.com/ant-design-blazor/ant-design-blazor)
@@ -183,7 +183,7 @@ Read what we [milestones](https://github.com/BeyondDimension/SteamTools/mileston
- [Avalonia for Visual Studio(Optional)](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio)
- [NUnit VS Templates(Optional)](https://marketplace.visualstudio.com/items?itemName=NUnitDevelopers.NUnitTemplatesforVisualStudio)
[OpenJDK 11](https://docs.microsoft.com/java/openjdk/download#openjdk-11)
[OpenJDK 17](https://docs.microsoft.com/en-us/java/openjdk/download#openjdk-17)
[Android Studio 2021.1.1 Or Higher](https://developer.android.com/studio/)
[Xcode 13 Or Higher](https://developer.apple.com/xcode/)
+2 -2
View File
@@ -17,7 +17,7 @@
[![C# 10.0](https://img.shields.io/badge/c%23-10.0-brightgreen.svg?style=flat-square&color=6da86a)](https://docs.microsoft.com/zh-cn/dotnet/csharp/whats-new/csharp-10)
[![Desktop UI](https://img.shields.io/badge/ui@desktop-AvaloniaUI-purple.svg?style=flat-square&color=8c45ab)](https://github.com/AvaloniaUI/Avalonia)
[![Mobile GUI](https://img.shields.io/badge/gui@mobile-Xamarin.Forms-blue.svg?style=flat-square&color=3498db)](https://github.com/xamarin/Xamarin.Forms)
[![Mobile UI](https://img.shields.io/badge/ui@mobile-MAUI-blue.svg?style=flat-square&color=512BD4)](https://github.com/dotnet/maui)
[![Official WebSite](https://img.shields.io/badge/website@official-Ant%20Design%20of%20React-blue.svg?style=flat-square&color=61dafb)](https://github.com/ant-design/ant-design)
[![BackManage WebSite](https://img.shields.io/badge/website@back_manage-Ant%20Design%20of%20Blazor-purple.svg?style=flat-square&color=512bd4)](https://github.com/ant-design-blazor/ant-design-blazor)
@@ -194,7 +194,7 @@ OS | Version | Architectures |
- [Avalonia for Visual Studio(可选)](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio)
- [NUnit VS Templates(可选)](https://marketplace.visualstudio.com/items?itemName=NUnitDevelopers.NUnitTemplatesforVisualStudio)
[OpenJDK 11](https://docs.microsoft.com/zh-cn/java/openjdk/download#openjdk-11)
[OpenJDK 17](https://docs.microsoft.com/zh-cn/java/openjdk/download#openjdk-17)
[Android Studio 2021.1.1 或更高版本](https://developer.android.google.cn/studio/)
[Xcode 13 或更高版本](https://developer.apple.com/xcode/)
+2 -1
View File
@@ -2670,6 +2670,7 @@ Global
{48417EE3-BACB-4E04-B48D-B3781862AD81} = {8E38AAF2-8C7D-4B0D-AB25-AB37EECB4029}
{41434AB5-48C3-47D4-8721-D118D87F472B} = {83186945-EC14-4B3F-8C6F-B205AA184CA4}
{07C9AC06-5D84-41AB-A3C9-5C5B1A27F526} = {83186945-EC14-4B3F-8C6F-B205AA184CA4}
{5C440EC2-9251-4DB5-A14E-68FF37814F12} = {83186945-EC14-4B3F-8C6F-B205AA184CA4}
{1891DD14-628D-4921-8A1E-279320312E95} = {327FDEE7-C4C8-4AA4-A7B0-8BCE3D75346F}
{3381B512-7CCB-4211-87B0-273260531BFC} = {327FDEE7-C4C8-4AA4-A7B0-8BCE3D75346F}
{CCB0CAEB-FEFC-4424-B6F4-53D3BB70154D} = {327FDEE7-C4C8-4AA4-A7B0-8BCE3D75346F}
@@ -2682,7 +2683,7 @@ Global
{8C996C50-B29F-4CB6-90A7-77C9CD3B0B70} = {910D6A0F-42AB-4B65-B532-852E864EC006}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9313F2B4-10DA-43AA-A481-CAA7571C740C}
RESX_NeutralResourcesLanguage = zh-Hans
SolutionGuid = {9313F2B4-10DA-43AA-A481-CAA7571C740C}
EndGlobalSection
EndGlobal
Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

+5 -1
View File
@@ -5,7 +5,11 @@ using Android.OS;
using Android.Webkit;
using Android.Util;
using Android.Opengl;
#if NET6_0_OR_GREATER
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
#else
using XEPlatform = Xamarin.Essentials.Platform;
#endif
using Process = System.Diagnostics.Process;
using AndroidApplication = Android.App.Application;
#endif
@@ -543,7 +547,7 @@ namespace System.Application.UI
b.Append(Essentials.IsSupported.ToLowerString());
b.AppendLine();
#if __ANDROID__
#if __ANDROID__ && !NET6_0_MAUI_LIB
b.Append("[startup.track] ");
b.AppendLine();
b.Append(MainApplication.StartupTrack);
@@ -3,7 +3,11 @@ using Microsoft.Extensions.DependencyInjection;
using AndroidToast = Android.Widget.Toast;
using AndroidToastLength = Android.Widget.ToastLength;
using JException = Java.Lang.Exception;
#if NET6_0_OR_GREATER
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
#else
using XEPlatform = Xamarin.Essentials.Platform;
#endif
namespace System.Application.Services.Implementation
{
@@ -7,7 +7,11 @@ using AndroidX.AppCompat.App;
using AndroidX.Fragment.App;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
#if NET6_0_OR_GREATER
using Microsoft.Maui.ApplicationModel;
#else
using Xamarin.Essentials;
#endif
using Fragment = AndroidX.Fragment.App.Fragment;
using Toolbar = AndroidX.AppCompat.Widget.Toolbar;
@@ -3,9 +3,17 @@ using Android.OS;
using Android.Provider;
using Android.Webkit;
using System.IO;
#if NET6_0_OR_GREATER
using Microsoft.Maui.ApplicationModel;
#else
using Xamarin.Essentials;
#endif
using AndroidUri = Android.Net.Uri;
#if NET6_0_OR_GREATER
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
#else
using XEPlatform = Xamarin.Essentials.Platform;
#endif
// ReSharper disable once CheckNamespace
namespace System
@@ -61,7 +61,7 @@ public sealed class FileSettingsProvider : ISerializationProvider
lock (_sync)
{
using var stream = new FileStream(_path, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete);
using var stream = OpenReadWrite(_path);
if (stream.Position > 0)
{
stream.Position = 0;
@@ -72,6 +72,28 @@ public sealed class FileSettingsProvider : ISerializationProvider
}
}
static FileStream OpenReadWrite(string path)
{
try
{
return OpenReadWrite();
}
catch (Exception)
{
if (File.Exists(path))
{
if (IOPath.FileTryDelete(path))
{
// 直接打开文件流失败,尝试删除后再打开一次
return OpenReadWrite();
}
}
throw;
}
FileStream OpenReadWrite() => new(path, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete);
}
public void Load()
{
if (File.Exists(_path))
@@ -34,4 +34,23 @@ public static class ProcessExtensions
process.Kill();
#endif
}
/// <summary>
/// 尝试 Process 组件在指定的毫秒数内等待关联进程退出。
/// </summary>
/// <param name="process"></param>
/// <param name="milliseconds"></param>
/// <returns></returns>
public static bool TryWaitForExit(this Process process, int milliseconds = 9000)
{
try
{
return process.WaitForExit(milliseconds);
}
catch
{
}
return false;
}
}
+5
View File
@@ -132,6 +132,11 @@ public static class FileEx
public const string Qt = ".qt";
public const string Wmv = ".wmv";
/// <summary>
/// 导入注册条目 (.reg) 文件是 Regedit.exe 的功能,Regedt32.exe 不支持。您可以使用 Regedit.exe 对基于 Windows NT 4.0 或基于 Windows 2000 的计算机上注册表进行一些更改,但某些更改需要 Regedt32.exe。
/// </summary>
public const string Reg = ".reg";
public static readonly string[] ImageFileExtensions = new string[]
{
PNG,
+83
View File
@@ -153,6 +153,89 @@ public static class IOPath
}
}
static readonly object lock_GetCacheFilePath = new();
/// <summary>
/// 根据缓存子文件夹名称与文件扩展名获取一个缓存文件路径
/// </summary>
/// <param name="dirName">缓存子文件夹名称</param>
/// <param name="fileNamePrefix">文件名前缀</param>
/// <param name="fileEx">文件扩展名</param>
public static string GetCacheFilePath(string dirName, string fileNamePrefix, string fileEx)
{
lock (lock_GetCacheFilePath)
{
var cacheDirPath = Path.Combine(CacheDirectory, dirName);
if (!Directory.Exists(cacheDirPath))
{
Directory.CreateDirectory(cacheDirPath);
return GetCacheFilePath();
}
else
{
string cacheFilePath;
do
{
cacheFilePath = GetCacheFilePath();
} while (File.Exists(cacheFilePath));
return cacheFilePath;
}
string GetCacheFilePath() => Path.Combine(cacheDirPath, GetCacheFileName());
string GetCacheFileName() => $"{fileNamePrefix}_{DateTimeOffset.Now.Ticks}{Random2.GenerateRandomString(4)}{fileEx}";
}
}
/// <summary>
/// 尝试延时一段时间后删除文件
/// </summary>
/// <param name="filePath">要删除的文件路径</param>
/// <param name="millisecondsDelay">延时等待的毫秒数</param>
public static async void TryDeleteInDelay(string filePath, int millisecondsDelay = 9000)
{
await Task.Delay(millisecondsDelay);
FileTryDelete(filePath);
}
/// <summary>
/// 启动进程后尝试延时一段时间后删除文件
/// </summary>
/// <param name="process">启动的进程</param>
/// <param name="filePath">要删除的文件路径</param>
/// <param name="millisecondsDelay">延时等待的毫秒数</param>
/// <param name="processWaitMillisecondsDelay">启动的进程等待退出的毫秒数</param>
public static void TryDeleteInDelay(Process? process, string filePath, int millisecondsDelay = 9000, int processWaitMillisecondsDelay = 9000)
{
if (process != null)
{
var waitForExitResult = process.TryWaitForExit(processWaitMillisecondsDelay);
if (!waitForExitResult)
{
try
{
process.KillEntireProcessTree();
}
catch
{
}
TryDeleteInDelay(filePath, millisecondsDelay);
return;
}
}
FileTryDelete(filePath);
}
/// <summary>
/// 尝试根据缓存子文件夹名称删除整个缓存子文件夹
/// </summary>
/// <param name="dirName">缓存子文件夹名称</param>
public static void TryDeleteCacheSubDir(string dirName)
{
var cacheDirPath = Path.Combine(CacheDirectory, dirName);
DirTryDelete(cacheDirPath);
}
public abstract class FileSystemBase
{
protected FileSystemBase()
@@ -12,7 +12,7 @@ namespace System.Properties
/// <item> macOS:\packaging\build-osx-app.sh &lt;CFBundleVersion&gt; &lt;CFBundleShortVersionString&gt; </item>
/// </list>
/// </summary>
public const string Version = "2.8.3";
public const string Version = "2.8.4";
//public const string InfoVersion = Version + "-beta";
}
+1 -1
View File
@@ -87,7 +87,7 @@ public struct ShortGuid
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
public override bool Equals(object? obj)
{
if (obj is ShortGuid shortGuid)
{
@@ -20,6 +20,27 @@ public sealed class ReverseProxyHttpClientHandler : DelegatingHandler
};
}
/// <summary>
/// 发送请求
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var uri = request.RequestUri;
if (uri == null)
{
throw new ApplicationException("The requested URI must be specified.");
}
// 设置请求头 host,修改协议为 http
request.Headers.Host = uri.Host;
request.RequestUri = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.Uri;
return await base.SendAsync(request, cancellationToken);
}
/// <summary>
/// 连接回调
/// </summary>
@@ -0,0 +1,15 @@
#if NET6_0_OR_GREATER
using System.Drawing;
using AndroidColor = Android.Graphics.Color;
namespace System;
public static partial class ColorExtensions
{
public static Color ToSystemColor(this AndroidColor color) =>
Color.FromArgb(color.A, color.R, color.G, color.B);
public static AndroidColor ToPlatformColor(this Color color) =>
new(color.R, color.G, color.B, color.A);
}
#endif
@@ -4,28 +4,29 @@ using Android.OS;
using NativeService = System.Application.Services.Native.IServiceBase;
// ReSharper disable once CheckNamespace
namespace System
namespace System;
partial class ContextExtensions
{
partial class ContextExtensions
/// <summary>
/// 启动或关闭前台服务
/// </summary>
/// <typeparam name="TService"></typeparam>
/// <param name="context"></param>
/// <param name="startOrStop"></param>
public static void StartOrStopForegroundService<TService>(this Context context, bool startOrStop) where TService : Service, NativeService
{
/// <summary>
/// 启动或关闭前台服务
/// </summary>
/// <typeparam name="TService"></typeparam>
/// <param name="context"></param>
/// <param name="startOrStop"></param>
public static void StartOrStopForegroundService<TService>(this Context context, bool startOrStop) where TService : Service, NativeService
Intent intent = new(context, typeof(TService));
intent.SetAction(startOrStop ? NativeService.START : NativeService.STOP);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
Intent intent = new(context, typeof(TService));
intent.SetAction(startOrStop ? NativeService.START : NativeService.STOP);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
context.StartForegroundService(intent);
}
else
{
context.StartService(intent);
}
#pragma warning disable CA1416 // 验证平台兼容性
context.StartForegroundService(intent);
#pragma warning restore CA1416 // 验证平台兼容性
}
else
{
context.StartService(intent);
}
}
}
@@ -1,6 +1,10 @@
using System.Threading.Tasks;
using Android.Telephony;
#if NET6_0_OR_GREATER
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
#else
using XEPlatform = Xamarin.Essentials.Platform;
#endif
namespace System.Application;
+10 -4
View File
@@ -1,7 +1,7 @@
<Project Sdk="MSBuild.Sdk.Extras">
<PropertyGroup>
<TargetFrameworks>MonoAndroid12.0</TargetFrameworks>
<TargetFrameworks>MonoAndroid12.0;net6.0-android</TargetFrameworks>
<RootNamespace>System.Application</RootNamespace>
<AssemblyName>Xamarin.Forms.Platform.Android.UnitTests</AssemblyName>
<DefineConstants>SHADOWSOCKS;$(DefineConstants)</DefineConstants>
@@ -9,6 +9,11 @@
<SupportedOSPlatformVersion Condition="$(TargetFramework.Contains('-android'))">21.0</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition=" !($(TargetFramework.StartsWith('MonoAndroid'))) ">
<UseMaui>true</UseMaui>
<DefineConstants>NET6_0_MAUI_LIB;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<!--当前包是否为应用商店渠道包(IS_STORE_PACKAGE)-->
<!--<PropertyGroup>
<DefineConstants>IS_STORE_PACKAGE;$(DefineConstants)</DefineConstants>
@@ -25,7 +30,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="..\Startup.cs" Link="UI\MainApplication.Startup.cs" />
<Compile Condition="!$(TargetFramework.Contains('-android'))" Include="..\Startup.cs" Link="UI\MainApplication.Startup.cs" />
<Compile Include="..\Common.CoreLib\Properties\AssemblyInfo.cs">
<Link>Properties\AssemblyInfo.cs</Link>
</Compile>
@@ -106,7 +111,7 @@
<LinkBase>UI\Adapters</LinkBase>
</Compile>
<Compile Include="..\Common.ClientLib.Droid\Application\DarkModeUtil.cs" />
<Compile Include="..\ST.Client.Desktop.Avalonia\ServiceCollectionExtensions.AddWindowManager.cs" />
<Compile Condition="!$(TargetFramework.Contains('-android'))" Include="..\ST.Client.Desktop.Avalonia\ServiceCollectionExtensions.AddWindowManager.cs" />
<Compile Include="..\ST.Client.Desktop.Avalonia\ServiceCollectionExtensions.AddApplicationUpdateService.cs" />
<AndroidResource Include="Resources\**\*" />
<Compile Remove="Services\V2Ray\**" />
@@ -158,7 +163,8 @@
<ItemGroup>
<ProjectReference Include="..\Common.CoreLib\Common.CoreLib.csproj" />
<ProjectReference Include="..\Common.Essentials.Xamarin\Common.Essentials.Xamarin.csproj" />
<ProjectReference Condition=" ($(TargetFramework.StartsWith('MonoAndroid'))) " Include="..\Common.Essentials.Xamarin\Common.Essentials.Xamarin.csproj" />
<ProjectReference Condition=" !($(TargetFramework.StartsWith('MonoAndroid'))) " Include="..\Common.Essentials.Maui\Common.Essentials.Maui.csproj" />
<ProjectReference Condition=" $(DefineConstants.Contains(`V2RAY`)) " Include="..\ST.Client.Android.V2Ray\ST.Client.Android.V2Ray.csproj" />
<ProjectReference Condition=" $(DefineConstants.Contains(`SHADOWSOCKS`)) " Include="..\ST.Client.Android.Shadowsocks\ST.Client.Android.Shadowsocks.csproj" />
<ProjectReference Include="..\ST.Client.AppCenter\ST.Client.AppCenter.csproj" />
@@ -1,7 +1,15 @@
using Android.OS;
using System.IO;
#if NET6_0_OR_GREATER
using Microsoft.Maui.Devices;
#else
using Xamarin.Essentials;
#endif
#if NET6_0_OR_GREATER
using XEDeviceType = Microsoft.Maui.Devices.DeviceType;
#else
using XEDeviceType = Xamarin.Essentials.DeviceType;
#endif
namespace System.Application.Services.Implementation
{
@@ -2,7 +2,11 @@ using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using ASF_FS = System.Application.Services.Native.ArchiSteamFarmForegroundService;
#if NET6_0_OR_GREATER
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
#else
using XEPlatform = Xamarin.Essentials.Platform;
#endif
using Android.App;
using System.Application.Services.Native;
@@ -4,7 +4,11 @@ using System.Net;
using AndroidX.AppCompat.App;
using System.Application.UI.Activities;
using AndroidApplication = Android.App.Application;
#if NET6_0_OR_GREATER
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
#else
using XEPlatform = Xamarin.Essentials.Platform;
#endif
namespace System.Application.Services.Implementation
{
@@ -12,288 +12,305 @@ using System.Application.UI.Resx;
using System.Application.UI.ViewModels;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using System.Collections.Generic;
#if NET6_0_OR_GREATER
using Microsoft.Maui.ApplicationModel;
using static Microsoft.Maui.ApplicationModel.Platform;
#else
using Xamarin.Essentials;
using static Xamarin.Essentials.Platform;
#endif
using AlertDialog = AndroidX.AppCompat.App.AlertDialog;
namespace System.Application.Services.Implementation
namespace System.Application.Services.Implementation;
/// <inheritdoc cref="IWindowManager"/>
public class AndroidWindowManagerImpl : IWindowManagerImpl
{
/// <inheritdoc cref="IWindowManager"/>
public class AndroidWindowManagerImpl : IWindowManagerImpl
/* AlertDialog
* =======================================
* = Title =
* = Message(Content) =
* = Neutral Negative Positive =
* =======================================
*/
protected static string PositiveButtonText => AppResources.Confirm;
protected static string NegativeButtonText => AppResources.Cancel;
protected static string NeutralButtonText => AppResources.RememberChooseNotToAskAgain;
protected async Task<bool> PlatformShowWindow(CustomWindow customWindow, PageViewModel? viewModel = null, string title = "")
{
/* AlertDialog
* =======================================
* = Title =
* = Message(Content) =
* = Neutral Negative Positive =
* =======================================
*/
protected static string PositiveButtonText => AppResources.Confirm;
protected static string NegativeButtonText => AppResources.Cancel;
protected static string NeutralButtonText => AppResources.RememberChooseNotToAskAgain;
protected async Task<bool> PlatformShowWindow(CustomWindow customWindow, PageViewModel? viewModel = null, string title = "")
Activity currentActivity;
do
{
Activity currentActivity;
do
{
currentActivity = await WaitForActivityAsync();
await Task.Delay(100);
} while (!currentActivity.HasValue());
return await MainThread.InvokeOnMainThreadAsync(async () =>
{
return await PlatformShowWindowCore(currentActivity, customWindow, viewModel, title);
});
currentActivity = await WaitForActivityAsync();
await Task.Delay(100);
} while (!currentActivity.HasValue());
return await MainThread.InvokeOnMainThreadAsync(async () =>
{
return await PlatformShowWindowCore(currentActivity, customWindow, viewModel, title);
});
}
bool IWindowManager.UseMyAuthenticatorWrapper => true;
protected Task<bool> PlatformShowWindowCore(Activity currentActivity, CustomWindow customWindow, PageViewModel? viewModel = null, string title = "")
{
switch (customWindow)
{
case CustomWindow.ShowAuth:
case CustomWindow.AuthTrade:
if (viewModel is IMyAuthenticatorWrapper viewModel_auth_w && viewModel_auth_w.MyAuthenticator != null)
{
switch (customWindow)
{
case CustomWindow.ShowAuth:
// Android Activity 传参需要序列化后再反序列化,不能直接传递
// 所以此处传递 Id,在 Activity 中从关联的集合中根据 Id 取值
SteamAuthDataActivity.StartActivity(currentActivity, viewModel_auth_w.MyAuthenticator.Id);
break;
case CustomWindow.AuthTrade:
SteamAuthTradeActivity.StartActivity(currentActivity, viewModel_auth_w.MyAuthenticator.Id);
break;
}
}
return Task.FromResult(false);
}
bool IWindowManager.UseMyAuthenticatorWrapper => true;
protected Task<bool> PlatformShowWindowCore(Activity currentActivity, CustomWindow customWindow, PageViewModel? viewModel = null, string title = "")
var activityType = customWindow switch
{
switch (customWindow)
{
case CustomWindow.ShowAuth:
case CustomWindow.AuthTrade:
if (viewModel is IMyAuthenticatorWrapper viewModel_auth_w && viewModel_auth_w.MyAuthenticator != null)
{
switch (customWindow)
{
case CustomWindow.ShowAuth:
// Android Activity 传参需要序列化后再反序列化,不能直接传递
// 所以此处传递 Id,在 Activity 中从关联的集合中根据 Id 取值
SteamAuthDataActivity.StartActivity(currentActivity, viewModel_auth_w.MyAuthenticator.Id);
break;
case CustomWindow.AuthTrade:
SteamAuthTradeActivity.StartActivity(currentActivity, viewModel_auth_w.MyAuthenticator.Id);
break;
}
}
return Task.FromResult(false);
}
CustomWindow.AddAuth => typeof(AddAuthActivity),
CustomWindow.ExportAuth => typeof(ExportAuthActivity),
CustomWindow.EncryptionAuth => typeof(EncryptionAuthActivity),
CustomWindow.ProxySettings => typeof(ProxySettingsActivity),
_ => null,
};
if (activityType != null)
{
currentActivity.StartActivity(activityType);
return Task.FromResult(false);
}
var activityType = customWindow switch
{
CustomWindow.AddAuth => typeof(AddAuthActivity),
CustomWindow.ExportAuth => typeof(ExportAuthActivity),
CustomWindow.EncryptionAuth => typeof(EncryptionAuthActivity),
CustomWindow.ProxySettings => typeof(ProxySettingsActivity),
_ => null,
};
if (activityType != null)
{
currentActivity.StartActivity(activityType);
return Task.FromResult(false);
}
TaskCompletionSource<bool> tcs = new();
try
{
AlertDialog? dialog = null;
TaskCompletionSource<bool> tcs = new();
try
void SetConfirmButton(MaterialAlertDialogBuilder b)
{
AlertDialog? dialog = null;
void SetConfirmButton(MaterialAlertDialogBuilder b)
b.SetPositiveButton(PositiveButtonText, (_, e) =>
{
b.SetPositiveButton(PositiveButtonText, (_, e) =>
{
tcs.TrySetResult(true);
});
}
void SetCancelButton(MaterialAlertDialogBuilder b)
{
b.SetNegativeButton(NegativeButtonText, (_, e) =>
{
tcs.TrySetResult(false);
});
}
void SetNeutralButton(MaterialAlertDialogBuilder b, MessageBoxWindowViewModel vm)
{
b.SetNeutralButton(NeutralButtonText, (_, e) =>
{
vm.RememberChoose = true;
tcs.TrySetResult(true);
});
}
Action<AlertDialog>? CreateMessageBoxDialogWindow(MaterialAlertDialogBuilder b)
{
SetConfirmButton(b);
var isCancelcBtn = false;
if (viewModel is MessageBoxWindowViewModel viewModel_mb)
{
b.SetMessage(viewModel_mb.Content);
isCancelcBtn = viewModel_mb.IsCancelcBtn;
if (viewModel_mb.IsShowRememberChoose) SetNeutralButton(b, viewModel_mb);
}
if (isCancelcBtn) SetCancelButton(b);
return null;
}
Action<AlertDialog>? CreateTextBoxDialogWindowCore(MaterialAlertDialogBuilder b, Action<textbox_password, TextBoxWindowViewModel>? action = null)
{
if (viewModel is not TextBoxWindowViewModel viewModel_tb) throw new NotSupportedException();
// https://material.io/components/text-fields/android
// https://material.io/components/dialogs#specs
IDialogInterfaceOnClickListener? listener = null;
b.SetPositiveButton(PositiveButtonText, listener);
SetCancelButton(b);
var view = LayoutInflater.From(b.Context)!.Inflate(Resource.Layout.textbox_password, null, false)!;
var binding = new textbox_password(view);
var selectionIndex = 0;
if (!string.IsNullOrEmpty(viewModel_tb.Value))
{
binding.tbPassword.Text = viewModel_tb.Value;
selectionIndex = viewModel_tb.Value!.Length;
}
binding.layoutPassword.Hint = viewModel_tb.Placeholder;
if (viewModel_tb.MaxLength > 0)
binding.tbPassword.SetMaxLength(viewModel_tb.MaxLength);
action?.Invoke(binding, viewModel_tb);
b.SetView(view);
return d =>
{
if (binding.tbPassword.KeyListener != null)
{
currentActivity.ShowSoftInput(binding.tbPassword, inOnCreate: true);
if (selectionIndex > 0)
{
binding.tbPassword.SetSelection(selectionIndex);
}
}
d.GetButton((int)DialogButtonType.Positive).Click += (_, _) =>
{
if (viewModel is ITextBoxWindowViewModel viewModel_tb)
{
viewModel_tb.Value = binding.tbPassword.Text;
if (viewModel_tb.InputValidator())
{
d.Dismiss();
tcs.TrySetResult(true);
}
}
};
};
}
Action<AlertDialog>? CreateTextBoxDialogWindow(MaterialAlertDialogBuilder b) => CreateTextBoxDialogWindowCore(b, static (binding, viewModel) =>
{
switch (viewModel.InputType)
{
case TextBoxWindowViewModel.TextBoxInputType.TextBox:
binding.layoutPassword.EndIconMode = TextInputLayout.EndIconClearText;
binding.tbPassword.InputType = InputTypes.ClassText | InputTypes.TextVariationNormal;
break;
case TextBoxWindowViewModel.TextBoxInputType.ReadOnlyText:
binding.layoutPassword.EndIconMode = TextInputLayout.EndIconNone;
binding.tbPassword.InputType = InputTypes.ClassText | InputTypes.TextVariationNormal;
binding.tbPassword.SetReadOnly();
break;
}
tcs.TrySetResult(true);
});
Func<MaterialAlertDialogBuilder, Action<AlertDialog>?> @delegate = customWindow switch
{
CustomWindow.MessageBox => CreateMessageBoxDialogWindow,
CustomWindow.TextBox => CreateTextBoxDialogWindow,
_ => throw new NotImplementedException(),
};
if (!string.IsNullOrEmpty(title) && viewModel != null)
{
viewModel.Title = title;
}
var builder = new MaterialAlertDialogBuilder(currentActivity);
if (!string.IsNullOrWhiteSpace(viewModel?.Title))
{
builder.SetTitle(viewModel!.Title);
}
var dialog_delegate = @delegate(builder);
dialog = builder.Create();
dialog.SetCanceledOnTouchOutside(false);
dialog.CancelEvent += (_, _) =>
}
void SetCancelButton(MaterialAlertDialogBuilder b)
{
b.SetNegativeButton(NegativeButtonText, (_, e) =>
{
tcs.TrySetResult(false);
};
if (dialog_delegate != null)
});
}
void SetNeutralButton(MaterialAlertDialogBuilder b, MessageBoxWindowViewModel vm)
{
b.SetNeutralButton(NeutralButtonText, (_, e) =>
{
dialog.ShowEvent += (_, _) =>
vm.RememberChoose = true;
tcs.TrySetResult(true);
});
}
Action<AlertDialog>? CreateMessageBoxDialogWindow(MaterialAlertDialogBuilder b)
{
SetConfirmButton(b);
var isCancelcBtn = false;
if (viewModel is MessageBoxWindowViewModel viewModel_mb)
{
b.SetMessage(viewModel_mb.Content);
isCancelcBtn = viewModel_mb.IsCancelcBtn;
if (viewModel_mb.IsShowRememberChoose) SetNeutralButton(b, viewModel_mb);
}
if (isCancelcBtn) SetCancelButton(b);
return null;
}
Action<AlertDialog>? CreateTextBoxDialogWindowCore(MaterialAlertDialogBuilder b, Action<textbox_password, TextBoxWindowViewModel>? action = null)
{
if (viewModel is not TextBoxWindowViewModel viewModel_tb) throw new NotSupportedException();
// https://material.io/components/text-fields/android
// https://material.io/components/dialogs#specs
IDialogInterfaceOnClickListener? listener = null;
b.SetPositiveButton(PositiveButtonText, listener);
SetCancelButton(b);
var view = LayoutInflater.From(b.Context)!.Inflate(Resource.Layout.textbox_password, null, false)!;
var binding = new textbox_password(view);
var selectionIndex = 0;
if (!string.IsNullOrEmpty(viewModel_tb.Value))
{
binding.tbPassword.Text = viewModel_tb.Value;
selectionIndex = viewModel_tb.Value!.Length;
}
binding.layoutPassword.Hint = viewModel_tb.Placeholder;
if (viewModel_tb.MaxLength > 0)
binding.tbPassword.SetMaxLength(viewModel_tb.MaxLength);
action?.Invoke(binding, viewModel_tb);
b.SetView(view);
return d =>
{
if (binding.tbPassword.KeyListener != null)
{
dialog_delegate?.Invoke(dialog);
currentActivity.ShowSoftInput(binding.tbPassword, inOnCreate: true);
if (selectionIndex > 0)
{
binding.tbPassword.SetSelection(selectionIndex);
}
}
d.GetButton((int)DialogButtonType.Positive).Click += (_, _) =>
{
if (viewModel is ITextBoxWindowViewModel viewModel_tb)
{
viewModel_tb.Value = binding.tbPassword.Text;
if (viewModel_tb.InputValidator())
{
d.Dismiss();
tcs.TrySetResult(true);
}
}
};
}
dialog.Show();
};
}
catch (Exception e)
Action<AlertDialog>? CreateTextBoxDialogWindow(MaterialAlertDialogBuilder b) => CreateTextBoxDialogWindowCore(b, static (binding, viewModel) =>
{
tcs.TrySetException(e);
}
return tcs.Task;
}
public virtual Type? WindowType => typeof(Activity);
public virtual Task Show<TWindowViewModel>(CustomWindow customWindow, TWindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true) where TWindowViewModel : WindowViewModel, new()
{
return PlatformShowWindow(customWindow, viewModel, title);
}
public virtual Task Show(Type typeWindowViewModel, CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true)
{
return PlatformShowWindow(customWindow, viewModel, title);
}
public virtual Task Show(CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true)
{
return PlatformShowWindow(customWindow, viewModel, title);
}
public virtual Task<bool> ShowDialog<TWindowViewModel>(CustomWindow customWindow, TWindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true, bool isParent = true) where TWindowViewModel : WindowViewModel, new()
{
return PlatformShowWindow(customWindow, viewModel, title);
}
public virtual Task ShowDialog(Type typeWindowViewModel, CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true)
{
return PlatformShowWindow(customWindow, viewModel, title);
}
public virtual Task ShowDialog(CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true)
{
return PlatformShowWindow(customWindow, viewModel, title);
}
protected bool IsMatchCurrentActivityViewModel(object vm, [NotNullWhen(true)] out Activity? activity)
{
foreach (var item in MainApplication.Activities)
{
if (item is IViewFor vf && vf.ViewModel == vm)
switch (viewModel.InputType)
{
activity = item;
return true;
case TextBoxWindowViewModel.TextBoxInputType.TextBox:
binding.layoutPassword.EndIconMode = TextInputLayout.EndIconClearText;
binding.tbPassword.InputType = InputTypes.ClassText | InputTypes.TextVariationNormal;
break;
case TextBoxWindowViewModel.TextBoxInputType.ReadOnlyText:
binding.layoutPassword.EndIconMode = TextInputLayout.EndIconNone;
binding.tbPassword.InputType = InputTypes.ClassText | InputTypes.TextVariationNormal;
binding.tbPassword.SetReadOnly();
break;
}
}
activity = null;
return false;
}
});
public virtual void CloseWindow(WindowViewModel vm)
{
if (IsMatchCurrentActivityViewModel(vm, out var activity))
Func<MaterialAlertDialogBuilder, Action<AlertDialog>?> @delegate = customWindow switch
{
activity.OnBackPressed();
}
}
public virtual bool IsVisibleWindow(WindowViewModel vm)
{
if (IsMatchCurrentActivityViewModel(vm, out var activity))
CustomWindow.MessageBox => CreateMessageBoxDialogWindow,
CustomWindow.TextBox => CreateTextBoxDialogWindow,
_ => throw new NotImplementedException(),
};
if (!string.IsNullOrEmpty(title) && viewModel != null)
{
return activity.HasValue();
viewModel.Title = title;
}
return default;
var builder = new MaterialAlertDialogBuilder(currentActivity);
if (!string.IsNullOrWhiteSpace(viewModel?.Title))
{
builder.SetTitle(viewModel!.Title);
}
var dialog_delegate = @delegate(builder);
dialog = builder.Create();
dialog.SetCanceledOnTouchOutside(false);
dialog.CancelEvent += (_, _) =>
{
tcs.TrySetResult(false);
};
if (dialog_delegate != null)
{
dialog.ShowEvent += (_, _) =>
{
dialog_delegate?.Invoke(dialog);
};
}
dialog.Show();
}
public virtual void HideWindow(WindowViewModel vm)
catch (Exception e)
{
CloseWindow(vm);
tcs.TrySetException(e);
}
return tcs.Task;
}
public virtual Type? WindowType => typeof(Activity);
public virtual Task Show<TWindowViewModel>(CustomWindow customWindow, TWindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true) where TWindowViewModel : WindowViewModel, new()
{
return PlatformShowWindow(customWindow, viewModel, title);
}
public virtual Task Show(Type typeWindowViewModel, CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true)
{
return PlatformShowWindow(customWindow, viewModel, title);
}
public virtual Task Show(CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true)
{
return PlatformShowWindow(customWindow, viewModel, title);
}
public virtual Task<bool> ShowDialog<TWindowViewModel>(CustomWindow customWindow, TWindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true, bool isParent = true) where TWindowViewModel : WindowViewModel, new()
{
return PlatformShowWindow(customWindow, viewModel, title);
}
public virtual Task ShowDialog(Type typeWindowViewModel, CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true)
{
return PlatformShowWindow(customWindow, viewModel, title);
}
public virtual Task ShowDialog(CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true)
{
return PlatformShowWindow(customWindow, viewModel, title);
}
protected virtual IEnumerable<Activity> Activities
{
get
{
#if NET6_0_MAUI_LIB
throw new NotImplementedException();
#else
return MainApplication.Activities;
#endif
}
}
protected bool IsMatchCurrentActivityViewModel(object vm, [NotNullWhen(true)] out Activity? activity)
{
foreach (var item in Activities)
{
if (item is IViewFor vf && vf.ViewModel == vm)
{
activity = item;
return true;
}
}
activity = null;
return false;
}
public virtual void CloseWindow(WindowViewModel vm)
{
if (IsMatchCurrentActivityViewModel(vm, out var activity))
{
activity.OnBackPressed();
}
}
public virtual bool IsVisibleWindow(WindowViewModel vm)
{
if (IsMatchCurrentActivityViewModel(vm, out var activity))
{
return activity.HasValue();
}
return default;
}
public virtual void HideWindow(WindowViewModel vm)
{
CloseWindow(vm);
}
}
@@ -3,7 +3,11 @@ using Android.OS;
#endif
using Microsoft.Extensions.Options;
using System.Application.Models;
#if NET6_0_OR_GREATER
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
#else
using XEPlatform = Xamarin.Essentials.Platform;
#endif
using System.Application.UI;
namespace System.Application.Services.Implementation
@@ -1,11 +1,17 @@
#if ANDROID || __ANDROID__
using Android.Content;
using Android.Net;
#endif
using System.Application.Services;
using System.Application.Services.Implementation;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
#if NET6_0_OR_GREATER
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
#else
using XEPlatform = Xamarin.Essentials.Platform;
#endif
namespace System.Application.Services.Implementation
{
@@ -3,7 +3,11 @@ using System.Linq;
using System.IO;
using System.Threading.Tasks;
using static AndroidX.Activity.Result.ActivityResultTask;
#if NET6_0_OR_GREATER
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
#else
using XEPlatform = Xamarin.Essentials.Platform;
#endif
namespace System.Application.Services.Implementation
{
@@ -1,5 +1,9 @@
using System.Threading.Tasks;
#if NET6_0_OR_GREATER
using XEPermissions = Microsoft.Maui.ApplicationModel.Permissions;
#else
using XEPermissions = Xamarin.Essentials.Permissions;
#endif
namespace System.Application.Services.Implementation.Permissions;
@@ -1,6 +1,10 @@
using AndroidX.Biometric;
using System.Threading.Tasks;
#if NET6_0_OR_GREATER
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
#else
using XEPlatform = Xamarin.Essentials.Platform;
#endif
namespace System.Application.Services.Implementation
{
@@ -16,15 +16,22 @@ using System.Application.Services;
using System.Application.UI.Resx;
using System.Collections.Generic;
using System.Linq;
using Xamarin.Essentials;
using Xamarin.Google.MLKit.Vision.BarCode;
using Xamarin.Google.MLKit.Vision.Common;
using Xamarin.Google.MLKit.Vision.Barcode.Common;
using AUri = Android.Net.Uri;
using Fragment = AndroidX.Fragment.App.Fragment;
using GmsTask = Android.Gms.Tasks.Task;
using JException = Java.Lang.Exception;
using JObject = Java.Lang.Object;
#if NET6_0_OR_GREATER
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Media;
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
#else
using Xamarin.Essentials;
using XEPlatform = Xamarin.Essentials.Platform;
#endif
namespace System.Application.UI.Activities
{
@@ -20,7 +20,13 @@ using System.Threading.Tasks;
using static AndroidX.Activity.Result.ActivityResultTask;
using _ThisAssembly = System.Properties.ThisAssembly;
using ASettings = Android.Provider.Settings;
#if NET6_0_OR_GREATER
using Microsoft.Maui.ApplicationModel;
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
#else
using Xamarin.Essentials;
using XEPlatform = Xamarin.Essentials.Platform;
#endif
namespace System.Application.UI.Activities
{
@@ -1,3 +1,4 @@
#if !NET6_0_MAUI_LIB
using Android.App;
using Android.Content;
using Android.Content.PM;
@@ -15,155 +16,155 @@ using Fragment = AndroidX.Fragment.App.Fragment;
using Google.Android.Material.Navigation;
using AndroidX.ViewPager2.Widget;
namespace System.Application.UI.Activities
namespace System.Application.UI.Activities;
/// <summary>
/// 使用 ViewPager2 + BottomNavigationView 实现的 MainActivity
/// </summary>
[Register(JavaPackageConstants.Activities + nameof(MainActivity))]
[Activity(Theme = ManifestConstants.MainTheme2_NoActionBar,
LaunchMode = LaunchMode.SingleTask,
ConfigurationChanges = ManifestConstants.ConfigurationChanges)]
internal sealed partial class MainActivity : BaseActivity<activity_main_3>, ViewPagerAdapter.IHost, BottomNavigationView.IOnItemSelectedListener
{
/// <summary>
/// 使用 ViewPager2 + BottomNavigationView 实现的 MainActivity
/// </summary>
[Register(JavaPackageConstants.Activities + nameof(MainActivity))]
[Activity(Theme = ManifestConstants.MainTheme2_NoActionBar,
LaunchMode = LaunchMode.SingleTask,
ConfigurationChanges = ManifestConstants.ConfigurationChanges)]
internal sealed partial class MainActivity : BaseActivity<activity_main_3>, ViewPagerAdapter.IHost, BottomNavigationView.IOnItemSelectedListener
protected override int? LayoutResource => Resource.Layout.activity_main_3;
protected override bool BackToHome => true;
void SetBottomNavigationMenuTitle(int resId, CharSequence value)
{
protected override int? LayoutResource => Resource.Layout.activity_main_3;
if (binding == null) return;
var menu = binding.nav_view.Menu;
var menuItem = menu.FindItem(resId);
if (menuItem == null) return;
menuItem.SetTitle(value);
}
protected override bool BackToHome => true;
void SetSubPageTitle(int resId, string value)
{
var value_ = value.ToJavaString();
SetBottomNavigationMenuTitle(resId, value_);
this.SetNavigationGraphTitle(resId, value_);
}
void SetBottomNavigationMenuTitle(int resId, CharSequence value)
static MainActivity? mainActivity;
public static MainActivity Instance => mainActivity ?? throw new ArgumentNullException(nameof(mainActivity));
(Type fragmentType, Func<string> getTitle, int menuId, TabItemViewModel.TabItemId tabItemId)[]? fragments;
protected override void OnCreate2(Bundle? savedInstanceState)
{
base.OnCreate2(savedInstanceState);
mainActivity = this;
fragments = new (Type fragmentType, Func<string> getTitle, int menuId, TabItemViewModel.TabItemId tabItemId)[]
{
(typeof(LocalAuthFragment), () => LocalAuthPageViewModel.DisplayName, Resource.Id.navigation_local_auth, TabItemViewModel.TabItemId.LocalAuth),
(typeof(ASFPlusFragment), () => ArchiSteamFarmPlusPageViewModel.DisplayName, Resource.Id.navigation_asf_plus, TabItemViewModel.TabItemId.ArchiSteamFarmPlus),
(typeof(CommunityFixFragment), () => AppResources.CommunityFix, Resource.Id.navigation_community_fix, TabItemViewModel.TabItemId.CommunityProxy),
(typeof(MyFragment), () => MyPageViewModel.DisplayName, Resource.Id.navigation_my, default),
};
SetSupportActionBar(binding!.toolbar);
R.Subscribe(() =>
{
if (binding == null) return;
var menu = binding.nav_view.Menu;
var menuItem = menu.FindItem(resId);
if (menuItem == null) return;
menuItem.SetTitle(value);
}
void SetSubPageTitle(int resId, string value)
{
var value_ = value.ToJavaString();
SetBottomNavigationMenuTitle(resId, value_);
this.SetNavigationGraphTitle(resId, value_);
}
static MainActivity? mainActivity;
public static MainActivity Instance => mainActivity ?? throw new ArgumentNullException(nameof(mainActivity));
(Type fragmentType, Func<string> getTitle, int menuId, TabItemViewModel.TabItemId tabItemId)[]? fragments;
protected override void OnCreate2(Bundle? savedInstanceState)
{
base.OnCreate2(savedInstanceState);
mainActivity = this;
fragments = new (Type fragmentType, Func<string> getTitle, int menuId, TabItemViewModel.TabItemId tabItemId)[]
foreach (var (_, getTitle, menuId, _) in fragments)
{
(typeof(LocalAuthFragment), () => LocalAuthPageViewModel.DisplayName, Resource.Id.navigation_local_auth, TabItemViewModel.TabItemId.LocalAuth),
(typeof(ASFPlusFragment), () => ArchiSteamFarmPlusPageViewModel.DisplayName, Resource.Id.navigation_asf_plus, TabItemViewModel.TabItemId.ArchiSteamFarmPlus),
(typeof(CommunityFixFragment), () => AppResources.CommunityFix, Resource.Id.navigation_community_fix, TabItemViewModel.TabItemId.CommunityProxy),
(typeof(MyFragment), () => MyPageViewModel.DisplayName, Resource.Id.navigation_my, default),
};
SetSupportActionBar(binding!.toolbar);
R.Subscribe(() =>
{
if (binding == null) return;
foreach (var (_, getTitle, menuId, _) in fragments)
{
var title = getTitle();
SetSubPageTitle(menuId, title);
}
SetTitleByPosition(binding.viewPager2.CurrentItem);
}).AddTo(this);
var adapter = new ViewPagerAdapter(this, this);
binding.viewPager2.Adapter = adapter;
binding.viewPager2.RegisterOnPageChangeCallback(new OnPageChangeCallback(this));
binding.nav_view.SetOnItemSelectedListener(this);
}
sealed class OnPageChangeCallback : ViewPager2.OnPageChangeCallback
{
readonly MainActivity thiz;
public OnPageChangeCallback(MainActivity thiz)
{
this.thiz = thiz;
var title = getTitle();
SetSubPageTitle(menuId, title);
}
SetTitleByPosition(binding.viewPager2.CurrentItem);
}).AddTo(this);
public override void OnPageSelected(int position)
{
thiz.OnPageSelected(position);
}
var adapter = new ViewPagerAdapter(this, this);
binding.viewPager2.Adapter = adapter;
binding.viewPager2.RegisterOnPageChangeCallback(new OnPageChangeCallback(this));
binding.nav_view.SetOnItemSelectedListener(this);
}
sealed class OnPageChangeCallback : ViewPager2.OnPageChangeCallback
{
readonly MainActivity thiz;
public OnPageChangeCallback(MainActivity thiz)
{
this.thiz = thiz;
}
protected override void OnNewIntent(Intent? intent)
public override void OnPageSelected(int position)
{
base.OnNewIntent(intent);
thiz.OnPageSelected(position);
}
}
if (binding == null || fragments == null) return;
var action = intent?.Action;
if (!string.IsNullOrWhiteSpace(action))
protected override void OnNewIntent(Intent? intent)
{
base.OnNewIntent(intent);
if (binding == null || fragments == null) return;
var action = intent?.Action;
if (!string.IsNullOrWhiteSpace(action))
{
if (Enum.TryParse<TabItemViewModel.TabItemId>(action, out var tabItem))
{
if (Enum.TryParse<TabItemViewModel.TabItemId>(action, out var tabItem))
for (int i = 0; i < fragments!.Length; i++)
{
for (int i = 0; i < fragments!.Length; i++)
if (fragments[i].tabItemId == tabItem)
{
if (fragments[i].tabItemId == tabItem)
{
Navigate(i);
}
Navigate(i);
}
}
}
}
}
int ViewPagerAdapter.IHost.ItemCount => fragments!.Length;
int ViewPagerAdapter.IHost.ItemCount => fragments!.Length;
Fragment ViewPagerAdapter.IHost.CreateFragment(int position) => (Fragment)Activator.CreateInstance(fragments![position].fragmentType);
Fragment ViewPagerAdapter.IHost.CreateFragment(int position) => (Fragment)Activator.CreateInstance(fragments![position].fragmentType);
bool NavigationBarView.IOnItemSelectedListener.OnNavigationItemSelected(IMenuItem item)
bool NavigationBarView.IOnItemSelectedListener.OnNavigationItemSelected(IMenuItem item)
{
if (binding == null || fragments == null) return false;
var itemId = item.ItemId;
for (int i = 0; i < fragments!.Length; i++)
{
if (binding == null || fragments == null) return false;
var itemId = item.ItemId;
for (int i = 0; i < fragments!.Length; i++)
if (fragments[i].menuId == itemId)
{
if (fragments[i].menuId == itemId)
{
Navigate(i);
return true;
}
Navigate(i);
return true;
}
return false;
}
return false;
}
void Navigate(int position)
{
binding!.viewPager2.SetCurrentItem(position, false);
SetTitleByPosition(position);
SetChecked(position);
}
void Navigate(int position)
{
binding!.viewPager2.SetCurrentItem(position, false);
SetTitleByPosition(position);
SetChecked(position);
}
void SetTitleByPosition(int position)
{
var title = fragments![position].getTitle();
Title = title;
}
void SetTitleByPosition(int position)
{
var title = fragments![position].getTitle();
Title = title;
}
void OnPageSelected(int position)
{
if (binding == null) return;
SetTitleByPosition(position);
SetChecked(position);
}
void OnPageSelected(int position)
{
if (binding == null) return;
SetTitleByPosition(position);
SetChecked(position);
}
void SetChecked(int position)
{
var menuItem = binding!.nav_view.Menu.FindItem(fragments![position].menuId);
if (menuItem != null && !menuItem.IsChecked) menuItem.SetChecked(true);
}
void SetChecked(int position)
{
var menuItem = binding!.nav_view.Menu.FindItem(fragments![position].menuId);
if (menuItem != null && !menuItem.IsChecked) menuItem.SetChecked(true);
}
}
#endif
@@ -1,3 +1,4 @@
#if !NET6_0_MAUI_LIB
using Android.App;
using Android.Content.PM;
using Android.OS;
@@ -5,31 +6,31 @@ using Android.Runtime;
using Android.Views;
using System.Application.Security;
namespace System.Application.UI.Activities
{
/// <summary>
/// 页面 - 启动屏幕
/// </summary>
[Register(JavaPackageConstants.Activities + nameof(SplashActivity))]
[Activity(Theme = ManifestConstants.MainTheme2_Splash,
MainLauncher = true,
LaunchMode = LaunchMode.SingleTask,
ConfigurationChanges = ManifestConstants.ConfigurationChanges,
NoHistory = true)]
internal sealed class SplashActivity : Activity
{
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
//if (!DeviceSecurityCheckUtil.IsAllowStart(this)) return;
StartActivity(IAndroidApplication.Instance.MainActivityType);
OverridePendingTransition(Resource.Animation.abc_fade_in, Resource.Animation.abc_fade_out);
}
namespace System.Application.UI.Activities;
public override void OnBackPressed()
{
GoToPlatformPages.MockHomePressed(this);
return;
}
/// <summary>
/// 页面 - 启动屏幕
/// </summary>
[Register(JavaPackageConstants.Activities + nameof(SplashActivity))]
[Activity(Theme = ManifestConstants.MainTheme2_Splash,
MainLauncher = true,
LaunchMode = LaunchMode.SingleTask,
ConfigurationChanges = ManifestConstants.ConfigurationChanges,
NoHistory = true)]
internal sealed class SplashActivity : Activity
{
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
//if (!DeviceSecurityCheckUtil.IsAllowStart(this)) return;
StartActivity(IAndroidApplication.Instance.MainActivityType);
OverridePendingTransition(Resource.Animation.abc_fade_in, Resource.Animation.abc_fade_out);
}
}
public override void OnBackPressed()
{
GoToPlatformPages.MockHomePressed(this);
return;
}
}
#endif
@@ -2,7 +2,11 @@ using Android.Views;
using Binding;
using ReactiveUI;
using System.Application.UI.ViewModels;
#if NET6_0_OR_GREATER
using Microsoft.Maui.ApplicationModel;
#else
using Xamarin.Essentials;
#endif
using TViewHolder = System.Application.UI.Adapters.FastLoginChannelViewHolder;
using TViewModel = System.Application.UI.ViewModels.LoginOrRegisterWindowViewModel.FastLoginChannelViewModel;
@@ -1,14 +1,18 @@
using Android.Views;
using Binding;
using ReactiveUI;
using System.Threading.Tasks;
using System.Application.UI.Resx;
using System.Application.UI.ViewModels;
using System.Application.UI.Activities;
using static System.Application.UI.Resx.AppResources;
#if NET6_0_OR_GREATER
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
#else
using XEPlatform = Xamarin.Essentials.Platform;
#endif
using TViewHolder = System.Application.UI.Adapters.SteamAuthTradeConfirmationViewHolder;
using TViewModel = WinAuth.WinAuthSteamClient.Confirmation;
using static System.Application.UI.Resx.AppResources;
using System.Application.UI.Activities;
using XEPlatform = Xamarin.Essentials.Platform;
using System.Threading.Tasks;
namespace System.Application.UI.Adapters
{
@@ -13,7 +13,11 @@ using System.Collections.Generic;
using System.Linq;
using System.Reactive.Disposables;
using System.Reflection;
#if NET6_0_OR_GREATER
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
#else
using XEPlatform = Xamarin.Essentials.Platform;
#endif
// ReSharper disable once CheckNamespace
namespace System.Application.UI.Activities
@@ -13,7 +13,11 @@ using System.Application.UI.Adapters;
using System.Application.UI.Resx;
using System.Application.UI.ViewModels;
using System.Text;
#if NET6_0_OR_GREATER
using Microsoft.Maui.ApplicationModel;
#else
using Xamarin.Essentials;
#endif
using static System.Application.UI.ViewModels.AboutPageViewModel;
using M = System.Application.UI.ViewModels.AboutPageViewModel;
using V = Binding.fragment_about;
@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
#if !NET6_0_MAUI_LIB
using AndroidApplication = Android.App.Application;
namespace System.Application.UI
@@ -10,3 +8,4 @@ namespace System.Application.UI
}
}
#endif
@@ -1,3 +1,4 @@
#if !NET6_0_MAUI_LIB
using System.Application.Services;
using System.Application.UI.Activities;
using System.Collections.Generic;
@@ -32,3 +33,4 @@ namespace System.Application.UI
IApplication IApplication.IProgramHost.Application => this;
}
}
#endif
+3 -1
View File
@@ -1,3 +1,4 @@
#if !NET6_0_MAUI_LIB
using Android.App;
using Android.Runtime;
using System.Application.UI.Resx;
@@ -286,4 +287,5 @@ namespace System.Application.UI
void IApplication.PlatformInitSettingSubscribe() => PlatformInitSettingSubscribe();
}
}
}
#endif
+16 -13
View File
@@ -1,24 +1,27 @@
using Android.Util;
using AndroidX.SwipeRefreshLayout.Widget;
#if NET6_0_OR_GREATER
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
#else
using XEPlatform = Xamarin.Essentials.Platform;
#endif
namespace System.Application.UI
namespace System.Application.UI;
public static class StylesEx
{
public static class Styles
public static void InitDefaultStyles(this SwipeRefreshLayout swipeRefreshLayout)
{
public static void InitDefaultStyles(this SwipeRefreshLayout swipeRefreshLayout)
{
swipeRefreshLayout.SetColorSchemeColors(new[] { ColorPrimary });
}
swipeRefreshLayout.SetColorSchemeColors(new[] { ColorPrimary });
}
public static int ColorPrimary
public static int ColorPrimary
{
get
{
get
{
using TypedValue typedValue = new();
XEPlatform.CurrentActivity.Theme!.ResolveAttribute(Resource.Attribute.colorPrimary, typedValue, true);
return typedValue.Data;
}
using TypedValue typedValue = new();
XEPlatform.CurrentActivity.Theme!.ResolveAttribute(Resource.Attribute.colorPrimary, typedValue, true);
return typedValue.Data;
}
}
}
@@ -6,7 +6,11 @@ using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using AndroidAppActivity = Android.App.Activity;
#if NET6_0_OR_GREATER
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
#else
using XEPlatform = Xamarin.Essentials.Platform;
#endif
using AndroidAppResult = Android.App.Result;
// 将 onActivityResult() 转换为 System.Threading.Tasks.Task
@@ -1,14 +1,29 @@
<Project Sdk="MSBuild.Sdk.Extras">
<PropertyGroup>
<!--<TargetFrameworks>MonoAndroid12.0;Xamarin.iOS10;net6.0-android;net6.0-windows</TargetFrameworks>-->
<TargetFrameworks>MonoAndroid12.0;Xamarin.iOS10;net6.0-windows</TargetFrameworks>
<TargetFrameworks>MonoAndroid12.0;Xamarin.iOS10;net6.0-android;net6.0-windows;net6.0-ios;net6.0-maccatalyst</TargetFrameworks>
<RootNamespace>System.Application.AppCenter</RootNamespace>
<AssemblyName>System.Application.SteamTools.Client.AppCenter</AssemblyName>
<ImplicitUsings>enable</ImplicitUsings>
<AndroidUseIntermediateDesignerFile>false</AndroidUseIntermediateDesignerFile>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
<SupportedOSPlatformVersion>14.2</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">
<SupportedOSPlatformVersion>14.0</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
<SupportedOSPlatformVersion>21.0</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
<TargetPlatformMinVersion>7.0</TargetPlatformMinVersion>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Common.CoreLib\Properties\AssemblyInfo.cs">
<Link>Properties\AssemblyInfo.cs</Link>
@@ -23,17 +38,17 @@
<Link>Properties\AssemblyInfo.OS.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AppCenter" />
<PackageReference Include="Microsoft.AppCenter.Analytics" />
<PackageReference Include="Microsoft.AppCenter.Crashes" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ST.Client.Android.ResSecrets\ST.Client.Android.ResSecrets.csproj" Condition=" $(TargetFramework.StartsWith('MonoAndroid')) OR $(TargetFramework.Contains('-android')) " />
<ProjectReference Include="..\ST.Client.iOS.ResSecrets\ST.Client.iOS.ResSecrets.csproj" Condition=" $(TargetFramework.StartsWith('Xamarin.iOS')) OR $(TargetFramework.Contains('-ios')) " />
@@ -11,7 +11,7 @@
<Identity
Name="4651ED44255E.47979655102CE"
Publisher="CN=A90E406B-B2D3-4A23-B061-0FA1D65C4F66"
Version="2.8.3.0" />
Version="2.8.4.0" />
<Properties>
<DisplayName>Watt Toolkit</DisplayName>
@@ -18,6 +18,7 @@
<WindowsSdkPackageVersion>10.0.19041.24</WindowsSdkPackageVersion>
<UseWindowsForms Condition="'$(Configuration)'=='Debug'">true</UseWindowsForms>
<UseWPF>false</UseWPF>
<DefineConstants>AVALONIA;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
@@ -44,7 +45,7 @@
.allowedextension
</AllowedReferenceRelatedFileExtensions>
</PropertyGroup>
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<RuntimeHostConfigurationOption Include="System.Net.SocketsHttpHandler.Http3Support" Value="true" />
</ItemGroup>
@@ -4,6 +4,7 @@ using System.Application.Services;
using Program = System.Application.UI.MauiProgram;
#endif
// ReSharper disable once CheckNamespace
namespace System.Application.UI;
static partial class
@@ -9,6 +9,7 @@
<RootNamespace>System.Application.UI</RootNamespace>
<AssemblyName>Steam++</AssemblyName>
<AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
<DefineConstants>AVALONIA;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
@@ -0,0 +1,57 @@
using System.Application.Services;
using System.Application.Settings;
using System.Application.UI.ViewModels;
using System.Application.UI.Views;
#if !MAUI
using System.Application.UI.Views.Windows;
#endif
namespace System.Application.UI;
partial class App
{
void HandlerViewModelManager(IViewModelManager vmService)
{
switch (vmService.MainWindow)
{
case CloudArchiveWindowViewModel:
ProgramHost.IsMinimize = false;
#if !MAUI
MainWindow = new CloudArchiveWindow();
#endif
break;
case AchievementWindowViewModel:
ProgramHost.IsMinimize = false;
#if !MAUI
MainWindow = new AchievementWindow();
#endif
break;
default:
#region 主窗口启动时加载的资源
#if !UI_DEMO
compositeDisposable.Add(SettingsHost.Save);
compositeDisposable.Add(ProxyService.Current.Exit);
if (IApplication.IsDesktopPlatform)
{
compositeDisposable.Add(SteamConnectService.Current.Dispose);
if (GeneralSettings.IsStartupAppMinimized.Value)
ProgramHost.IsMinimize = true;
}
compositeDisposable.Add(ASFService.Current.StopASF);
#endif
#endregion
#if !MAUI
MainWindow = new MainWindow();
#endif
break;
}
#if !MAUI
MainWindow.DataContext = vmService.MainWindow;
#else
var mainWindowVM = (MainWindowViewModel?)vmService.MainWindow;
MainPage = new AppShell(mainWindowVM.ThrowIsNull());
#endif
}
}
@@ -199,38 +199,7 @@ namespace System.Application.UI
/*if (isTrace) */StartWatchTrace.Record("App.LoadXaml");
#endif
Name = ThisAssembly.AssemblyTrademark;
ProgramHost.OnCreateAppExecuted(handlerViewModelManager: vmService =>
{
switch (vmService.MainWindow)
{
case CloudArchiveWindowViewModel:
ProgramHost.IsMinimize = false;
MainWindow = new CloudArchiveWindow();
break;
case AchievementWindowViewModel:
ProgramHost.IsMinimize = false;
MainWindow = new AchievementWindow();
break;
default:
#region 主窗口启动时加载的资源
#if !UI_DEMO
compositeDisposable.Add(SettingsHost.Save);
compositeDisposable.Add(ProxyService.Current.Exit);
compositeDisposable.Add(SteamConnectService.Current.Dispose);
compositeDisposable.Add(ASFService.Current.StopASF);
#pragma warning disable CA1416 // 验证平台兼容性
if (GeneralSettings.IsStartupAppMinimized.Value)
ProgramHost.IsMinimize = true;
#pragma warning restore CA1416 // 验证平台兼容性
#endif
#endregion
MainWindow = new MainWindow();
break;
}
MainWindow.DataContext = vmService.MainWindow;
}, isTrace: isTrace);
ProgramHost.OnCreateAppExecuted(handlerViewModelManager: HandlerViewModelManager, isTrace: isTrace);
LiveCharts.Configure(config =>
{
@@ -1,27 +1,25 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
using System.Application.Services;
#if MAUI
using WindowManagerImpl_ = System.Application.Services.Implementation.MauiWindowManagerImpl;
using WindowManagerImpl = System.Application.Services.Implementation.MauiWindowManagerImpl;
#elif __ANDROID__
using WindowManagerImpl_ = System.Application.Services.Implementation.AndroidWindowManagerImpl;
using WindowManagerImpl = System.Application.Services.Implementation.AndroidWindowManagerImpl;
#elif AVALONIA
using WindowManagerImpl_ = System.Application.Services.Implementation.AvaloniaWindowManagerImpl;
using WindowManagerImpl = System.Application.Services.Implementation.AvaloniaWindowManagerImpl;
#endif
// ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.DependencyInjection
namespace Microsoft.Extensions.DependencyInjection;
public static partial class ServiceCollectionExtensions
{
public static partial class ServiceCollectionExtensions
/// <summary>
/// 添加窗口管理服务
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddWindowManager(this IServiceCollection services)
{
/// <summary>
/// 添加窗口管理服务
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection TryAddWindowManager(this IServiceCollection services)
{
services.TryAddSingleton<IWindowManager, WindowManagerImpl_>();
return services;
}
services.AddSingleton<IWindowManager, WindowManagerImpl>();
return services;
}
}
+13 -6
View File
@@ -1,12 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="MSBuild.Sdk.Extras">
<PropertyGroup>
<!--<TargetFramework>net6.0-macos</TargetFramework>-->
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;net6.0-macos;net6.0-maccatalyst</TargetFrameworks>
<RootNamespace>System.Application</RootNamespace>
<AssemblyName>System.Application.SteamTools.Client.Mac</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
<SupportedOSPlatformVersion>14.2</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">
<SupportedOSPlatformVersion>14.0</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition=" $(TargetFramework.StartsWith('Xamarin.Mac20')) OR $(TargetFramework.Contains('-mac')) ">
<DefineConstants>XAMARIN_MAC;$(DefineConstants)</DefineConstants>
</PropertyGroup>
@@ -41,15 +48,15 @@
<!--<PackageReference Include="Gameloop.Vdf" />-->
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('Xamarin.Mac20')) OR $(TargetFramework.Contains('-mac')) ">
<!--<ItemGroup Condition=" ($(TargetFramework.StartsWith('Xamarin.Mac20')) OR $(TargetFramework.Contains('-mac'))) AND !$(TargetFramework.EndsWith('.0')) ">
<ProjectReference Include="..\ST.Client.AppCenter\ST.Client.AppCenter.csproj" />
</ItemGroup>
</ItemGroup>-->
<ItemGroup Condition=" $(TargetFramework.StartsWith('netstandard')) OR $(TargetFramework.EndsWith('net5.0')) OR $(TargetFramework.EndsWith('net6.0')) ">
<PackageReference Include="MonoMac.NetStandard" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\references\Gameloop.Vdf\Gameloop.Vdf\Gameloop.Vdf.csproj" />
<ProjectReference Include="..\..\references\SteamAchievementManager\SAM.API\SAM.API.csproj" />
@@ -15,7 +15,9 @@ namespace Microsoft.Extensions.DependencyInjection
if (OperatingSystem2.IsMacOS())
{
services.AddSingleton<IHttpPlatformHelperService, MacClientHttpPlatformHelperServiceImpl>();
#if !MACCATALYST
services.AddSingleton(AppDelegate.Instance!);
#endif
services.AddSingleton<MacPlatformServiceImpl>();
services.AddSingleton<IPlatformService>(s => s.GetRequiredService<MacPlatformServiceImpl>());
services.AddSingleton<IEmailPlatformService>(s => s.GetRequiredService<MacPlatformServiceImpl>());
@@ -7,32 +7,35 @@ using Foundation;
#endif
using System.Threading.Tasks;
namespace System.Application.Services.Implementation
namespace System.Application.Services.Implementation;
partial class MacPlatformServiceImpl : IEmailPlatformService
{
partial class MacPlatformServiceImpl : IEmailPlatformService
// https://github.com/xamarin/Essentials/blob/main/Xamarin.Essentials/Email/Email.macos.cs
static T? InvokeOnMainThread<T>(Func<T> factory)
{
// https://github.com/xamarin/Essentials/blob/main/Xamarin.Essentials/Email/Email.macos.cs
T? value = default;
NSRunLoop.Main.InvokeOnMainThread(() => value = factory());
return value;
}
static T? InvokeOnMainThread<T>(Func<T> factory)
public Task PlatformComposeAsync(EmailMessage? message)
{
#if MACCATALYST
throw new PlatformNotSupportedException();
#else
var isComposeSupported = InvokeOnMainThread(() => NSWorkspace.SharedWorkspace.UrlForApplication(NSUrl.FromString("mailto:")) != null);
if (!isComposeSupported)
{
T? value = default;
NSRunLoop.Main.InvokeOnMainThread(() => value = factory());
return value;
throw new NotSupportedException();
}
public Task PlatformComposeAsync(EmailMessage? message)
{
var isComposeSupported = InvokeOnMainThread(() => NSWorkspace.SharedWorkspace.UrlForApplication(NSUrl.FromString("mailto:")) != null);
if (!isComposeSupported)
{
throw new NotSupportedException();
}
var url = Email2.GetMailToUri(message);
var url = Email2.GetMailToUri(message);
using var nsurl = NSUrl.FromString(url);
NSWorkspace.SharedWorkspace.OpenUrl(nsurl);
return Task.CompletedTask;
}
using var nsurl = NSUrl.FromString(url);
NSWorkspace.SharedWorkspace.OpenUrl(nsurl);
return Task.CompletedTask;
#endif
}
}
@@ -55,7 +55,7 @@ namespace System.Application.Services.Implementation
internal static bool IsCertificateInstalled(X509Certificate2 certificate2)
{
#if MONO_MAC
//#if MONO_MAC
using var p = new Process();
p.StartInfo.FileName = "security";
p.StartInfo.Arguments = $" verify-cert -c \"{IReverseProxyService.Instance.CertificateManager.GetCerFilePathGeneratedWhenNoFileExists()}\"";
@@ -65,23 +65,23 @@ namespace System.Application.Services.Implementation
var returnStr = p.StandardOutput.ReadToEnd().TrimEnd();
p.Kill();
return returnStr.Contains("...certificate verification successful.", StringComparison.OrdinalIgnoreCase);
#elif XAMARIN_MAC
bool result = false;
var scer = new SecCertificate(cer);
var addCertificate = new SecRecord(scer);
var cerTrust = SecKeyChain.QueryAsRecord(addCertificate, out var t2code);
if (cerTrust != SecStatusCode.ItemNotFound)
{
using (var trust = new SecTrust(cerTrust, null))
{
trust.SetPolicy(policy);
trust.SetAnchorCertificates(fcollection);
result=trust.Evaluate(out var error);
Toast.Show(error.Description);
}
}
return result;
#endif
//#elif XAMARIN_MAC
// bool result = false;
// var scer = new SecCertificate(cer);
// var addCertificate = new SecRecord(scer);
// var cerTrust = SecKeyChain.QueryAsRecord(addCertificate, out var t2code);
// if (cerTrust != SecStatusCode.ItemNotFound)
// {
// using (var trust = new SecTrust(cerTrust, null))
// {
// trust.SetPolicy(policy);
// trust.SetAnchorCertificates(fcollection);
// result = trust.Evaluate(out var error);
// Toast.Show(error.Description);
// }
// }
// return result;
//#endif
}
@@ -182,7 +182,14 @@ namespace System.Application.Services.Implementation
Toast.Show(msg);
}
public static void OpenFile(string appName, string filePath) => NSWorkspace.SharedWorkspace.OpenFile(filePath, appName);
public static void OpenFile(string appName, string filePath)
{
#if MACCATALYST
throw new PlatformNotSupportedException();
#else
NSWorkspace.SharedWorkspace.OpenFile(filePath, appName);
#endif
}
public void SetSystemSessionEnding(Action action)
{
@@ -191,13 +198,21 @@ namespace System.Application.Services.Implementation
public void OpenFolderByDirectoryPath(DirectoryInfo info)
{
#if MACCATALYST
throw new PlatformNotSupportedException();
#else
//NSWorkspace.SharedWorkspace.SelectFile(string.Empty, info.FullName);
NSWorkspace.SharedWorkspace.ActivateFileViewer(new[] { new NSUrl(info.FullName, isDir: true) });
#endif
}
public void OpenFolderSelectFilePath(FileInfo info)
{
#if MACCATALYST
throw new PlatformNotSupportedException();
#else
NSWorkspace.SharedWorkspace.ActivateFileViewer(new[] { new NSUrl(info.FullName, isDir: false) });
#endif
}
public string? GetFileName(TextReaderProvider provider) => provider switch
+127 -124
View File
@@ -7,136 +7,139 @@ using AppKit;
using Foundation;
#endif
namespace System.Application.UI
#if !MACCATALYST
namespace System.Application.UI;
/// <summary>
/// This class is an AppDelegate helper specifically for Mac OSX
/// Int it's infinite wisdom and unlike Linux and or Windows Mac does not pass in the URL from a sqrl:// invokation
/// directly as a startup app paramter, instead it uses a System Event to do this which has to be registered
/// and listed to.
/// This requires us to use Xamarin.Mac to make it work with .net standard
/// </summary>
[Register("AppDelegate")]
public class AppDelegate : NSApplicationDelegate
{
/// <summary>
/// This class is an AppDelegate helper specifically for Mac OSX
/// Int it's infinite wisdom and unlike Linux and or Windows Mac does not pass in the URL from a sqrl:// invokation
/// directly as a startup app paramter, instead it uses a System Event to do this which has to be registered
/// and listed to.
/// This requires us to use Xamarin.Mac to make it work with .net standard
/// </summary>
[Register("AppDelegate")]
public class AppDelegate : NSApplicationDelegate
public AppDelegate()
{
public AppDelegate()
}
/// <summary>
/// Because we are creating our own mac application delegate we are removing / overriding
/// the one that Avalonia creates. This causes the application to not be handled as it should.
/// This is the Avalonia Implementation: https://github.com/AvaloniaUI/Avalonia/blob/5a2ef35dacbce0438b66d9f012e5f629045beb3d/native/Avalonia.Native/src/OSX/app.mm
/// So what we are doing here is re-creating this implementation to mimick their behavior.
/// </summary>
/// <param name="notification"></param>
public override void WillFinishLaunching(NSNotification notification)
{
if (NSApplication.SharedApplication.ActivationPolicy != NSApplicationActivationPolicy.Regular)
{
foreach (var x in NSRunningApplication.GetRunningApplications(@"com.apple.dock"))
{
x.Activate(NSApplicationActivationOptions.ActivateIgnoringOtherWindows);
break;
}
NSApplication.SharedApplication.ActivationPolicy = NSApplicationActivationPolicy.Regular;
}
/// <summary>
/// Because we are creating our own mac application delegate we are removing / overriding
/// the one that Avalonia creates. This causes the application to not be handled as it should.
/// This is the Avalonia Implementation: https://github.com/AvaloniaUI/Avalonia/blob/5a2ef35dacbce0438b66d9f012e5f629045beb3d/native/Avalonia.Native/src/OSX/app.mm
/// So what we are doing here is re-creating this implementation to mimick their behavior.
/// </summary>
/// <param name="notification"></param>
public override void WillFinishLaunching(NSNotification notification)
Console.WriteLine("开始注册通知");
try
{
if (NSApplication.SharedApplication.ActivationPolicy != NSApplicationActivationPolicy.Regular)
{
foreach (var x in NSRunningApplication.GetRunningApplications(@"com.apple.dock"))
{
x.Activate(NSApplicationActivationOptions.ActivateIgnoringOtherWindows);
break;
}
NSApplication.SharedApplication.ActivationPolicy = NSApplicationActivationPolicy.Regular;
}
Console.WriteLine("开始注册通知");
try
{
var manager = NSAppleEventManager.SharedAppleEventManager;
manager.SetEventHandler(this,
new MonoMac.ObjCRuntime.Selector("handleGetURLEvent:withReplyEvent:"),
AEEventClass.Internet,
AEEventID.GetUrl);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
var manager = NSAppleEventManager.SharedAppleEventManager;
manager.SetEventHandler(this,
new("handleGetURLEvent:withReplyEvent:"),
AEEventClass.Internet,
AEEventID.GetUrl);
}
#if MONO_MAC
[Export("handleGetURLEvent:withReplyEvent:")]
private void HandleGetURLEvent(NSAppleEventDescriptor descriptor, NSAppleEventDescriptor replyEvent)
catch (Exception e)
{
try
{
// stackoverflow.com/questions/1945
// https://forums.xamarin.com/discussion/9774/custom-url-schema-handling
var keyDirectObject = "----";
var keyword =
((uint)keyDirectObject[0]) << 24 |
((uint)keyDirectObject[1]) << 16 |
((uint)keyDirectObject[2]) << 8 |
((uint)keyDirectObject[3]);
var urlString = descriptor.ParamDescriptorForKeyword(keyword).StringValue;
using (var alert = new NSAlert())
{
alert.MessageText = urlString;
alert.RunSheetModal(null);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
#elif XAMARIN_MAC
public override void OpenUrls(NSApplication application, NSUrl[] urls)
{
throw new System.Exception("opened with: " + urls[0].AbsoluteString);
}
#endif
/// <summary>
/// Because we are creating our own mac application delegate we are removing / overriding
/// the one that Avalonia creates. This causes the application to not be handled as it should.
/// This is the Avalonia Implementation: https://github.com/AvaloniaUI/Avalonia/blob/5a2ef35dacbce0438b66d9f012e5f629045beb3d/native/Avalonia.Native/src/OSX/app.mm
/// So what we are doing here is re-creating this implementation to mimick their behavior.
/// </summary>
/// <param name="notification"></param>
public override void DidFinishLaunching(NSNotification notification)
{
#if !NETSTANDARD && !NET5_0 && !NET6_0
VisualStudioAppCenterSDK.Init();
#endif
NSRunningApplication.CurrentApplication.Activate(NSApplicationActivationOptions.ActivateIgnoringOtherWindows);
}
public override bool ApplicationShouldHandleReopen(NSApplication sender, bool hasVisibleWindows)
{
if (hasVisibleWindows)
{
return false;
}
else
{
// 点击 Dock 重新打开已关闭的窗口
IApplication.Instance.RestoreMainWindow();
return true;
}
}
static bool isInitialized;
internal static AppDelegate? Instance { get; private set; }
public static void Init(string[] args)
{
if (!isInitialized)
{
isInitialized = true;
NSApplication.Init();
//NSApplication.Main(args);
Instance = new();
NSApplication.SharedApplication.Delegate = Instance;
}
Console.WriteLine(e.Message);
}
}
}
#if MONO_MAC
[Export("handleGetURLEvent:withReplyEvent:")]
private void HandleGetURLEvent(NSAppleEventDescriptor descriptor, NSAppleEventDescriptor replyEvent)
{
try
{
// stackoverflow.com/questions/1945
// https://forums.xamarin.com/discussion/9774/custom-url-schema-handling
var keyDirectObject = "----";
var keyword =
((uint)keyDirectObject[0]) << 24 |
((uint)keyDirectObject[1]) << 16 |
((uint)keyDirectObject[2]) << 8 |
((uint)keyDirectObject[3]);
var urlString = descriptor.ParamDescriptorForKeyword(keyword).StringValue;
using (var alert = new NSAlert())
{
alert.MessageText = urlString;
alert.RunSheetModal(null);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
#elif XAMARIN_MAC
public override void OpenUrls(NSApplication application, NSUrl[] urls)
{
throw new System.Exception("opened with: " + urls[0].AbsoluteString);
}
#endif
/// <summary>
/// Because we are creating our own mac application delegate we are removing / overriding
/// the one that Avalonia creates. This causes the application to not be handled as it should.
/// This is the Avalonia Implementation: https://github.com/AvaloniaUI/Avalonia/blob/5a2ef35dacbce0438b66d9f012e5f629045beb3d/native/Avalonia.Native/src/OSX/app.mm
/// So what we are doing here is re-creating this implementation to mimick their behavior.
/// </summary>
/// <param name="notification"></param>
public override void DidFinishLaunching(NSNotification notification)
{
#if !NETSTANDARD && !NET5_0 && !NET6_0
VisualStudioAppCenterSDK.Init();
#endif
NSRunningApplication.CurrentApplication.Activate(NSApplicationActivationOptions.ActivateIgnoringOtherWindows);
}
public override bool ApplicationShouldHandleReopen(NSApplication sender, bool hasVisibleWindows)
{
if (hasVisibleWindows)
{
return false;
}
else
{
// 点击 Dock 重新打开已关闭的窗口
IApplication.Instance.RestoreMainWindow();
return true;
}
}
static bool isInitialized;
internal static AppDelegate? Instance { get; private set; }
public static void Init(string[] args)
{
if (!isInitialized)
{
isInitialized = true;
NSApplication.Init();
//NSApplication.Main(args);
Instance = new();
NSApplication.SharedApplication.Delegate = Instance;
}
}
}
#endif
@@ -31,11 +31,24 @@ namespace System.Application.Services.Implementation
}
public async Task<byte[]> ProtectAsync(byte[] data)
{
try
{
return await ProtectCoreAsync(data);
}
catch
{
return await ProtectCore2Async(data);
}
}
static async Task<byte[]> ProtectCoreAsync(byte[] data)
{
// LOCAL=user and LOCAL=machine do not require enterprise auth capability
var provider = GetDataProtectionProvider("LOCAL=user");
// https://appcenter.ms/orgs/BeyondDimension/apps/Steam/crashes/errors/842356268u/overview
// https://appcenter.ms/orgs/BeyondDimension/apps/Steam/crashes/errors/1550045210u/overview
// System.Runtime.InteropServices.COMException: 无法在设置线程模式后对其加以更改。 (0x80010106 (RPC_E_CHANGED_MODE))
var buffer = await provider.ProtectAsync(data.AsBuffer());
@@ -45,7 +58,9 @@ namespace System.Application.Services.Implementation
return encBytes;
}
public async Task<byte[]> UnprotectAsync(byte[] data)
static async Task<byte[]> ProtectCore2Async(byte[] data) => await await Task.Factory.StartNew(async () => await ProtectCoreAsync(data));
static async Task<byte[]> UnprotectCoreAsync(byte[] data)
{
var provider = GetDataProtectionProvider();
@@ -53,5 +68,19 @@ namespace System.Application.Services.Implementation
return buffer.ToArray();
}
static async Task<byte[]> UnprotectCore2Async(byte[] data) => await await Task.Factory.StartNew(async () => await UnprotectCoreAsync(data));
public async Task<byte[]> UnprotectAsync(byte[] data)
{
try
{
return await UnprotectCoreAsync(data);
}
catch
{
return await UnprotectCore2Async(data);
}
}
}
}
@@ -2,6 +2,11 @@ namespace System.Application.Services.Implementation
{
partial class WindowsPlatformServiceImpl : IDisposable
{
/// <summary>
/// 用于 <see cref="IOPath.GetCacheFilePath(string, string, string)"/> 中 dirName,临时文件夹名称,在程序退出时将删除整个文件夹
/// </summary>
const string CacheTempDirName = "Temporary";
bool disposedValue;
private void Dispose(bool disposing)
@@ -12,6 +17,8 @@ namespace System.Application.Services.Implementation
{
// TODO: 释放托管状态(托管对象)
isLightOrDarkThemeWatch?.Dispose();
IOPath.TryDeleteCacheSubDir(CacheTempDirName);
}
// TODO: 释放未托管的资源(未托管的对象)并重写终结器
@@ -1,6 +1,5 @@
using Microsoft.Win32;
using System.Application.Models;
using System.Application.UI;
using System.Application.UI.Resx;
using System.Collections.Generic;
using System.ComponentModel;
@@ -11,343 +10,354 @@ using System.IO;
using System.Linq;
using System.Management;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Versioning;
using System.Runtime.InteropServices;
using System.Security.Principal;
using Windows.ApplicationModel;
using System.Application.Properties;
using System.Text;
using _SR = System.Application.Properties.SR;
namespace System.Application.Services.Implementation
namespace System.Application.Services.Implementation;
internal sealed partial class WindowsPlatformServiceImpl : IPlatformService
{
internal sealed partial class WindowsPlatformServiceImpl : IPlatformService
const string TAG = "WindowsPlatformS";
const string SteamRegistryPath = @"SOFTWARE\Valve\Steam";
public string? GetRegistryVdfPath() { return null; }
public string GetCommandLineArgs(Process process)
{
const string TAG = "WindowsPlatformS";
const string SteamRegistryPath = @"SOFTWARE\Valve\Steam";
public string? GetRegistryVdfPath() { return null; }
public string GetCommandLineArgs(Process process)
try
{
try
{
using var searcher = new ManagementObjectSearcher(
"SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + process.Id);
using var objects = searcher.Get();
var @object = objects.Cast<ManagementBaseObject>().SingleOrDefault();
return @object?["CommandLine"]?.ToString() ?? "";
}
catch (Win32Exception ex) when ((uint)ex.ErrorCode == 0x80004005)
{
// 没有对该进程的安全访问权限。
return string.Empty;
}
using var searcher = new ManagementObjectSearcher(
"SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + process.Id);
using var objects = searcher.Get();
var @object = objects.Cast<ManagementBaseObject>().SingleOrDefault();
return @object?["CommandLine"]?.ToString() ?? "";
}
static readonly Lazy<string> mHostsFilePath = new(() =>
catch (Win32Exception ex) when ((uint)ex.ErrorCode == 0x80004005)
{
return Path.Combine(Environment.SystemDirectory, "drivers", "etc", "hosts");
});
public string HostsFilePath => mHostsFilePath.Value;
public string DefaultHostsContent => _SR.hosts;
static string GetFolderPath(Environment.SpecialFolder folder)
{
switch (folder)
{
case Environment.SpecialFolder.ProgramFiles:
var trimEndMark = " (x86)";
var value = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
if (value.EndsWith(trimEndMark, StringComparison.OrdinalIgnoreCase))
{
var value2 = value.Substring(0, value.Length - trimEndMark.Length);
if (Directory.Exists(value2))
{
return value2;
}
}
return value;
default:
return Environment.GetFolderPath(folder);
}
// 没有对该进程的安全访问权限。
return string.Empty;
}
public void OpenFolderByDirectoryPath(DirectoryInfo info)
{
Process.Start(new ProcessStartInfo
{
FileName = Explorer,
Arguments = $"\"{info.FullName}\"",
UseShellExecute = false,
});
}
public void OpenFolderSelectFilePath(FileInfo info)
{
Process.Start(new ProcessStartInfo
{
FileName = Explorer,
Arguments = $"/select,\"{info.FullName}\"",
UseShellExecute = false,
});
}
public string? GetFileName(TextReaderProvider provider)
{
switch (provider)
{
case TextReaderProvider.VSCode:
var vsCodePaths = new[] {
GetFolderPath(Environment.SpecialFolder.ProgramFiles),
GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)
}.Distinct().Select(x => Path.Combine(x, "Microsoft VS Code", "Code.exe"));
foreach (var vsCodePath in vsCodePaths)
{
if (File.Exists(vsCodePath))
{
return vsCodePath;
}
}
return null;
case TextReaderProvider.Notepad:
return "notepad";
default:
return null;
}
}
public void SetSystemSessionEnding(Action action)
{
SystemEvents.SessionEnding += (sender, e) =>
{
//IDesktopAppService.Instance.CompositeDisposable.Dispose();
action.Invoke();
};
}
static string? GetFullPath(string s)
{
if (!string.IsNullOrWhiteSpace(s))
{
return Path.GetFullPath(s);
}
return null;
}
public string? GetSteamDirPath()
=> GetFullPath(Registry.CurrentUser.Read(SteamRegistryPath, "SteamPath"));
public string? GetSteamProgramPath()
=> GetFullPath(Registry.CurrentUser.Read(SteamRegistryPath, "SteamExe"));
public string GetLastSteamLoginUserName()
=> Registry.CurrentUser.Read(SteamRegistryPath, "AutoLoginUser");
public void SetCurrentUser(string userName)
{
if (DesktopBridge.IsRunningAsUwp)
{
var reg = $"Windows Registry Editor Version 5.00{Environment.NewLine}[HKEY_CURRENT_USER\\Software\\Valve\\Steam]{Environment.NewLine}\"AutoLoginUser\"=\"{userName}\"";
var path = Path.Combine(IOPath.CacheDirectory, "switchuser.reg");
File.WriteAllText(path, reg, Text.Encoding.UTF8);
var p = Process2.Start("regedit", $"/s \"{path}\"", true);
p?.WaitForExit();
}
else
{
Registry.CurrentUser.AddOrUpdate(SteamRegistryPath, "AutoLoginUser", userName, RegistryValueKind.String);
}
}
static string GetMachineSecretKey()
=> Registry.LocalMachine.Read(@"SOFTWARE\Microsoft\Cryptography", "MachineGuid");
static readonly Lazy<(byte[] key, byte[] iv)> mMachineSecretKey = IPlatformService.GetMachineSecretKey(GetMachineSecretKey);
public (byte[] key, byte[] iv) MachineSecretKey => mMachineSecretKey.Value;
//const string runas_exe = "runas.exe";
//static Process? StartAsInvokerByRunas(string fileName, string? arguments = null)
//{
// // https://blog.lindexi.com/post/%E5%A6%82%E4%BD%95%E5%9C%A8-RunAs-%E5%90%AF%E5%8A%A8%E7%9A%84%E8%BD%AF%E4%BB%B6%E4%BC%A0%E5%85%A5%E5%B8%A6%E7%A9%BA%E6%A0%BC%E7%9A%84%E8%B7%AF%E5%BE%84%E5%B8%A6%E7%A9%BA%E6%A0%BC%E5%8F%82%E6%95%B0.html
// string arguments_;
// if (string.IsNullOrEmpty(arguments))
// arguments_ = $"/trustlevel:0x20000 \"{fileName}\"";
// else arguments_ = $"/trustlevel:0x20000 \"{fileName} \"{arguments}\"\"";
// return Process.Start(runas_exe, arguments_);
//}
static readonly Lazy<string> _explorer_exe = new(() => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "explorer.exe"));
/// <summary>
/// %windir%\explorer.exe
/// </summary>
static string Explorer => _explorer_exe.Value;
static Process? StartAsInvokerByExplorer(string fileName, string? arguments = null)
{
if (string.IsNullOrEmpty(arguments))
return Process.Start(Explorer, $"\"{fileName}\"");
var cacheCmdFile = Path.Combine(IOPath.CacheDirectory, "StartAsInvokerByExplorer" + FileEx.CMD);
if (File.Exists(cacheCmdFile)) File.Delete(cacheCmdFile);
File.WriteAllText(cacheCmdFile, $"@echo {Constants.HARDCODED_APP_NAME} StartAsInvokerByExplorer{Environment.NewLine}start \"\" \"{fileName}\" {arguments}{Environment.NewLine}exit 0");
return Process.Start(Explorer, $"\"{cacheCmdFile}\"");
}
public Process? StartAsInvoker(string fileName, string? arguments = null)
{
if (((IPlatformService)this).IsAdministrator)
{
// runas /trustlevel:0x20000 没有真正的降权,只是作为具有限制特权,使用 explorer 最好,但不接受参数,可以创建一个临时cmd脚本启动
//return StartAsInvokerByRunas(fileName, arguments);
return StartAsInvokerByExplorer(fileName, arguments);
}
else
{
return Process2.Start(fileName, arguments);
}
}
//public Process? StartAsInvoker(ProcessStartInfo startInfo)
//{
// startInfo.FileName = runas_exe;
// startInfo.Arguments = $"/trustlevel:0x20000 \"{startInfo.FileName}\"";
// return Process.Start(startInfo);
//}
//public Process? GetProcessByPortOccupy(ushort port, bool isTCPorUDP = true)
//{
// if (isTCPorUDP)
// {
// return SocketHelper.GetProcessByTcpPort(port);
// }
// else
// {
// throw new NotSupportedException();
// }
// //try
// //{
// // using var p = new Process
// // {
// // StartInfo = new ProcessStartInfo
// // {
// // FileName = "cmd",
// // UseShellExecute = false,
// // RedirectStandardInput = true,
// // RedirectStandardOutput = true,
// // CreateNoWindow = true,
// // },
// // };
// // p.Start();
// // p.StandardInput.WriteLine($"netstat -ano|findstr \"{port}\"&exit");
// // p.StandardInput.AutoFlush = true;
// // var reader = p.StandardOutput;
// // while (!reader.EndOfStream)
// // {
// // var line = reader.ReadLine();
// // if (line == null) break;
// // var lineArray = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
// // if (lineArray.Length != 5) continue;
// // if (!lineArray[0].Equals(isTCPorUDP ? "TCP" : "UDP", StringComparison.OrdinalIgnoreCase)) continue;
// // if (!lineArray[3].Equals("LISTENING", StringComparison.OrdinalIgnoreCase)) continue;
// // if (!lineArray[1].EndsWith($":{port}")) continue;
// // if (!ushort.TryParse(lineArray[4], out var pid)) continue;
// // p.Close();
// // return Process.GetProcessById(pid);
// // }
// // _ = p.WaitForExit(550);
// //}
// //catch
// //{
// //}
// //return default;
//}
const string ProxyOverride = "\"ProxyOverride\"=\"localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*\"";
/// <summary>
/// 设置启用或关闭系统代理
/// </summary>
/// <param name="state"></param>
/// <param name="ip"></param>
/// <param name="port"></param>
/// <returns></returns>
public bool SetAsSystemProxy(bool state, IPAddress? ip, int port)
{
try
{
var proxyEnable = $"\"ProxyEnable\"=dword:{(state ? "00000001" : "00000000")}";
var hasIpAndProt = ip != null && port >= 0;
var proxyServer = hasIpAndProt ? $"\"proxyServer\"=\"{ip}:{port}\"" : "";
var reg = $"Windows Registry Editor Version 5.00{Environment.NewLine}[HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings]{Environment.NewLine}{proxyEnable}{Environment.NewLine}{(state ? $"{ProxyOverride}{Environment.NewLine}" : "")}{proxyServer}";
var path = Path.Combine(IOPath.CacheDirectory, "switchproxy.reg");
File.WriteAllText(path, reg, Text.Encoding.UTF8);
var p = Process2.Start("regedit", $"/s \"{path}\"", true);
p?.WaitForExit();
return true;
}
catch (Exception ex)
{
Toast.LogAndShowT(ex, TAG);
return false;
}
}
/// <summary>
/// 设置启用或关闭PAC代理
/// </summary>
/// <param name="state"></param>
/// <param name="url"></param>
/// <returns></returns>
public bool SetAsSystemPACProxy(bool state, string? url = null)
{
try
{
var regAutoConfigUrl = state ? $"\"AutoConfigURL\"=\"{url}\"" : "\"AutoConfigURL\"=\"\"";
var reg = $"Windows Registry Editor Version 5.00{Environment.NewLine}[HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings]{Environment.NewLine}{regAutoConfigUrl}";
var path = Path.Combine(IOPath.CacheDirectory, "switchpacproxy.reg");
File.WriteAllText(path, reg, Text.Encoding.UTF8);
var p = Process2.Start("regedit", $"/s \"{path}\"", true);
p?.WaitForExit();
return true;
}
catch (Exception ex)
{
Toast.LogAndShowT(ex, TAG);
return false;
}
}
public IReadOnlyCollection<KeyValuePair<string, string>> GetFontsByGdiPlus()
{
// https://docs.microsoft.com/zh-cn/typography/font-list
var culture = R.Culture;
InstalledFontCollection ifc = new();
var list = ifc.Families.Where(x => x.IsStyleAvailable(FontStyle.Regular)).Select(x => KeyValuePair.Create(x.GetName(culture.LCID), x.GetName(1033))).ToList();
list.Insert(0, IFontManager.Default);
return list;
}
#region IsInstall
static readonly Lazy<bool> _IsInstall = new(() =>
{
using var reg32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
var value = reg32.Read(@"SOFTWARE\Steam++", "InstPath").TrimEnd(Path.DirectorySeparatorChar);
return string.Equals(IOPath.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar), value, StringComparison.OrdinalIgnoreCase);
});
/// <inheritdoc cref="IPlatformService.IsInstall"/>
public static bool IsInstall => _IsInstall.Value;
bool IPlatformService.IsInstall => IsInstall;
#endregion
}
static readonly Lazy<string> mHostsFilePath = new(() =>
{
return Path.Combine(Environment.SystemDirectory, "drivers", "etc", "hosts");
});
public string HostsFilePath => mHostsFilePath.Value;
public string DefaultHostsContent => _SR.hosts;
static string GetFolderPath(Environment.SpecialFolder folder)
{
switch (folder)
{
case Environment.SpecialFolder.ProgramFiles:
var trimEndMark = " (x86)";
var value = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
if (value.EndsWith(trimEndMark, StringComparison.OrdinalIgnoreCase))
{
var value2 = value[..^trimEndMark.Length];
if (Directory.Exists(value2))
{
return value2;
}
}
return value;
default:
return Environment.GetFolderPath(folder);
}
}
public void OpenFolderByDirectoryPath(DirectoryInfo info)
{
StartProcessExplorer($"\"{info.FullName}\"");
}
public void OpenFolderSelectFilePath(FileInfo info)
{
StartProcessExplorer($"/select,\"{info.FullName}\"");
}
public string? GetFileName(TextReaderProvider provider)
{
switch (provider)
{
case TextReaderProvider.VSCode:
var vsCodePaths = new[] {
GetFolderPath(Environment.SpecialFolder.ProgramFiles),
GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)
}.Distinct().Select(x => Path.Combine(x, "Microsoft VS Code", "Code.exe"));
foreach (var vsCodePath in vsCodePaths)
{
if (File.Exists(vsCodePath))
{
return vsCodePath;
}
}
return null;
case TextReaderProvider.Notepad:
return "notepad";
default:
return null;
}
}
public void SetSystemSessionEnding(Action action)
{
SystemEvents.SessionEnding += (sender, e) =>
{
//IDesktopAppService.Instance.CompositeDisposable.Dispose();
action.Invoke();
};
}
static string? GetFullPath(string s)
{
if (!string.IsNullOrWhiteSpace(s))
{
return Path.GetFullPath(s);
}
return null;
}
public string? GetSteamDirPath()
=> GetFullPath(Registry.CurrentUser.Read(SteamRegistryPath, "SteamPath"));
public string? GetSteamProgramPath()
=> GetFullPath(Registry.CurrentUser.Read(SteamRegistryPath, "SteamExe"));
public string GetLastSteamLoginUserName()
=> Registry.CurrentUser.Read(SteamRegistryPath, "AutoLoginUser");
public void SetCurrentUser(string userName)
{
if (DesktopBridge.IsRunningAsUwp)
{
var reg = $"Windows Registry Editor Version 5.00{Environment.NewLine}[HKEY_CURRENT_USER\\Software\\Valve\\Steam]{Environment.NewLine}\"AutoLoginUser\"=\"{userName}\"";
var path = IOPath.GetCacheFilePath(CacheTempDirName, "SwitchSteamUser", FileEx.Reg);
File.WriteAllText(path, reg, Encoding.UTF8);
var p = StartProcessRegedit($"/s \"{path}\"");
IOPath.TryDeleteInDelay(p, path);
}
else
{
Registry.CurrentUser.AddOrUpdate(SteamRegistryPath, "AutoLoginUser", userName, RegistryValueKind.String);
}
}
static string GetMachineSecretKey()
=> Registry.LocalMachine.Read(@"SOFTWARE\Microsoft\Cryptography", "MachineGuid");
static readonly Lazy<(byte[] key, byte[] iv)> mMachineSecretKey = IPlatformService.GetMachineSecretKey(GetMachineSecretKey);
public (byte[] key, byte[] iv) MachineSecretKey => mMachineSecretKey.Value;
/// <summary>
/// %windir%
/// </summary>
static readonly Lazy<string> _windir = new(() => Environment.GetFolderPath(Environment.SpecialFolder.Windows));
//const string runas_exe = "runas.exe";
//static Process? StartAsInvokerByRunas(string fileName, string? arguments = null)
//{
// // https://blog.lindexi.com/post/%E5%A6%82%E4%BD%95%E5%9C%A8-RunAs-%E5%90%AF%E5%8A%A8%E7%9A%84%E8%BD%AF%E4%BB%B6%E4%BC%A0%E5%85%A5%E5%B8%A6%E7%A9%BA%E6%A0%BC%E7%9A%84%E8%B7%AF%E5%BE%84%E5%B8%A6%E7%A9%BA%E6%A0%BC%E5%8F%82%E6%95%B0.html
// string arguments_;
// if (string.IsNullOrEmpty(arguments))
// arguments_ = $"/trustlevel:0x20000 \"{fileName}\"";
// else arguments_ = $"/trustlevel:0x20000 \"{fileName} \"{arguments}\"\"";
// return Process.Start(runas_exe, arguments_);
//}
static readonly Lazy<string> _explorer_exe = new(() => Path.Combine(_windir.Value, "explorer.exe"));
/// <summary>
/// %windir%\explorer.exe
/// </summary>
static string Explorer => _explorer_exe.Value;
/// <summary>
/// 带参数(可选/null)启动 %windir%\explorer.exe
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
static Process? StartProcessExplorer(string args) => Process2.Start(Explorer, args, workingDirectory: _windir.Value);
static readonly Lazy<string> _regedit_exe = new(() => Path.Combine(_windir.Value, "regedit.exe"));
/// <summary>
/// %windir%\regedit.exe
/// </summary>
static string Regedit => _regedit_exe.Value;
/// <summary>
/// 带参数(可选/null)启动 %windir%\regedit.exe
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
static Process? StartProcessRegedit(string? args) => Process2.Start(Regedit, args, workingDirectory: _windir.Value);
static Process? StartAsInvokerByExplorer(string fileName, string? arguments = null)
{
if (string.IsNullOrEmpty(arguments))
return StartProcessExplorer($"\"{fileName}\"");
var cacheCmdFile = IOPath.GetCacheFilePath(CacheTempDirName, "StartAsInvokerByExplorer", FileEx.CMD);
File.WriteAllText(cacheCmdFile, $"@echo {Constants.HARDCODED_APP_NAME} StartAsInvokerByExplorer{Environment.NewLine}start \"\" \"{fileName}\" {arguments}{Environment.NewLine}exit 0");
var process = StartProcessExplorer($"\"{cacheCmdFile}\"");
IOPath.TryDeleteInDelay(process, cacheCmdFile);
return process;
}
public Process? StartAsInvoker(string fileName, string? arguments = null)
{
if (((IPlatformService)this).IsAdministrator)
{
// runas /trustlevel:0x20000 没有真正的降权,只是作为具有限制特权,使用 explorer 最好,但不接受参数,可以创建一个临时cmd脚本启动
//return StartAsInvokerByRunas(fileName, arguments);
return StartAsInvokerByExplorer(fileName, arguments);
}
else
{
return Process2.Start(fileName, arguments);
}
}
//public Process? StartAsInvoker(ProcessStartInfo startInfo)
//{
// startInfo.FileName = runas_exe;
// startInfo.Arguments = $"/trustlevel:0x20000 \"{startInfo.FileName}\"";
// return Process.Start(startInfo);
//}
//public Process? GetProcessByPortOccupy(ushort port, bool isTCPorUDP = true)
//{
// if (isTCPorUDP)
// {
// return SocketHelper.GetProcessByTcpPort(port);
// }
// else
// {
// throw new NotSupportedException();
// }
// //try
// //{
// // using var p = new Process
// // {
// // StartInfo = new ProcessStartInfo
// // {
// // FileName = "cmd",
// // UseShellExecute = false,
// // RedirectStandardInput = true,
// // RedirectStandardOutput = true,
// // CreateNoWindow = true,
// // },
// // };
// // p.Start();
// // p.StandardInput.WriteLine($"netstat -ano|findstr \"{port}\"&exit");
// // p.StandardInput.AutoFlush = true;
// // var reader = p.StandardOutput;
// // while (!reader.EndOfStream)
// // {
// // var line = reader.ReadLine();
// // if (line == null) break;
// // var lineArray = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
// // if (lineArray.Length != 5) continue;
// // if (!lineArray[0].Equals(isTCPorUDP ? "TCP" : "UDP", StringComparison.OrdinalIgnoreCase)) continue;
// // if (!lineArray[3].Equals("LISTENING", StringComparison.OrdinalIgnoreCase)) continue;
// // if (!lineArray[1].EndsWith($":{port}")) continue;
// // if (!ushort.TryParse(lineArray[4], out var pid)) continue;
// // p.Close();
// // return Process.GetProcessById(pid);
// // }
// // _ = p.WaitForExit(550);
// //}
// //catch
// //{
// //}
// //return default;
//}
const string ProxyOverride = "\"ProxyOverride\"=\"localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*\"";
/// <summary>
/// 设置启用或关闭系统代理
/// </summary>
/// <param name="state"></param>
/// <param name="ip"></param>
/// <param name="port"></param>
/// <returns></returns>
public bool SetAsSystemProxy(bool state, IPAddress? ip, int port)
{
try
{
var proxyEnable = $"\"ProxyEnable\"=dword:{(state ? "00000001" : "00000000")}";
var hasIpAndProt = ip != null && port >= 0;
var proxyServer = hasIpAndProt ? $"\"proxyServer\"=\"{ip}:{port}\"" : "";
var reg = $"Windows Registry Editor Version 5.00{Environment.NewLine}[HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings]{Environment.NewLine}{proxyEnable}{Environment.NewLine}{(state ? $"{ProxyOverride}{Environment.NewLine}" : "")}{proxyServer}";
var path = IOPath.GetCacheFilePath(CacheTempDirName, "SwitchProxy", FileEx.Reg);
File.WriteAllText(path, reg, Encoding.UTF8);
var p = StartProcessRegedit($"/s \"{path}\"");
IOPath.TryDeleteInDelay(p, path);
return true;
}
catch (Exception ex)
{
Toast.LogAndShowT(ex, TAG);
return false;
}
}
/// <summary>
/// 设置启用或关闭PAC代理
/// </summary>
/// <param name="state"></param>
/// <param name="url"></param>
/// <returns></returns>
public bool SetAsSystemPACProxy(bool state, string? url = null)
{
try
{
var regAutoConfigUrl = state ? $"\"AutoConfigURL\"=\"{url}\"" : "\"AutoConfigURL\"=\"\"";
var reg = $"Windows Registry Editor Version 5.00{Environment.NewLine}[HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings]{Environment.NewLine}{regAutoConfigUrl}";
var path = IOPath.GetCacheFilePath(CacheTempDirName, "SwitchPacProxy", FileEx.Reg);
File.WriteAllText(path, reg, Encoding.UTF8);
var p = StartProcessRegedit($"/s \"{path}\"");
IOPath.TryDeleteInDelay(p, path);
return true;
}
catch (Exception ex)
{
Toast.LogAndShowT(ex, TAG);
return false;
}
}
public IReadOnlyCollection<KeyValuePair<string, string>> GetFontsByGdiPlus()
{
// https://docs.microsoft.com/zh-cn/typography/font-list
var culture = R.Culture;
InstalledFontCollection ifc = new();
var list = ifc.Families.Where(x => x.IsStyleAvailable(FontStyle.Regular)).Select(x => KeyValuePair.Create(x.GetName(culture.LCID), x.GetName(1033))).ToList();
list.Insert(0, IFontManager.Default);
return list;
}
#region IsInstall
static readonly Lazy<bool> _IsInstall = new(() =>
{
using var reg32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
var value = reg32.Read(@"SOFTWARE\Steam++", "InstPath").TrimEnd(Path.DirectorySeparatorChar);
return string.Equals(IOPath.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar), value, StringComparison.OrdinalIgnoreCase);
});
/// <inheritdoc cref="IPlatformService.IsInstall"/>
public static bool IsInstall => _IsInstall.Value;
bool IPlatformService.IsInstall => IsInstall;
#endregion
}
+12 -2
View File
@@ -15,6 +15,7 @@ using Android.Runtime;
using Android.Content.PM;
#endif
// ReSharper disable once CheckNamespace
namespace System.Application.UI;
public static partial class MauiProgram
@@ -25,6 +26,10 @@ public static partial class MauiProgram
static string[] GetCommandLineArgs()
{
#if ANDROID
// Android 中 Environment.GetCommandLineArgs 返回 apk 安装目录的 string[1]
return Array.Empty<string>();
#else
try
{
var args = Environment.GetCommandLineArgs();
@@ -34,6 +39,7 @@ public static partial class MauiProgram
{
return Array.Empty<string>();
}
#endif
}
internal static MauiApp CreateMauiApp(string[]? args = null)
@@ -65,8 +71,12 @@ public static partial class MauiProgram
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
;
}).ConfigureMauiHandlers(handlers =>
{
#if ANDROID
handlers.AddHandler<Shell, ShellRenderer>();
#endif
});
ConfigureServices(host, level, builder.Services);
};
@@ -1,10 +0,0 @@
{
"profiles": {
"Watt Toolkit (Package)": {
"commandName": "MsixPackage"
},
"Watt Toolkit (Unpackaged)": {
"commandName": "Project"
}
}
}
@@ -8,10 +8,12 @@
<OutputType>Exe</OutputType>
<AssemblyName>Watt Toolkit</AssemblyName>
<RootNamespace>System.Application.UI</RootNamespace>
<AndroidResgenNamespace>System.Application.UI.AndroidResgen</AndroidResgenNamespace>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<DefineConstants>MAUI;$(DefineConstants)</DefineConstants>
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
<!-- Display name -->
<ApplicationTitle>Watt Toolkit</ApplicationTitle>
@@ -23,8 +25,6 @@
<!-- Versions -->
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
@@ -69,14 +69,36 @@
<ItemGroup>
<ProjectReference Include="..\Common.CoreLib\Common.CoreLib.csproj" />
<ProjectReference Include="..\Common.Essentials.Maui\Common.Essentials.Maui.csproj" />
<ProjectReference Include="..\ST.Client\ST.Client.csproj" />
<ProjectReference Include="..\ST.Client.CommandLine\ST.Client.CommandLine.csproj" />
<ProjectReference Include="..\ST.Client.Maui\ST.Client.Maui.csproj" />
<ProjectReference Include="..\ST.Client.ReverseProxy.Yarp\ST.Client.ReverseProxy.Yarp.csproj" />
<ProjectReference Include="..\ST.Client.AppCenter\ST.Client.AppCenter.csproj" />
<ProjectReference Include="..\ST.Client.ResSecrets\ST.Client.ResSecrets.csproj" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
<ProjectReference Include="..\ST.Client.Desktop.Windows\ST.Client.Windows.csproj" />
<ProjectReference Include="..\ST.Client.JumpList\ST.Client.JumpList.csproj" />
<ProjectReference Include="..\ST.Client.ReverseProxy.Yarp\ST.Client.ReverseProxy.Yarp.csproj" />
<ProjectReference Include="..\ST.Client.Windows.ScheduledTasks\ST.Client.Windows.ScheduledTasks.csproj" />
<ProjectReference Include="..\ST.Client.Windows.ResSecrets\ST.Client.Windows.ResSecrets.csproj" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
<ProjectReference Include="..\ST.Client.iOS\ST.Client.iOS.csproj" />
<ProjectReference Include="..\ST.Client.iOS.ResSecrets\ST.Client.iOS.ResSecrets.csproj" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">
<ProjectReference Include="..\ST.Client.Desktop.Mac\ST.Client.Mac.csproj" />
<ProjectReference Include="..\ST.Client.Mac.ResSecrets\ST.Client.Mac.ResSecrets.csproj" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
<ProjectReference Include="..\ST.Client.Android\ST.Client.Android.csproj" />
<ProjectReference Include="..\ST.Client.ReverseProxy.Titanium\ST.Client.ReverseProxy.Titanium.csproj" />
<ProjectReference Include="..\ST.Client.Android.ResSecrets\ST.Client.Android.ResSecrets.csproj" />
</ItemGroup>
</Project>
+53 -20
View File
@@ -6,6 +6,7 @@
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
<!-- <TargetFrameworks>$(TargetFrameworks);net6.0-tizen</TargetFrameworks> -->
<RootNamespace>System.Application</RootNamespace>
<AndroidResgenNamespace>System.Application.AndroidResgen</AndroidResgenNamespace>
<AssemblyName>System.Application.SteamTools.Client.Maui</AssemblyName>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
@@ -14,7 +15,30 @@
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
<SupportedOSPlatformVersion>14.2</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">
<SupportedOSPlatformVersion>14.0</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
<SupportedOSPlatformVersion>21.0</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">
<SupportedOSPlatformVersion>6.5</SupportedOSPlatformVersion>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\ST.Client.Desktop.Avalonia\Application\UI\App.OnCreateAppExecuted.HandlerViewModelManager.cs">
<Link>UI\App.OnCreateAppExecuted.HandlerViewModelManager.cs</Link>
</Compile>
<Compile Include="..\Common.CoreLib\Properties\AssemblyInfo.cs">
<Link>Properties\AssemblyInfo.cs</Link>
</Compile>
@@ -54,26 +78,6 @@
</Compile>
</ItemGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
<SupportedOSPlatformVersion>14.2</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">
<SupportedOSPlatformVersion>14.0</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
<SupportedOSPlatformVersion>21.0</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">
<SupportedOSPlatformVersion>6.5</SupportedOSPlatformVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Common.Essentials.Maui\Common.Essentials.Maui.csproj" />
<ProjectReference Include="..\ST.Client\ST.Client.csproj" />
@@ -105,4 +109,33 @@
<MauiAsset Remove="UI\Assets\Raw\AboutAssets.txt" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
<ProjectReference Include="..\ST.Client.Desktop.Windows\ST.Client.Windows.csproj" />
<ProjectReference Include="..\ST.Client.JumpList\ST.Client.JumpList.csproj" />
<ProjectReference Include="..\ST.Client.ReverseProxy.Yarp\ST.Client.ReverseProxy.Yarp.csproj" />
<ProjectReference Include="..\ST.Client.Windows.ScheduledTasks\ST.Client.Windows.ScheduledTasks.csproj" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
<ProjectReference Include="..\ST.Client.iOS\ST.Client.iOS.csproj" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">
<ProjectReference Include="..\ST.Client.Desktop.Mac\ST.Client.Mac.csproj" />
</ItemGroup>
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
<ProjectReference Include="..\ST.Client.Android\ST.Client.Android.csproj" />
<ProjectReference Include="..\ST.Client.ReverseProxy.Titanium\ST.Client.ReverseProxy.Titanium.csproj" />
</ItemGroup>
<ItemGroup>
<MauiXaml Update="UI\Views\Controls\FlyoutFooter.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="UI\Views\Controls\FlyoutHeader.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
</ItemGroup>
</Project>
@@ -3,48 +3,47 @@
using System.Application.UI;
using System.Application.UI.ViewModels;
namespace System.Application.Services.Implementation
namespace System.Application.Services.Implementation;
/// <inheritdoc cref="IWindowManager"/>
internal sealed class MauiWindowManagerImpl : IWindowManagerImpl
{
/// <inheritdoc cref="IWindowManager"/>
internal sealed class MauiWindowManagerImpl : IWindowManagerImpl
readonly IMauiApplication app;
public MauiWindowManagerImpl(IMauiApplication app)
{
readonly IMauiApplication app;
this.app = app;
}
public MauiWindowManagerImpl(IMauiApplication app)
{
this.app = app;
}
Type? IWindowManagerImpl.WindowType => typeof(Page);
Type? IWindowManagerImpl.WindowType => typeof(Page);
public Task Show<TWindowViewModel>(CustomWindow customWindow, TWindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true) where TWindowViewModel : WindowViewModel, new()
{
throw new NotImplementedException();
}
public Task Show<TWindowViewModel>(CustomWindow customWindow, TWindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true) where TWindowViewModel : WindowViewModel, new()
{
throw new NotImplementedException();
}
public Task Show(Type typeWindowViewModel, CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true)
{
throw new NotImplementedException();
}
public Task Show(Type typeWindowViewModel, CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true)
{
throw new NotImplementedException();
}
public Task Show(CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true)
{
throw new NotImplementedException();
}
public Task Show(CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = false, bool isParent = true)
{
throw new NotImplementedException();
}
public Task<bool> ShowDialog<TWindowViewModel>(CustomWindow customWindow, TWindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true, bool isParent = true) where TWindowViewModel : WindowViewModel, new()
{
throw new NotImplementedException();
}
public Task<bool> ShowDialog<TWindowViewModel>(CustomWindow customWindow, TWindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true, bool isParent = true) where TWindowViewModel : WindowViewModel, new()
{
throw new NotImplementedException();
}
public Task ShowDialog(Type typeWindowViewModel, CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true)
{
throw new NotImplementedException();
}
public Task ShowDialog(Type typeWindowViewModel, CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true)
{
throw new NotImplementedException();
}
public Task ShowDialog(CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true)
{
throw new NotImplementedException();
}
public Task ShowDialog(CustomWindow customWindow, WindowViewModel? viewModel = null, string title = "", ResizeMode resizeMode = ResizeMode.NoResize, bool isDialog = true)
{
throw new NotImplementedException();
}
}
+14 -13
View File
@@ -1,14 +1,15 @@
<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:System.Application.UI"
xmlns:styles="clr-namespace:System.Application.UI.Styles"
x:Class="System.Application.UI.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary Source="Styles\ThemeLight.xaml" />
<ResourceDictionary Source="Styles\Controls.xaml" />
<styles:DrawingSvg x:Key="DrawingSvg" />
</ResourceDictionary>
</Application.Resources>
<?xml version="1.0" encoding="UTF-8" ?>
<Application
x:Class="System.Application.UI.App"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:System.Application.UI"
xmlns:styles="clr-namespace:System.Application.UI.Styles">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary Source="Styles\ThemeLight.xaml" />
<ResourceDictionary Source="Styles\Controls.xaml" />
<styles:DrawingSvg x:Key="DrawingSvg" />
</ResourceDictionary>
</Application.Resources>
</Application>
+3 -29
View File
@@ -1,8 +1,8 @@
using System.Application.Mvvm;
using System.Application.Services;
using System.Application.Settings;
using System.Application.UI.Styles;
using System.Application.UI.ViewModels;
using System.Application.UI.Styles;
using System.Reactive.Disposables;
using System.Windows.Input;
using EAppTheme = System.Application.Models.AppTheme;
@@ -43,37 +43,11 @@ public partial class App : MauiApplication, IDisposableHolder, IApplication, IMa
{
const bool isTrace =
#if StartWatchTrace
true;
true;
#else
false;
#endif
ProgramHost.OnCreateAppExecuted(handlerViewModelManager: vmService =>
{
switch (vmService.MainWindow)
{
case AchievementWindowViewModel:
ProgramHost.IsMinimize = false;
//MainWindow = new AchievementWindow();
break;
default:
#region 主窗口启动时加载的资源
#if !UI_DEMO
compositeDisposable.Add(SettingsHost.Save);
compositeDisposable.Add(ProxyService.Current.Dispose);
compositeDisposable.Add(SteamConnectService.Current.Dispose);
compositeDisposable.Add(ASFService.Current.StopASF);
#pragma warning disable CA1416 // 验证平台兼容性
if (GeneralSettings.IsStartupAppMinimized.Value)
ProgramHost.IsMinimize = true;
#pragma warning restore CA1416 // 验证平台兼容性
#endif
#endregion
//MainWindow = new MainWindow();
break;
}
MainPage = new AppShell((MainWindowViewModel)vmService.MainWindow);
}, isTrace: isTrace);
ProgramHost.OnCreateAppExecuted(handlerViewModelManager: HandlerViewModelManager, isTrace: isTrace);
}
public Window GetActiveWindow()
+7 -25
View File
@@ -3,35 +3,17 @@
x:Class="System.Application.UI.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:System.Application.UI.Views.Controls"
xmlns:local="clr-namespace:System.Application.UI.Views"
xmlns:resx="clr-namespace:System.Application.UI.Resx;assembly=System.Application.SteamTools.Client"
xmlns:vm="clr-namespace:System.Application.UI.ViewModels;assembly=System.Application.SteamTools.Client"
Title="{Binding Title}"
x:DataType="vm:MainWindowViewModel">
<Shell.FlyoutHeaderTemplate>
<DataTemplate>
<StackLayout Padding="24,24,24,22" Orientation="Horizontal">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="OnFlyoutHeaderTapped" />
</StackLayout.GestureRecognizers>
<Image Source="avatar.jpg"
HeightRequest="64"
WidthRequest="64">
<Image.Clip>
<EllipseGeometry Center="32,32"
RadiusX="32"
RadiusY="32" />
</Image.Clip>
</Image>
<Label Margin="16,0,0,0"
FontSize="16"
LineBreakMode="TailTruncation"
MaxLines="2"
Text="{x:Static resx:AppResources.User_UnLogin}"
TextColor="{DynamicResource ShellTitleColor}"
VerticalTextAlignment="Center" />
</StackLayout>
</DataTemplate>
</Shell.FlyoutHeaderTemplate>
<Shell.FlyoutHeader>
<controls:FlyoutHeader />
</Shell.FlyoutHeader>
<Shell.FlyoutFooter>
<controls:FlyoutFooter />
</Shell.FlyoutFooter>
</Shell>
+58 -190
View File
@@ -9,12 +9,9 @@ namespace System.Application.UI;
public partial class AppShell : Shell, IViewFor<MainWindowViewModel>
{
/// <summary>
/// 是否使用底部导航菜单
/// </summary>
internal static bool IsUseBottomNav { private get; set; }
//readonly IPlatformPageRouteService? pageRouteService = IPlatformPageRouteService.Instance;
readonly IPlatformPageRouteService? pageRouteService = IPlatformPageRouteService.Instance;
#region VM
MainWindowViewModel viewModel;
@@ -36,6 +33,8 @@ public partial class AppShell : Shell, IViewFor<MainWindowViewModel>
set => BindingContext = value;
}
#endregion
public AppShell(MainWindowViewModel viewModel)
{
BindingContext = this.viewModel = viewModel;
@@ -49,194 +48,35 @@ public partial class AppShell : Shell, IViewFor<MainWindowViewModel>
// });
//#endif
if (!IsUseBottomNav)
#region InitTabItems
FlyoutItem flyoutItem = new()
{
#region Flyout
var topTabs = IApplication.IsDesktopPlatform ? Array.Empty<TabItemId>() : new[] { TabItemId.LocalAuth, TabItemId.ArchiSteamFarmPlus };
InitTabItems(viewModel, topTabs);
AddPage(MyPageViewModel.Instance, isVisible: false);
#if DEBUG
AddPage(new LoginOrRegisterWindowViewModel());
#endif
#endregion
}
else
{
#region BottomNav
FlyoutBehavior = FlyoutBehavior.Disabled;
InitButtomTabItems(viewModel);
AddPage(SettingsPageViewModel.Instance, isVisible: false);
AddPage(AboutPageViewModel.Instance, isVisible: false);
#endregion
}
}
#region Flyout(左侧导航菜单)
void InitTabItems(MainWindowViewModel mainWindow, params TabItemId[] topTabs)
{
IEnumerable<TabItemId> topTabs_ = topTabs;
InitTabItems(mainWindow, topTabs_);
}
void InitTabItems(MainWindowViewModel mainWindow, IEnumerable<TabItemId>? topTabs = null)
{
var tabItems = mainWindow.TabItems.Where(x => x.Id != TabItemId.GameList);
if (topTabs.Any_Nullable())
{
tabItems = tabItems.Where(x => topTabs.Contains(x.Id)).Concat(tabItems.Where(x => !topTabs.Contains(x.Id)));
}
foreach (var item in tabItems)
{
InitTabItem(item);
}
void InitTabItem(TabItemViewModel tab)
{
if (pageRouteService?.IsUseNativePage(tab.Id) ?? false)
{
var tab_item = new MenuItem
{
IconImageSource = tab.IconKey,
BindingContext = tab,
};
tab_item.Clicked += (_, _) => pageRouteService!.GoToNativePage(tab.Id);
tab_item.SetBinding(MenuItem.TextProperty, nameof(tab.Name), BindingMode.OneWay);
Items.Add(tab_item);
}
else
{
var tab_item = new FlyoutItem
{
Icon = tab.IconKey,
BindingContext = tab,
};
tab_item.SetBinding(BaseShellItem.TitleProperty, nameof(tab.Name), BindingMode.OneWay);
tab_item.Items.Add(new ShellContent
{
Route = tab.IconKey!.TrimEnd("ViewModel"),
ContentTemplate = new DataTemplate(GetPageType(tab)),
});
Items.Add(tab_item);
}
}
}
void AddPage(ViewModelBase vm, bool isVisible = true)
{
var data = GetShellItemData(vm);
if (data == default) return;
var tab_item = new FlyoutItem
{
BindingContext = vm,
FlyoutItemIsVisible = isVisible,
FlyoutDisplayOptions = FlyoutDisplayOptions.AsMultipleItems,
};
tab_item.SetBinding(BaseShellItem.TitleProperty, data.bindingPathName, BindingMode.OneWay);
var pageType = GetPageType(vm);
var content = new ShellContent
AddPage(viewModel.LocalAuthPage);
AddPage(viewModel.ASFPage);
if (!OperatingSystem.IsIOS())
AddPage(viewModel.CommunityProxyPage);
AddPage(MyPageViewModel.Instance, flyoutItemIsVisible: false);
Items.Add(flyoutItem);
AddPage(SettingsPageViewModel.Instance, isSecondary: false);
AddPage(AboutPageViewModel.Instance, isSecondary: false);
void AddPage(ViewModelBase vm, bool flyoutItemIsVisible = true, bool isSecondary = true)
{
ContentTemplate = new DataTemplate(pageType),
};
if (data.route != null) content.Route = pageType.Name;
tab_item.Items.Add(content);
Items.Add(tab_item);
var shellContent = CreateShellContent(vm);
if (shellContent == null) return;
shellContent.FlyoutItemIsVisible = flyoutItemIsVisible;
if (isSecondary) flyoutItem.Items.Add(shellContent);
else Items.Add(shellContent);
}
#endregion
}
#endregion
#region BottomNav(底部导航菜单)
void InitButtomTabItems(MainWindowViewModel mainWindow)
{
var tabItems = new ViewModelBase[] {
mainWindow.LocalAuthPage,
mainWindow.ASFPage,
MyPageViewModel.Instance,
};
TabBar tabBar = new();
foreach (var tab in tabItems)
{
var data = GetShellItemData(tab);
if (data == default) continue;
var tab_item = new Tab
{
BindingContext = tab,
Icon = data.icon,
};
tab_item.SetBinding(BaseShellItem.TitleProperty, data.bindingPathName, BindingMode.OneWay);
var content = new ShellContent
{
ContentTemplate = new DataTemplate(GetPageType(tab)),
};
if (data.route != null) content.Route = data.route;
tab_item.Items.Add(content);
tabBar.Items.Add(tab_item);
}
Items.Add(tabBar);
}
#endregion
static (string icon, string bindingPathName, string route) GetShellItemData(ViewModelBase vm, bool rSubscribe = true)
{
string? icon = null;
string? bindingPathName = null;
string? route = null;
if (vm is TabItemViewModel tabItemVM)
{
if (tabItemVM.IconKey == null) return default;
if (IsUseBottomNav)
{
if (tabItemVM is LocalAuthPageViewModel)
{
icon = "baseline_verified_user_black_24";
}
else if (tabItemVM is ArchiSteamFarmPlusPageViewModel)
{
icon = "icon_asf_24";
}
else
{
return default;
}
}
else
{
icon = tabItemVM.IconKey;
}
bindingPathName = nameof(tabItemVM.Name);
route = tabItemVM.IconKey.TrimEnd("ViewModel");
if (tabItemVM is SettingsPageViewModel || tabItemVM is AboutPageViewModel)
{
route = $"MyPage/{route}";
//route = null;
}
rSubscribe = false;
}
else if (vm is PageViewModel pageVM)
{
bindingPathName = nameof(pageVM.Title);
route = pageVM.GetType().Name.TrimEnd("ViewModel");
if (pageVM is MyPageViewModel)
{
icon = "baseline_person_black_24";
}
}
if (icon == null || bindingPathName == null || route == null) return default;
if (rSubscribe)
{
R.Subscribe(() =>
{
vm.RaisePropertyChanged(nameof(PageViewModel.Title));
}).AddTo(vm);
}
return (icon, bindingPathName, route);
}
#region TabItems
static Type GetPageType(ViewModelBase vm)
{
@@ -268,11 +108,39 @@ public partial class AppShell : Shell, IViewFor<MainWindowViewModel>
return typeof(UnderConstructionPage);
}
void OnFlyoutHeaderTapped(object? sender, EventArgs e)
static ShellContent? CreateShellContent(ViewModelBase vm)
{
Current.FlyoutIsPresented = false;
var pageType = GetPageType(vm);
var content = new ShellContent
{
ContentTemplate = new DataTemplate(pageType),
BindingContext = vm,
};
string? bindingPathName;
if (vm is TabItemViewModel tabVM)
{
content.Icon = tabVM.IconKey;
content.Route = tabVM.Id.ToString();
bindingPathName = nameof(TabItemViewModel.Name);
}
else if (vm is PageViewModel)
{
bindingPathName = nameof(PageViewModel.Title);
content.Icon = "baseline_person_black_24";
}
else
{
bindingPathName = default;
}
if (bindingPathName != default)
{
content.SetBinding(BaseShellItem.TitleProperty, bindingPathName, BindingMode.OneWay);
}
return content;
}
#endregion
public static Task PopAsync()
{
// https://docs.microsoft.com/zh-cn/xamarin/xamarin-forms/app-fundamentals/shell/navigation#backwards-navigation
@@ -0,0 +1,16 @@
#if ANDROID
using AndroidX.Fragment.App;
using AView = Android.Views.View;
using BaseShellRenderer = Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer;
namespace System.Application.UI;
public sealed partial class ShellRenderer : BaseShellRenderer
{
protected override void SwitchFragment(FragmentManager manager, AView targetView, ShellItem newItem, bool animate)
{
// 禁用 Android 上的切换 Page 时动画
base.SwitchFragment(manager, targetView, newItem, false);
}
}
#endif
+353 -352
View File
@@ -1,383 +1,384 @@
<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="System.Application.UI.Styles.Controls">
<ResourceDictionary
x:Class="System.Application.UI.Styles.Controls"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<Style TargetType="ActivityIndicator">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
</Style>
<Style TargetType="ActivityIndicator">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
</Style>
<Style TargetType="IndicatorView">
<Setter Property="IndicatorColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}"/>
<Setter Property="SelectedIndicatorColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray100}}"/>
</Style>
<Style TargetType="IndicatorView">
<Setter Property="IndicatorColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="SelectedIndicatorColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray100}}" />
</Style>
<Style TargetType="Border">
<Setter Property="Stroke" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="StrokeShape" Value="Rectangle"/>
<Setter Property="StrokeThickness" Value="1"/>
</Style>
<Style TargetType="Border">
<Setter Property="Stroke" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="StrokeShape" Value="Rectangle" />
<Setter Property="StrokeThickness" Value="1" />
</Style>
<Style TargetType="BoxView">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="BoxView">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="Button">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Primary}}" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="CornerRadius" Value="8"/>
<Setter Property="Padding" Value="14,10"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Button">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Primary}}" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="CornerRadius" Value="8" />
<Setter Property="Padding" Value="14,10" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="CheckBox">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="CheckBox">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="DatePicker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="DatePicker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Editor">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Editor">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Entry">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Entry">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Frame">
<Setter Property="HasShadow" Value="False" />
<Setter Property="BorderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="CornerRadius" Value="8" />
</Style>
<Style TargetType="Frame">
<Setter Property="HasShadow" Value="False" />
<Setter Property="BorderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="CornerRadius" Value="8" />
</Style>
<Style TargetType="ImageButton">
<Setter Property="Opacity" Value="1" />
<Setter Property="BorderColor" Value="Transparent"/>
<Setter Property="BorderWidth" Value="0"/>
<Setter Property="CornerRadius" Value="0"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="Opacity" Value="0.5" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="ImageButton">
<Setter Property="Opacity" Value="1" />
<Setter Property="BorderColor" Value="Transparent" />
<Setter Property="BorderWidth" Value="0" />
<Setter Property="CornerRadius" Value="0" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="Opacity" Value="0.5" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Label">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Label">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="ListView">
<Setter Property="SeparatorColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="RefreshControlColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="ListView">
<Setter Property="SeparatorColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="RefreshControlColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="Picker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="TitleColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="TitleColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Picker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="TitleColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="TitleColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="ProgressBar">
<Setter Property="ProgressColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="ProgressColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="ProgressBar">
<Setter Property="ProgressColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="ProgressColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="RadioButton">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="RadioButton">
<Setter Property="Background" Value="Transparent" />
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="RefreshView">
<Setter Property="RefreshColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="RefreshView">
<Setter Property="RefreshColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="SearchBar">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="PlaceholderColor" Value="{StaticResource Gray500}" />
<Setter Property="CancelButtonColor" Value="{StaticResource Gray500}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="SearchBar">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="PlaceholderColor" Value="{StaticResource Gray500}" />
<Setter Property="CancelButtonColor" Value="{StaticResource Gray500}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="SearchHandler">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="PlaceholderColor" Value="{StaticResource Gray500}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="SearchHandler">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="PlaceholderColor" Value="{StaticResource Gray500}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Shadow">
<Setter Property="Radius" Value="15" />
<Setter Property="Opacity" Value="0.5" />
<Setter Property="Brush" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource White}}" />
<Setter Property="Offset" Value="10,10" />
</Style>
<Style TargetType="Shadow">
<Setter Property="Radius" Value="15" />
<Setter Property="Opacity" Value="0.5" />
<Setter Property="Brush" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource White}}" />
<Setter Property="Offset" Value="10,10" />
</Style>
<Style TargetType="Slider">
<Setter Property="MinimumTrackColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="MaximumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="MinimumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
<Setter Property="MaximumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Slider">
<Setter Property="MinimumTrackColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="MaximumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="MinimumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="MaximumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="SwipeItem">
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
</Style>
<Style TargetType="SwipeItem">
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
</Style>
<Style TargetType="Switch">
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="ThumbColor" Value="{StaticResource White}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="On">
<VisualState.Setters>
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Secondary}, Dark={StaticResource Gray200}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Off">
<VisualState.Setters>
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray400}, Dark={StaticResource Gray500}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Switch">
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="ThumbColor" Value="{StaticResource White}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="On">
<VisualState.Setters>
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Secondary}, Dark={StaticResource Gray200}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Off">
<VisualState.Setters>
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray400}, Dark={StaticResource Gray500}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="TimePicker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="Background" Value="Transparent"/>
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="TimePicker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Page" ApplyToDerivedTypes="True">
<Setter Property="Padding" Value="0"/>
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
</Style>
<Style ApplyToDerivedTypes="True" TargetType="Page">
<Setter Property="Padding" Value="0" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
</Style>
<Style TargetType="Shell" ApplyToDerivedTypes="True">
<Setter Property="Shell.BackgroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource Gray950}}" />
<Setter Property="Shell.ForegroundColor" Value="{OnPlatform WinUI={StaticResource Primary}, Default={StaticResource White}}"></Setter>
<Setter Property="Shell.TitleColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource White}}" />
<Setter Property="Shell.DisabledColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="Shell.UnselectedColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray200}}" />
<Setter Property="Shell.NavBarHasShadow" Value="False" />
<Setter Property="Shell.TabBarBackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
<Setter Property="Shell.TabBarForegroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="Shell.TabBarTitleColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="Shell.TabBarUnselectedColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style ApplyToDerivedTypes="True" TargetType="Shell">
<Setter Property="Shell.BackgroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource Gray950}}" />
<Setter Property="Shell.ForegroundColor" Value="{OnPlatform WinUI={StaticResource Primary}, Default={StaticResource White}}" />
<Setter Property="Shell.TitleColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource White}}" />
<Setter Property="Shell.DisabledColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="Shell.UnselectedColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray200}}" />
<Setter Property="Shell.NavBarHasShadow" Value="False" />
<Setter Property="Shell.TabBarBackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
<Setter Property="Shell.TabBarForegroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="Shell.TabBarTitleColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="Shell.TabBarUnselectedColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="NavigationPage">
<Setter Property="BarBackgroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource Gray950}}" />
<Setter Property="BarTextColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
<Setter Property="IconColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
</Style>
<Style TargetType="NavigationPage">
<Setter Property="BarBackgroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource Gray950}}" />
<Setter Property="BarTextColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
<Setter Property="IconColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
</Style>
<Style TargetType="TabbedPage">
<Setter Property="BarBackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Gray950}}" />
<Setter Property="BarTextColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="UnselectedTabColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="SelectedTabColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="TabbedPage">
<Setter Property="BarBackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Gray950}}" />
<Setter Property="BarTextColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="UnselectedTabColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="SelectedTabColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
</Style>
</ResourceDictionary>
+52 -51
View File
@@ -1,55 +1,56 @@
<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="System.Application.UI.Styles.ThemeLight">
<Color x:Key="Primary">#2196F3</Color>
<!-- 背景颜色 -->
<Color x:Key="GreyWindowBackground">#F9F9F9</Color>
<!-- 主要文本 -->
<Color x:Key="TextPrimary">#333333</Color>
<!-- 次要文本 -->
<Color x:Key="TextSecondary">#666666</Color>
<!-- 点击文字颜色 -->
<Color x:Key="TextLink">#007ffc</Color>
<Color x:Key="ShellBackgroundColor">White</Color>
<Color x:Key="ShellTitleColor">#222222</Color>
<!--<Color x:Key="ShellForegroundColor">#767676</Color>-->
<ResourceDictionary
x:Class="System.Application.UI.Styles.ThemeLight"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<Color x:Key="Primary">#2196F3</Color>
<!-- 背景颜色 -->
<Color x:Key="GreyWindowBackground">#F9F9F9</Color>
<!-- 主要文本 -->
<Color x:Key="TextPrimary">#333333</Color>
<!-- 次要文本 -->
<Color x:Key="TextSecondary">#666666</Color>
<!-- 点击文字颜色 -->
<Color x:Key="TextLink">#007ffc</Color>
<Color x:Key="ShellBackgroundColor">White</Color>
<Color x:Key="ShellTitleColor">#222222</Color>
<!--<Color x:Key="ShellForegroundColor">#767676</Color>-->
<!-- MAUI 默认模板资源 -->
<!--<Color x:Key="Primary">#512BD4</Color>-->
<Color x:Key="Secondary">#DFD8F7</Color>
<Color x:Key="Tertiary">#2B0B98</Color>
<Color x:Key="White">White</Color>
<Color x:Key="Black">Black</Color>
<Color x:Key="Gray100">#E1E1E1</Color>
<Color x:Key="Gray200">#C8C8C8</Color>
<Color x:Key="Gray300">#ACACAC</Color>
<Color x:Key="Gray400">#919191</Color>
<Color x:Key="Gray500">#6E6E6E</Color>
<Color x:Key="Gray600">#404040</Color>
<Color x:Key="Gray900">#212121</Color>
<Color x:Key="Gray950">#141414</Color>
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource Primary}"/>
<SolidColorBrush x:Key="SecondaryBrush" Color="{StaticResource Secondary}"/>
<SolidColorBrush x:Key="TertiaryBrush" Color="{StaticResource Tertiary}"/>
<SolidColorBrush x:Key="WhiteBrush" Color="{StaticResource White}"/>
<SolidColorBrush x:Key="BlackBrush" Color="{StaticResource Black}"/>
<SolidColorBrush x:Key="Gray100Brush" Color="{StaticResource Gray100}"/>
<SolidColorBrush x:Key="Gray200Brush" Color="{StaticResource Gray200}"/>
<SolidColorBrush x:Key="Gray300Brush" Color="{StaticResource Gray300}"/>
<SolidColorBrush x:Key="Gray400Brush" Color="{StaticResource Gray400}"/>
<SolidColorBrush x:Key="Gray500Brush" Color="{StaticResource Gray500}"/>
<SolidColorBrush x:Key="Gray600Brush" Color="{StaticResource Gray600}"/>
<SolidColorBrush x:Key="Gray900Brush" Color="{StaticResource Gray900}"/>
<SolidColorBrush x:Key="Gray950Brush" Color="{StaticResource Gray950}"/>
<!-- MAUI 默认模板资源 -->
<!--<Color x:Key="Primary">#512BD4</Color>-->
<Color x:Key="Secondary">#DFD8F7</Color>
<Color x:Key="Tertiary">#2B0B98</Color>
<Color x:Key="White">White</Color>
<Color x:Key="Black">Black</Color>
<Color x:Key="Gray100">#E1E1E1</Color>
<Color x:Key="Gray200">#C8C8C8</Color>
<Color x:Key="Gray300">#ACACAC</Color>
<Color x:Key="Gray400">#919191</Color>
<Color x:Key="Gray500">#6E6E6E</Color>
<Color x:Key="Gray600">#404040</Color>
<Color x:Key="Gray900">#212121</Color>
<Color x:Key="Gray950">#141414</Color>
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource Primary}" />
<SolidColorBrush x:Key="SecondaryBrush" Color="{StaticResource Secondary}" />
<SolidColorBrush x:Key="TertiaryBrush" Color="{StaticResource Tertiary}" />
<SolidColorBrush x:Key="WhiteBrush" Color="{StaticResource White}" />
<SolidColorBrush x:Key="BlackBrush" Color="{StaticResource Black}" />
<SolidColorBrush x:Key="Gray100Brush" Color="{StaticResource Gray100}" />
<SolidColorBrush x:Key="Gray200Brush" Color="{StaticResource Gray200}" />
<SolidColorBrush x:Key="Gray300Brush" Color="{StaticResource Gray300}" />
<SolidColorBrush x:Key="Gray400Brush" Color="{StaticResource Gray400}" />
<SolidColorBrush x:Key="Gray500Brush" Color="{StaticResource Gray500}" />
<SolidColorBrush x:Key="Gray600Brush" Color="{StaticResource Gray600}" />
<SolidColorBrush x:Key="Gray900Brush" Color="{StaticResource Gray900}" />
<SolidColorBrush x:Key="Gray950Brush" Color="{StaticResource Gray950}" />
<Color x:Key="Yellow100Accent">#F7B548</Color>
<Color x:Key="Yellow200Accent">#FFD590</Color>
<Color x:Key="Yellow300Accent">#FFE5B9</Color>
<Color x:Key="Cyan100Accent">#28C2D1</Color>
<Color x:Key="Cyan200Accent">#7BDDEF</Color>
<Color x:Key="Cyan300Accent">#C3F2F4</Color>
<Color x:Key="Blue100Accent">#3E8EED</Color>
<Color x:Key="Blue200Accent">#72ACF1</Color>
<Color x:Key="Blue300Accent">#A7CBF6</Color>
<Color x:Key="Yellow100Accent">#F7B548</Color>
<Color x:Key="Yellow200Accent">#FFD590</Color>
<Color x:Key="Yellow300Accent">#FFE5B9</Color>
<Color x:Key="Cyan100Accent">#28C2D1</Color>
<Color x:Key="Cyan200Accent">#7BDDEF</Color>
<Color x:Key="Cyan300Accent">#C3F2F4</Color>
<Color x:Key="Blue100Accent">#3E8EED</Color>
<Color x:Key="Blue200Accent">#72ACF1</Color>
<Color x:Key="Blue300Accent">#A7CBF6</Color>
</ResourceDictionary>
@@ -0,0 +1,12 @@
<ContentView
x:Class="System.Application.UI.Views.Controls.FlyoutFooter"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:System.Application.UI.Views.Controls">
<Label
Padding="8"
FontSize="12"
HorizontalOptions="Center"
Text="{Binding Source={x:Static controls:FlyoutFooter.FooterText}, Mode=OneTime}"
TextColor="{DynamicResource ShellTitleColor}" />
</ContentView>
@@ -0,0 +1,11 @@
namespace System.Application.UI.Views.Controls;
public partial class FlyoutFooter : ContentView
{
public FlyoutFooter()
{
InitializeComponent();
}
public static string FooterText { get; } = $"Powered by .NET {Environment.Version}";
}
@@ -0,0 +1,31 @@
<ContentView
x:Class="System.Application.UI.Views.Controls.FlyoutHeader"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:System.Application.UI.Views.Controls"
xmlns:resx="clr-namespace:System.Application.UI.Resx;assembly=System.Application.SteamTools.Client">
<StackLayout Padding="24,24,24,22" Orientation="Horizontal">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="OnFlyoutHeaderTapped" />
</StackLayout.GestureRecognizers>
<Image
HeightRequest="64"
Source="avatar.jpg"
WidthRequest="64">
<Image.Clip>
<EllipseGeometry
Center="32,32"
RadiusX="32"
RadiusY="32" />
</Image.Clip>
</Image>
<Label
Margin="16,0,0,0"
FontSize="16"
LineBreakMode="TailTruncation"
MaxLines="2"
Text="{x:Static resx:AppResources.User_UnLogin}"
TextColor="{DynamicResource ShellTitleColor}"
VerticalTextAlignment="Center" />
</StackLayout>
</ContentView>
@@ -0,0 +1,14 @@
namespace System.Application.UI.Views.Controls;
public partial class FlyoutHeader : ContentView
{
public FlyoutHeader()
{
this.InitializeComponent();
}
void OnFlyoutHeaderTapped(object? sender, EventArgs e)
{
Shell.Current.FlyoutIsPresented = false;
}
}
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="20220814" android:versionName="2.8.3" package="net.steampp.app" android:installLocation="auto">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="20220831" android:versionName="2.8.4" package="net.steampp.app" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:fullBackupContent="@xml/auto_backup_rules"></application>
<!-- Required for API Level 30 to make sure the app can detect browsers and other apps where communication is needed.-->
+8 -8
View File
@@ -4,14 +4,14 @@
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:styles="clr-namespace:System.Application.UI.Styles">
<!--
<!--
Define global resources and styles here, that apply to all pages in your app.
-->
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary Source="Styles\ThemeLight.xaml" />
<styles:DrawingSvg x:Key="DrawingSvg" />
<!--<Color x:Key="Primary">#2196F3</Color>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary Source="Styles\ThemeLight.xaml" />
<styles:DrawingSvg x:Key="DrawingSvg" />
<!--<Color x:Key="Primary">#2196F3</Color>
<Style TargetType="Button">
<Setter Property="TextColor" Value="White"></Setter>
<Setter Property="VisualStateManager.VisualStateGroups">
@@ -31,6 +31,6 @@
</VisualStateGroupList>
</Setter>
</Style>-->
</ResourceDictionary>
</Application.Resources>
</ResourceDictionary>
</Application.Resources>
</Application>
@@ -3,16 +3,16 @@
x:Class="System.Application.UI.Styles.ThemeLight"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<Color x:Key="Primary">#2196F3</Color>
<!-- 背景颜色 -->
<Color x:Key="GreyWindowBackground">#F9F9F9</Color>
<!-- 主要文本 -->
<Color x:Key="TextPrimary">#333333</Color>
<!-- 次要文本 -->
<Color x:Key="TextSecondary">#666666</Color>
<!-- 点击文字颜色 -->
<Color x:Key="TextLink">#007ffc</Color>
<Color x:Key="ShellBackgroundColor">White</Color>
<Color x:Key="ShellTitleColor">#222222</Color>
<!--<Color x:Key="ShellForegroundColor">#767676</Color>-->
<Color x:Key="Primary">#2196F3</Color>
<!-- 背景颜色 -->
<Color x:Key="GreyWindowBackground">#F9F9F9</Color>
<!-- 主要文本 -->
<Color x:Key="TextPrimary">#333333</Color>
<!-- 次要文本 -->
<Color x:Key="TextSecondary">#666666</Color>
<!-- 点击文字颜色 -->
<Color x:Key="TextLink">#007ffc</Color>
<Color x:Key="ShellBackgroundColor">White</Color>
<Color x:Key="ShellTitleColor">#222222</Color>
<!--<Color x:Key="ShellForegroundColor">#767676</Color>-->
</ResourceDictionary>
+12 -3
View File
@@ -1,13 +1,17 @@
<Project Sdk="MSBuild.Sdk.Extras">
<PropertyGroup>
<TargetFrameworks>Xamarin.iOS10</TargetFrameworks>
<TargetFrameworks>Xamarin.iOS10;net6.0-ios</TargetFrameworks>
<RootNamespace>System.Application</RootNamespace>
<AssemblyName>System.Application.SteamTools.Client.iOS</AssemblyName>
<SupportedOSPlatformVersion Condition="$(TargetFramework.Contains('-ios'))">14.2</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$(TargetFramework.Contains('-maccatalyst'))">14.0</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition=" !($(TargetFramework.StartsWith('Xamarin.iOS'))) ">
<UseMaui>true</UseMaui>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Common.CoreLib\Properties\AssemblyInfo.cs">
<Link>Properties\AssemblyInfo.cs</Link>
@@ -24,11 +28,16 @@
<Compile Include="..\ST.Client.Android\UI\Views.Controls\*.shared.cs">
<LinkBase>UI\Views.Controls</LinkBase>
</Compile>
<Compile Include="..\ST.Client.Desktop\ServiceCollectionExtensions.AddGeneralLogging.cs" />
<Compile Include="..\ST.Client.Android\Services\Implementation\DnsAnalysisServiceImpl.cs">
<LinkBase>Services\Implementation</LinkBase>
</Compile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Common.Essentials.Xamarin\Common.Essentials.Xamarin.csproj" />
<ProjectReference Condition=" ($(TargetFramework.StartsWith('Xamarin.iOS'))) " Include="..\Common.Essentials.Xamarin\Common.Essentials.Xamarin.csproj" />
<ProjectReference Condition=" !($(TargetFramework.StartsWith('Xamarin.iOS'))) " Include="..\Common.Essentials.Maui\Common.Essentials.Maui.csproj" />
<ProjectReference Include="..\ST.Client.AppCenter\ST.Client.AppCenter.csproj" />
<ProjectReference Include="..\ST.Client\ST.Client.csproj" />
<ProjectReference Include="..\ST.Client.iOS.ResSecrets\ST.Client.iOS.ResSecrets.csproj" />
@@ -36,7 +45,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Toast.iOS" Aliases="NUGET_PACKAGE_TOAST_IOS" />
<!--<PackageReference Include="Toast.iOS" Aliases="NUGET_PACKAGE_TOAST_IOS" />-->
</ItemGroup>
<ItemGroup>
@@ -18,7 +18,7 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddSingleton<IPlatformService>(s => s.GetRequiredService<ApplePlatformServiceImpl>());
//services.AddPlatformNotificationService();
services.TryAddAppleClientHttpPlatformHelperService();
PlatformToastImpl.TryAddToast(services);
//PlatformToastImpl.TryAddToast(services);
//if (options.HasGUI)
//{
// services.AddSingleton<IBiometricService, PlatformBiometricServiceImpl>();
@@ -1,4 +1,8 @@
#if XAMARINIOS
using XEPlatform = Xamarin.Essentials.Platform;
#else
using XEPlatform = Microsoft.Maui.ApplicationModel.Platform;
#endif
namespace System.Application.Services.Implementation
{
@@ -1,36 +1,36 @@
extern alias NUGET_PACKAGE_TOAST_IOS;
//extern alias NUGET_PACKAGE_TOAST_IOS;
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;
using iOSToast = NUGET_PACKAGE_TOAST_IOS::GlobalToast.Toast;
//using Microsoft.Extensions.DependencyInjection;
//using System.Threading.Tasks;
//using iOSToast = NUGET_PACKAGE_TOAST_IOS::GlobalToast.Toast;
// TODO Use CommunityToolkit.Maui.Core.Views.PlatformToast
// https://github.com/CommunityToolkit/Maui/blob/main/src/CommunityToolkit.Maui/Alerts/Toast/Toast.macios.cs
// https://github.com/CommunityToolkit/Maui/blob/main/src/CommunityToolkit.Maui.Core/Views/Toast/PlatformToast.macios.cs
//// TODO Use CommunityToolkit.Maui.Core.Views.PlatformToast
//// https://github.com/CommunityToolkit/Maui/blob/main/src/CommunityToolkit.Maui/Alerts/Toast/Toast.macios.cs
//// https://github.com/CommunityToolkit/Maui/blob/main/src/CommunityToolkit.Maui.Core/Views/Toast/PlatformToast.macios.cs
namespace System.Application.Services.Implementation
{
/// <summary>
/// https://github.com/andrius-k/Toast
/// </summary>
internal sealed class PlatformToastImpl : ToastBaseImpl
{
public PlatformToastImpl(IToastIntercept intercept) : base(intercept)
{
}
//namespace System.Application.Services.Implementation
//{
// /// <summary>
// /// https://github.com/andrius-k/Toast
// /// </summary>
// internal sealed class PlatformToastImpl : ToastBaseImpl
// {
// public PlatformToastImpl(IToastIntercept intercept) : base(intercept)
// {
// }
protected override async void PlatformShow(string text, int duration)
{
var toast = iOSToast.ShowToast(text)
.SetDuration(duration)
.SetShowShadow(false)
.SetAutoDismiss(true)
.Show();
await Task.Delay(duration);
toast.Dismiss();
}
// protected override async void PlatformShow(string text, int duration)
// {
// var toast = iOSToast.ShowToast(text)
// .SetDuration(duration)
// .SetShowShadow(false)
// .SetAutoDismiss(true)
// .Show();
// await Task.Delay(duration);
// toast.Dismiss();
// }
internal static IServiceCollection TryAddToast(IServiceCollection services)
=> TryAddToast<PlatformToastImpl>(services);
}
}
// internal static IServiceCollection TryAddToast(IServiceCollection services)
// => TryAddToast<PlatformToastImpl>(services);
// }
//}
@@ -1,4 +1,4 @@
namespace System.Application.Models
namespace System.Application.Models
{
/// <summary>
/// 文本阅读器提供商
+6 -3
View File
@@ -85,10 +85,13 @@ namespace System.Application.Services
reverseProxyService.ProxyIp = IPAddress2.TryParse(ProxySettings.SystemProxyIp.Value, out var ip) ? ip : IPAddress.Any;
// macOS\Linux 上目前因权限问题仅支持 0.0.0.0(IPAddress.Any)
if ((OperatingSystem2.IsMacOS() || OperatingSystem2.IsLinux()) && IPAddress.IsLoopback(IReverseProxyService.Instance.ProxyIp))
if (!OperatingSystem2.IsAndroid())
{
reverseProxyService.ProxyIp = IPAddress.Any;
// macOS\Linux 上目前因权限问题仅支持 0.0.0.0(IPAddress.Any)
if ((OperatingSystem2.IsMacOS() || OperatingSystem2.IsLinux()) && IPAddress.IsLoopback(IReverseProxyService.Instance.ProxyIp))
{
reverseProxyService.ProxyIp = IPAddress.Any;
}
}
// Android VPN 模式使用 tun2socks
+4 -4
View File
@@ -10,10 +10,10 @@ public enum SmsCodeType
/// </summary>
Register = 200,
///// <summary>
///// [通过手机号码重设密码]找回密码
///// </summary>
//ForgotPassword = 201,
/// <summary>
/// [通过手机号码重设密码]找回密码
/// </summary>
ForgotPassword = 201,
/// <summary>
/// [换新手机号要旧手机号短信验证]绑定新手机号
+15 -16
View File
@@ -40,20 +40,15 @@ using System.Windows;
using Microsoft.Extensions.Http;
using Xamarin.Android.Net;
using Program = System.Application.UI.MainApplication;
using PlatformApplication = System.Application.UI.MainApplication;
#elif __IOS__
using Program = System.Application.UI.AppDelegate;
#elif !__MOBILE__
#endif
using ReactiveUI;
using System.Reactive;
#endif
#if !MAUI && !__MOBILE__
using AvaloniaApplication = Avalonia.Application;
#endif
#if __ANDROID__ && !MAUI
#else
using PlatformApplication = System.Application.UI.App;
#endif
#if ANDROID || IOS || __ANDROID__
#if MAUI
using EssentialsFileSystem = Microsoft.Maui.Storage.FileSystem;
@@ -75,7 +70,13 @@ using CreateHttpHandlerArgs = System.ValueTuple<
System.Net.CookieContainer,
System.Net.IWebProxy?,
int>;
#if MAUI || AVALONIA
using PlatformApplication = System.Application.UI.App;
#elif ANDROID || __ANDROID__
using PlatformApplication = System.Application.UI.MainApplication;
#endif
// ReSharper disable once CheckNamespace
namespace System.Application.UI
{
partial class
@@ -242,7 +243,7 @@ namespace System.Application.UI
#if MAUI || __MOBILE__ || ANDROID || IOS || __ANDROID__
services.TryAddEssentials();
#endif
#if __MOBILE__ || ANDROID || IOS || __ANDROID__
#if !MACCATALYST && (__MOBILE__ || ANDROID || IOS || __ANDROID__)
// 添加运行时权限
services.AddPlatformPermissions();
#endif
@@ -305,14 +306,12 @@ namespace System.Application.UI
services.AddSingleton(_ => PlatformApplication.Instance);
services.AddSingleton<IApplication>(s => s.GetRequiredService<PlatformApplication>());
#if __ANDROID__
#if !MAUI && __ANDROID__
services.AddSingleton<IAndroidApplication>(s => s.GetRequiredService<PlatformApplication>());
#endif
#if __MOBILE__
//services.AddMSALPublicClientApp(AppSettings.MASLClientId);
#elif MAUI
#if MAUI
services.AddSingleton<IMauiApplication>(s => s.GetRequiredService<PlatformApplication>());
#else
#elif AVALONIA
services.AddSingleton<IAvaloniaApplication>(s => s.GetRequiredService<PlatformApplication>());
services.TryAddSingleton<IClipboardPlatformService>(s => s.GetRequiredService<PlatformApplication>());
@@ -340,7 +339,7 @@ namespace System.Application.UI
* - 按钮文本(ButtonText)缺少本地化翻译(Translate)
* - 某些图标图片与枚举值不太匹配,例如 Information
*/
services.TryAddWindowManager();
services.AddWindowManager();
#if WINDOWS
// 可选项,在 Win 平台使用 WPF 实现的 MessageBox
@@ -369,7 +368,7 @@ namespace System.Application.UI
if (options.HasHttpClientFactory)
{
#if __MOBILE__
#if __MOBILE__ && !MACCATALYST
// 添加 HttpClientFactory 平台原生实现
services.AddNativeHttpClient();
#endif
@@ -380,7 +379,7 @@ namespace System.Application.UI
services.TryAddScriptManager();
if (isTrace) StartWatchTrace.Record("DI.D.ScriptManager");
#if !CONSOLEAPP
#if !CONSOLEAPP && !MACCATALYST && !IOS
if (options.HasHttpProxy)
{
// 通用 Http 代理服务
@@ -435,7 +434,7 @@ namespace System.Application.UI
#endif
if (options.HasSteam)
{
#if !__ANDROID__
#if !__ANDROID__ && !IOS
// Steam 相关助手、工具类服务
services.AddSteamService();
+69
View File
@@ -1,5 +1,11 @@
using NUnit.Framework;
using System.Security.Cryptography;
using System.Text;
#if WINDOWS
using Windows.Security.Cryptography.DataProtection;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Runtime.Versioning;
#endif
namespace System;
@@ -81,4 +87,67 @@ public class ByteArrayTest
Assert.IsTrue(value1 == value3);
}
#if WINDOWS
/// <summary>
/// Windows10DataProtectionProvider 的单元测试
/// </summary>
/// <returns></returns>
[Test]
public async Task DataProtection()
{
if (!OperatingSystem.IsWindowsVersionAtLeast(10, 0, 10240)) return;
var value = DateTime.Now.ToString() + " 🔮🎮";
var bytes = Encoding.UTF8.GetBytes(value);
var e = await ProtectCore2Async(bytes);
var d = await UnprotectCore2Async(e);
TestContext.WriteLine(Encoding.UTF8.GetString(d));
Assert.IsTrue(d.SequenceEqual(bytes));
}
#endif
#if WINDOWS
[SupportedOSPlatform("Windows10.0.10240.0")]
static DataProtectionProvider GetDataProtectionProvider(string? protectionDescriptor = null)
{
DataProtectionProvider provider = protectionDescriptor == null ? new() : new(protectionDescriptor);
return provider;
}
[SupportedOSPlatform("Windows10.0.10240.0")]
static async Task<byte[]> ProtectCoreAsync(byte[] data)
{
// LOCAL=user and LOCAL=machine do not require enterprise auth capability
var provider = GetDataProtectionProvider("LOCAL=user");
// https://appcenter.ms/orgs/BeyondDimension/apps/Steam/crashes/errors/842356268u/overview
// System.Runtime.InteropServices.COMException: 无法在设置线程模式后对其加以更改。 (0x80010106 (RPC_E_CHANGED_MODE))
var buffer = await provider.ProtectAsync(data.AsBuffer());
var encBytes = buffer.ToArray();
return encBytes;
}
[SupportedOSPlatform("Windows10.0.10240.0")]
static async Task<byte[]> ProtectCore2Async(byte[] data) => await await Task.Factory.StartNew(async () => await ProtectCoreAsync(data));
[SupportedOSPlatform("Windows10.0.10240.0")]
static async Task<byte[]> UnprotectCoreAsync(byte[] data)
{
var provider = GetDataProtectionProvider();
var buffer = await provider.UnprotectAsync(data.AsBuffer());
return buffer.ToArray();
}
[SupportedOSPlatform("Windows10.0.10240.0")]
static async Task<byte[]> UnprotectCore2Async(byte[] data) => await await Task.Factory.StartNew(async () => await UnprotectCoreAsync(data));
#endif
}
+2 -1
View File
@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">$(TargetFrameworks);net6.0-windows10.0.19041.0</TargetFrameworks>
<RootNamespace>System</RootNamespace>
<AssemblyName>System.Common.UnitTest</AssemblyName>
<IsPackable>false</IsPackable>