mirror of
https://github.com/BeyondDimension/SteamTools.git
synced 2026-10-26 12:09:32 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a503243243 | ||
|
|
405352fdbf | ||
|
|
9122069317 | ||
|
|
8a6ecf9a5d | ||
|
|
028769429e | ||
|
|
47c06f01ed | ||
|
|
0437294392 | ||
|
|
a26f58af4a | ||
|
|
8a2568ed82 | ||
|
|
5b023f10a9 | ||
|
|
26e69e2ea5 | ||
|
|
4f733a0486 | ||
|
|
c0bab2e4db | ||
|
|
bd3dac8203 | ||
|
|
e47f615b70 | ||
|
|
fc921f0442 | ||
|
|
f0d1d1452c | ||
|
|
f81aa40719 | ||
|
|
7da4381c32 | ||
|
|
d93ed5bc49 | ||
|
|
eb96db599c | ||
|
|
7e5544fcb7 | ||
|
|
4a0ec3b56e | ||
|
|
4dde59b9ee | ||
|
|
d45740dd01 | ||
|
|
fbbafdc099 | ||
|
|
d2fce502dd | ||
|
|
001141d86b | ||
|
|
58c9ae051b | ||
|
|
a5c22f665d | ||
|
|
8f0e5e714e | ||
|
|
5a03e0fee6 | ||
|
|
c7007566b5 | ||
|
|
d4381409df | ||
|
|
70f5a30e53 | ||
|
|
cff0f3578a | ||
|
|
e5fd44b218 | ||
|
|
999e1323d8 | ||
|
|
f8b8548185 | ||
|
|
f299b03a34 | ||
|
|
942d6d5fd5 | ||
|
|
5bd1076083 | ||
|
|
4b358993be | ||
|
|
586c14b961 | ||
|
|
1453ae4368 | ||
|
|
177abb2dda | ||
|
|
536b1d9ade | ||
|
|
5db27f73bf | ||
|
|
011e1bcab5 | ||
|
|
03adbc3094 | ||
|
|
adf1bf0d97 | ||
|
|
25851f7070 | ||
|
|
0da4c530ea | ||
|
|
abb476e508 | ||
|
|
b55d7bcbb5 | ||
|
|
cb556f4f38 | ||
|
|
1d7181e79d | ||
|
|
91de79c6ec | ||
|
|
fb173ae725 | ||
|
|
5fad2ffead | ||
|
|
8adf02e181 | ||
|
|
decaae56d4 | ||
|
|
49b22d2aaf | ||
|
|
450e63158c | ||
|
|
6e073df974 | ||
|
|
24026c62d7 | ||
|
|
02d88955c8 | ||
|
|
6df9610463 | ||
|
|
a8e6896c3d | ||
|
|
435ab6e034 | ||
|
|
b17c094e76 | ||
|
|
316a522721 | ||
|
|
18bf3e5809 | ||
|
|
7e8e366f66 | ||
|
|
97693cf9d9 | ||
|
|
58cf7dc69d | ||
|
|
a9fb87bb79 | ||
|
|
3994049c24 | ||
|
|
f0cca6ee6d | ||
|
|
e008fe542c | ||
|
|
ef1971a907 | ||
|
|
bb17cdeec3 | ||
|
|
760e05aa10 | ||
|
|
2a94e4a110 | ||
|
|
16ef05173d | ||
|
|
645636c4ca | ||
|
|
0d9d86c04a | ||
|
|
c0818da7db | ||
|
|
89862e94af | ||
|
|
ca1f997e7d | ||
|
|
185e8b0fe9 | ||
|
|
ab1ca7dd03 | ||
|
|
3516fbd463 | ||
|
|
c5dbac3c8e | ||
|
|
bc665d1313 | ||
|
|
e5b4c73d15 | ||
|
|
0c77f07b05 | ||
|
|
eef2b826d1 | ||
|
|
05a4b2bfbf | ||
|
|
2f1ab0dfc9 | ||
|
|
9746e11163 | ||
|
|
0b5e2cbd0c | ||
|
|
c4b104be38 | ||
|
|
28a46972ed | ||
|
|
d0f5e77f28 | ||
|
|
dad478ad5f | ||
|
|
cee29aeabe | ||
|
|
eb4f9924a8 | ||
|
|
1911141d2a | ||
|
|
82dd22467a | ||
|
|
54feb7ba60 | ||
|
|
e424352f93 | ||
|
|
81dfd5dce3 | ||
|
|
8cc303ac16 | ||
|
|
e7da3e509f | ||
|
|
dfafc52011 | ||
|
|
03bf521b51 | ||
|
|
510d45709d | ||
|
|
f58fcd40dd | ||
|
|
e638a26cb8 | ||
|
|
9d6bc2ebc8 | ||
|
|
bf762ab952 | ||
|
|
84bbb0d5c8 | ||
|
|
32b241299a | ||
|
|
6562d28336 | ||
|
|
7c26e1fbc6 | ||
|
|
f15dc904ff | ||
|
|
a6102b92d2 | ||
|
|
fdf0e43ce6 | ||
|
|
8f64b542a6 | ||
|
|
5fcab98d4d | ||
|
|
c8bf9b39f4 | ||
|
|
fcaad74bb9 | ||
|
|
8361481d5c | ||
|
|
db6bf1d673 | ||
|
|
cbb7330bda | ||
|
|
079469b89e | ||
|
|
7f45ca966e | ||
|
|
a26b9689ee | ||
|
|
53bca3f027 | ||
|
|
0cc5c28c85 | ||
|
|
32bdab41a7 | ||
|
|
6bdfb0b9e0 | ||
|
|
8558ec864f | ||
|
|
99745d8c77 | ||
|
|
e20f96753b | ||
|
|
4cc738ca38 | ||
|
|
83433aea70 | ||
|
|
d97ab12427 | ||
|
|
8bb3526e27 | ||
|
|
6894ad27e2 | ||
|
|
053259bdaf | ||
|
|
62d6f66344 | ||
|
|
1d4b18da88 | ||
|
|
90dc90329d | ||
|
|
76bf2eb80d | ||
|
|
73b60d428d | ||
|
|
695657cbbc | ||
|
|
aa20d3d19c | ||
|
|
c9b0fdbe88 | ||
|
|
35498b2751 | ||
|
|
f894f0f58b | ||
|
|
6c08f51e9f | ||
|
|
12e46bd5a0 | ||
|
|
8e98313634 | ||
|
|
b947d6cdd5 | ||
|
|
e8e64c1c74 | ||
|
|
44b24406cf | ||
|
|
a4f06a2aa5 | ||
|
|
13db927cd3 | ||
|
|
90088e8d2c | ||
|
|
4d8f577e0b | ||
|
|
6ed175a01c | ||
|
|
7794eb7495 | ||
|
|
f6c869081b | ||
|
|
8a88dd0615 | ||
|
|
b247e0f6bc | ||
|
|
6e6fd8a881 | ||
|
|
1e118ae5e0 | ||
|
|
6c126f1c1f | ||
|
|
3df3d2857f | ||
|
|
d3e6293a89 | ||
|
|
161297a478 | ||
|
|
e244056d7d | ||
|
|
2e44dfc8ef | ||
|
|
930eb2261f | ||
|
|
c57b49e8de | ||
|
|
37be6b16eb | ||
|
|
c64fe98f73 | ||
|
|
b8ebbc73b3 | ||
|
|
12512231ba | ||
|
|
a8f6f663a1 | ||
|
|
35e9910ece | ||
|
|
0d9394aea9 | ||
|
|
208435cd57 | ||
|
|
e169bb4e88 | ||
|
|
88755791b1 | ||
|
|
73ba4d01bc | ||
|
|
b230eb9839 | ||
|
|
70df1891bf | ||
|
|
7bf0bc2be7 | ||
|
|
c694bd0586 | ||
|
|
ef79ceeb84 | ||
|
|
3ee1d99330 | ||
|
|
37735e0add | ||
|
|
61e31aab82 | ||
|
|
2ef08a87ea | ||
|
|
147ff6ce9d | ||
|
|
ec43d282e6 | ||
|
|
4211ab64a2 | ||
|
|
7f8411edc5 | ||
|
|
075ff38198 | ||
|
|
3627e26c5e | ||
|
|
2e296cb3ec | ||
|
|
132dd15f97 | ||
|
|
7965d4cab4 | ||
|
|
869567fde5 |
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Bug Report 🐛
|
||||
name: "Bug Report \U0001F41B"
|
||||
about: 创建 Bug 报告以帮助我们改进 / Create a report to help us improve
|
||||
title: "🐛[BUG] "
|
||||
title: "\U0001F41B[BUG] "
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
name: Feature request 👑
|
||||
name: "Feature request \U0001F451"
|
||||
about: 对程序的需求或建议 / Suggest an idea for program
|
||||
title: "👑[Enhancement] "
|
||||
labels: enhancement
|
||||
title: "\U0001F451[Enhancement] "
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
@@ -26,4 +26,4 @@ If you have a solution, explain it clearly here
|
||||
<!--
|
||||
如截图等其他信息可以贴在这里
|
||||
Other information such as screenshots can be posted here
|
||||
-->
|
||||
-->
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Milestone
|
||||
about: Issue a summary of the fixes recorded for the next release
|
||||
title: "vTBD Milestone"
|
||||
labels: documentation
|
||||
assignees: rmbadmin, AigioL, Mossimos
|
||||
|
||||
---
|
||||
|
||||
## New release previous preparations | 发行新版本前准备工作
|
||||
- [ ] Translate resx | 翻译 resx
|
||||
- [ ] Compile msixupload package | 编译 msixupload 包
|
||||
- [ ] Github release / Gitee release | 在 Github 与 Gitee 上发布
|
||||
- [ ] Upload network disk | 上传网盘
|
||||
- [ ] Update the official website | 更新官网
|
||||
- [ ] Server publishing tool enable new version | 服务器发布工具启用新版本
|
||||
- [ ] ~~Enable MS store channel new version notification after waiting for MS store approval | 等待微软商店审核完成后启用该渠道新版本通知~~
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
name: question 🧐
|
||||
name: "Question \U0001F9D0"
|
||||
about: 对程序使用的疑问或需要帮助 / Questions about the use of the program or need help
|
||||
title: "🧐[Question] "
|
||||
labels: question
|
||||
title: "\U0001F9D0[Question] "
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
@@ -23,4 +23,4 @@ Other information such as screenshots can be posted here
|
||||
|
||||
- 程序版本(Program Version):
|
||||
- 系统版本号(OS Version): <!-- example Windows 10.19042.844(see winver) / macOS Monterey 12 / Ubuntu 20.04.2 LTS -->
|
||||
- 是否为应用商店版本(Whether it is a Store version): <!--是(Yes)/否(No)-->
|
||||
- 是否为应用商店版本(Whether it is a Store version): <!--是(Yes)/否(No)-->
|
||||
|
||||
@@ -137,6 +137,14 @@ jobs:
|
||||
with:
|
||||
name: '[${{ matrix.configuration }}]${{ env.ProjectName }}_macos_x64_${{ env.GITHUB_SHA_SHORT }}'
|
||||
path: src\ST.Client.Desktop.Avalonia.App\bin\${{ matrix.configuration }}\Publish\${{ env.ProjectName }}_macos_x64_${{ env.GITHUB_SHA_SHORT }}.7z
|
||||
|
||||
- name: Upload macos-arm64 App ${{ matrix.configuration }}
|
||||
continue-on-error: true
|
||||
if: ${{ matrix.os == 'windows-latest' && !startsWith(github.ref, 'refs/tags/') && github.event_name == 'push' }}
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: '[${{ matrix.configuration }}]${{ env.ProjectName }}_macos_arm64_${{ env.GITHUB_SHA_SHORT }}'
|
||||
path: src\ST.Client.Desktop.Avalonia.App\bin\${{ matrix.configuration }}\Publish\${{ env.ProjectName }}_macos_arm64_${{ env.GITHUB_SHA_SHORT }}.7z
|
||||
|
||||
- name: Upload linux-x64 App ${{ matrix.configuration }}
|
||||
continue-on-error: true
|
||||
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
env:
|
||||
Token: ${{ secrets.PUBLISH_TOKEN }}
|
||||
|
||||
- name: Package fd-win-x64
|
||||
- name: Package fd-win-x64 7z
|
||||
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
|
||||
shell: pwsh
|
||||
run: |
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
Copy-Item ".\src\ST.Client.Desktop.Avalonia.App\bin\${env:Configuration}\Publish\${env:ProjectName}_win_x64_fde_v${{ steps.tag.outputs.tag }}.exe" "${env:PublishFilePath}"
|
||||
Write-Output "exe_fd_win_x64_SHA256=$(.\packaging\SHA256.ps1 ${env:PublishFilePath}\${env:ProjectName}_win_x64_fde_v${{ steps.tag.outputs.tag }}.exe)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||
|
||||
- name: Package win-x64
|
||||
- name: Package win-x64 7z
|
||||
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
|
||||
shell: pwsh
|
||||
run: |
|
||||
@@ -144,7 +144,7 @@ jobs:
|
||||
Copy-Item ".\src\ST.Client.Desktop.Avalonia.App\bin\${env:Configuration}\Publish\${env:ProjectName}_win_x64_v${{ steps.tag.outputs.tag }}.exe" "${env:PublishFilePath}"
|
||||
Write-Output "exe_win_x64_SHA256=$(.\packaging\SHA256.ps1 ${env:PublishFilePath}\${env:ProjectName}_win_x64_v${{ steps.tag.outputs.tag }}.exe)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||
|
||||
- name: Package osx-64
|
||||
- name: Package osx-x64 7z
|
||||
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
|
||||
shell: pwsh
|
||||
run: |
|
||||
@@ -152,6 +152,14 @@ jobs:
|
||||
Copy-Item ".\src\ST.Client.Desktop.Avalonia.App\bin\${env:Configuration}\Publish\${env:ProjectName}_macos_x64_v${{ steps.tag.outputs.tag }}.7z" "${env:PublishFilePath}"
|
||||
Write-Output "macos_x64_SHA256=$(.\packaging\SHA256.ps1 ${env:PublishFilePath}\${env:ProjectName}_macos_x64_v${{ steps.tag.outputs.tag }}.7z)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||
|
||||
- name: Package osx-arm64 7z
|
||||
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
|
||||
shell: pwsh
|
||||
run: |
|
||||
New-Item -ItemType Directory -Path ${env:PublishFilePath} -Force > $null
|
||||
Copy-Item ".\src\ST.Client.Desktop.Avalonia.App\bin\${env:Configuration}\Publish\${env:ProjectName}_macos_arm64_v${{ steps.tag.outputs.tag }}.7z" "${env:PublishFilePath}"
|
||||
Write-Output "macos_arm64_SHA256=$(.\packaging\SHA256.ps1 ${env:PublishFilePath}\${env:ProjectName}_macos_arm64_v${{ steps.tag.outputs.tag }}.7z)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||
|
||||
- name: Package linux-x64 7z
|
||||
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }}
|
||||
shell: pwsh
|
||||
@@ -237,6 +245,7 @@ jobs:
|
||||
| <sub>${{ env.ProjectName }}_linux_arm64_v${{ steps.tag.outputs.tag }}.rpm</sub> | <sub>${{ env.rpm_linux_arm64_SHA256 }}</sub> |
|
||||
|||
|
||||
| <sub>${{ env.ProjectName }}_macos_x64_v${{ steps.tag.outputs.tag }}.7z</sub> | <sub>${{ env.macos_x64_SHA256 }}</sub> |
|
||||
| <sub>${{ env.ProjectName }}_macos_arm64_v${{ steps.tag.outputs.tag }}.7z</sub> | <sub>${{ env.macos_arm64_SHA256 }}</sub> |
|
||||
|
||||
|
||||
[](https://steampp.net)
|
||||
|
||||
@@ -2,11 +2,11 @@ name: Sync To Gitee
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, develop ]
|
||||
branches: [ main, develop, www ]
|
||||
delete:
|
||||
branches: [ main, develop ]
|
||||
branches: [ main, develop, www ]
|
||||
create:
|
||||
branches: [ main, develop ]
|
||||
branches: [ main, develop, www ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -18,4 +18,4 @@ jobs:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.GITEE_PRIVATE_KEY }}
|
||||
with:
|
||||
source-repo: "git@github.com:BeyondDimension/SteamTools.git"
|
||||
destination-repo: "git@gitee.com:rmbgame/SteamTools"
|
||||
destination-repo: "git@gitee.com:rmbgame/SteamTools"
|
||||
|
||||
+89
-91
@@ -1,22 +1,21 @@
|
||||
<Project>
|
||||
<ItemGroup Condition=" $(TargetFramework.StartsWith('netstandard')) Or $(TargetFramework.StartsWith('netcoreapp3.1'))">
|
||||
<!-- Microsoft.Extensions.* & AspNetCore & EntityFrameworkCore(NETCOREAPP31) -->
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="3.1.21" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="3.1.21" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="3.1.21" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.21" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.AzureAppServices" Version="3.1.21" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.21" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="3.1.21" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.21" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.21" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.21" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.21" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.21" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="3.1.21" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="3.1.21" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.21" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="3.1.21" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="3.1.22" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="3.1.22" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="3.1.22" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.22" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.AzureAppServices" Version="3.1.22" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.22" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="3.1.22" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.22" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.22" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.1.22" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.22" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.22" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="3.1.22" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="3.1.22" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.22" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" $(TargetFramework.StartsWith('net5.0')) ">
|
||||
<!-- Microsoft.Extensions.* & AspNetCore & EntityFrameworkCore(NET50) -->
|
||||
@@ -35,7 +34,6 @@
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="5.0.12" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="5.0.12" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="5.0.12" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="5.0.12" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" $(TargetFramework.StartsWith('net6.0')) ">
|
||||
<!-- Microsoft.Extensions.* & AspNetCore & EntityFrameworkCore(NET60) -->
|
||||
@@ -43,28 +41,27 @@
|
||||
<PackageVersion Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.AzureAppServices" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.AzureAppServices" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.1" />
|
||||
</ItemGroup>
|
||||
<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.13.2" />
|
||||
<PackageVersion Include="Z.EntityFramework.Plus.EFCore" Version="6.13.7" />
|
||||
|
||||
<!-- AspNetCore 6 OpenId/OAuth -->
|
||||
<PackageVersion Include="AspNet.Security.OpenId.Steam" Version="6.0.0" />
|
||||
<PackageVersion Include="AspNet.Security.OAuth.QQ" Version="6.0.0" />
|
||||
<PackageVersion Include="AspNet.Security.OAuth.Apple" Version="6.0.0" />
|
||||
<PackageVersion Include="AspNet.Security.OAuth.QQ" Version="6.0.3" />
|
||||
<PackageVersion Include="AspNet.Security.OAuth.Apple" Version="6.0.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
@@ -82,7 +79,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" $(TargetFramework.StartsWith('netstandard')) ">
|
||||
<!-- BCL(NETSTANDARD) -->
|
||||
<PackageVersion Include="System.Text.Json" Version="6.0.0" />
|
||||
<PackageVersion Include="System.Text.Json" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Text.Encodings.Web" Version="6.0.0" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="6.0.0" />
|
||||
<PackageVersion Include="System.Security.Principal.Windows" Version="5.0.0" />
|
||||
@@ -97,24 +94,24 @@
|
||||
<!--<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" />-->
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22000.196" />
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22000.197" />
|
||||
|
||||
<!-- AppCenter.* -->
|
||||
<PackageVersion Include="Microsoft.AppCenter" Version="4.4.0" />
|
||||
<PackageVersion Include="Microsoft.AppCenter.Analytics" Version="4.4.0" />
|
||||
<PackageVersion Include="Microsoft.AppCenter.Crashes" Version="4.4.0" />
|
||||
<PackageVersion Include="Aigio.Avalonia.AppCenter" Version="4.4.0.1" />
|
||||
<PackageVersion Include="Aigio.Avalonia.AppCenter.Analytics" Version="4.4.0" />
|
||||
<PackageVersion Include="Aigio.Avalonia.AppCenter.Crashes" Version="4.4.0" />
|
||||
<PackageVersion Include="nor0x.AppCenter" Version="4.4.0-XMAC" />
|
||||
<PackageVersion Include="Microsoft.AppCenter" Version="4.5.0" />
|
||||
<PackageVersion Include="Microsoft.AppCenter.Analytics" Version="4.5.0" />
|
||||
<PackageVersion Include="Microsoft.AppCenter.Crashes" Version="4.5.0" />
|
||||
<PackageVersion Include="Aigio.Avalonia.AppCenter" Version="4.5.0" />
|
||||
<PackageVersion Include="Aigio.Avalonia.AppCenter.Analytics" Version="4.5.0" />
|
||||
<PackageVersion Include="Aigio.Avalonia.AppCenter.Crashes" Version="4.5.0" />
|
||||
<!--<PackageVersion Include="nor0x.AppCenter" Version="4.4.0-XMAC" />
|
||||
<PackageVersion Include="nor0x.AppCenter.Analytics" Version="4.4.0-XMAC" />
|
||||
<PackageVersion Include="nor0x.AppCenter.Crashes" Version="4.4.0-XMAC" />
|
||||
<PackageVersion Include="nor0x.AppCenter.Crashes" Version="4.4.0-XMAC" />-->
|
||||
|
||||
<!-- .NET Platform Extensions 6 -->
|
||||
<PackageVersion Include="System.Security.Cryptography.ProtectedData" Version="6.0.0" />
|
||||
|
||||
<!-- Xamarin -->
|
||||
<PackageVersion Include="Xamarin.Essentials" Version="1.7.0" />
|
||||
<PackageVersion Include="Xamarin.Essentials" Version="1.7.1" />
|
||||
<!-- Xamarin.Forms -->
|
||||
<PackageVersion Include="Xamarin.Forms" Version="5.0.0.2244" />
|
||||
<PackageVersion Include="Xamarin.Forms.Visual.Material" Version="5.0.0.2244" />
|
||||
@@ -122,7 +119,7 @@
|
||||
|
||||
<!-- NUnit & NUnit.Xamarin & Moq -->
|
||||
<PackageVersion Include="NUnit" Version="3.13.2" />
|
||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.2.1" />
|
||||
<PackageVersion Include="nunit.xamarin" Version="3.6.1" />
|
||||
<PackageVersion Include="PCLStorage" Version="1.0.2" />
|
||||
<PackageVersion Include="Moq" Version="4.16.1" />
|
||||
@@ -134,18 +131,18 @@
|
||||
|
||||
<!-- ReactiveUI -->
|
||||
<PackageVersion Include="System.Reactive" Version="5.0.0" />
|
||||
<PackageVersion Include="Splat" Version="14.1.1" />
|
||||
<PackageVersion Include="DynamicData" Version="7.4.3" />
|
||||
<PackageVersion Include="ReactiveUI" Version="17.1.6" />
|
||||
<!--<PackageVersion Include="ReactiveUI.Fody" Version="17.1.6" /> -->
|
||||
<PackageVersion Include="ReactiveUI.XamForms" Version="17.1.6" />
|
||||
<PackageVersion Include="ReactiveUI.AndroidX" Version="17.0.1" />
|
||||
<PackageVersion Include="Splat" Version="14.1.17" />
|
||||
<PackageVersion Include="DynamicData" Version="7.4.11" />
|
||||
<PackageVersion Include="ReactiveUI" Version="17.1.17" />
|
||||
<!--<PackageVersion Include="ReactiveUI.Fody" Version="17.1.17" /> -->
|
||||
<PackageVersion Include="ReactiveUI.XamForms" Version="17.1.17" />
|
||||
<PackageVersion Include="ReactiveUI.AndroidX" Version="17.1.17" />
|
||||
|
||||
<!-- SkiaSharp & HarfBuzzSharp -->
|
||||
<PackageVersion Include="HarfBuzzSharp" Version="2.6.1.8" />
|
||||
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.6.1.8" />
|
||||
<PackageVersion Include="SkiaSharp" Version="2.80.3" />
|
||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.80.3" />
|
||||
<PackageVersion Include="HarfBuzzSharp" Version="2.8.2-preview.179" />
|
||||
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2-preview.179" />
|
||||
<PackageVersion Include="SkiaSharp" Version="2.88.0-preview.179" />
|
||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.0-preview.179" />
|
||||
|
||||
<!-- AutoMapper -->
|
||||
<PackageVersion Include="AutoMapper" Version="10.1.1" />
|
||||
@@ -156,13 +153,13 @@
|
||||
|
||||
<!-- QRCode -->
|
||||
<!--<PackageVersion Include="QRCoder" Version="1.4.2" />-->
|
||||
<PackageVersion Include="Net.Codecrete.QrCodeGenerator" Version="2.0.0" />
|
||||
<PackageVersion Include="Net.Codecrete.QrCodeGenerator" Version="2.0.1" />
|
||||
|
||||
<!-- NotifyIcon -->
|
||||
<PackageVersion Include="NotifyIcon.Base" Version="1.0.5" />
|
||||
<!--<PackageVersion Include="NotifyIcon.Linux" Version="1.0.5" />-->
|
||||
<!--<PackageVersion Include="NotifyIcon.Mac" Version="1.0.5" />-->
|
||||
<PackageVersion Include="NotifyIcon.Windows" Version="1.0.5" />
|
||||
<PackageVersion Include="NotifyIcon.Base" Version="1.0.5.1" />
|
||||
<!--<PackageVersion Include="NotifyIcon.Linux" Version="1.0.5.1" />-->
|
||||
<!--<PackageVersion Include="NotifyIcon.Mac" Version="1.0.5.1" />-->
|
||||
<PackageVersion Include="NotifyIcon.Windows" Version="1.0.5.1" />
|
||||
<!--<PackageVersion Include="GtkSharp" Version="3.24.24.34" />-->
|
||||
|
||||
<!-- Compression -->
|
||||
@@ -181,7 +178,7 @@
|
||||
<PackageVersion Include="TinyPinyin.Net" Version="1.0.2" />
|
||||
|
||||
<!-- Win32/Windows -->
|
||||
<PackageVersion Include="TaskScheduler" Version="2.9.2" />
|
||||
<PackageVersion Include="TaskScheduler" Version="2.10.0" />
|
||||
|
||||
<!-- Swagger -->
|
||||
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.2.3" />
|
||||
@@ -203,18 +200,18 @@
|
||||
<PackageVersion Include="MonoMac.NetStandard" Version="0.0.4" />
|
||||
|
||||
<!-- .NET Compat -->
|
||||
<PackageVersion Include="OperatingSystem2" Version="1.4.2" />
|
||||
<PackageVersion Include="JustArchiNET.Madness" Version="3.0.0" />
|
||||
<PackageVersion Include="OperatingSystem2" Version="1.4.3" />
|
||||
<PackageVersion Include="JustArchiNET.Madness" Version="3.2.1" />
|
||||
|
||||
<!-- Serialize -->
|
||||
<PackageVersion Include="MessagePack" Version="2.3.85" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
|
||||
<!-- Html Parser -->
|
||||
<PackageVersion Include="HtmlAgilityPack" Version="1.11.39" />
|
||||
<PackageVersion Include="HtmlAgilityPack" Version="1.11.41" />
|
||||
|
||||
<!-- Clipboard -->
|
||||
<PackageVersion Include="TextCopy" Version="4.3.1" />
|
||||
<PackageVersion Include="TextCopy" Version="6.1.0" />
|
||||
|
||||
<!-- Nito.* -->
|
||||
<PackageVersion Include="Nito.Comparers" Version="6.2.2" />
|
||||
@@ -227,60 +224,61 @@
|
||||
<PackageVersion Include="Fleck" Version="1.2.0" />
|
||||
|
||||
<!-- DNS -->
|
||||
<PackageVersion Include="DnsClient" Version="1.5.0" />
|
||||
<PackageVersion Include="DnsClient" Version="1.6.0" />
|
||||
|
||||
<!-- Cryptographic Algorithms & Protocols -->
|
||||
<PackageVersion Include="Portable.BouncyCastle" Version="1.9.0" />
|
||||
|
||||
<!-- Resilience & Transient Fault Handling -->
|
||||
<PackageVersion Include="Polly" Version="7.2.2" />
|
||||
<PackageVersion Include="Polly" Version="7.2.3" />
|
||||
|
||||
<!--Avalonia Start-->
|
||||
<PackageVersion Include="Avalonia" Version="0.10.10" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="0.10.10" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.10" />
|
||||
<PackageVersion Include="Avalonia.ReactiveUI" Version="0.10.10" />
|
||||
<!--<PackageVersion Include="Avalonia.Direct2D1" Version="0.10.9" />-->
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.10" />
|
||||
<PackageVersion Include="Avalonia.Xaml.Behaviors" Version="0.10.10.4" />
|
||||
<PackageVersion Include="Avalonia" Version="0.10.12" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="0.10.12" />
|
||||
<PackageVersion Include="Avalonia.Skia" Version="0.10.12" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.12" />
|
||||
<PackageVersion Include="Avalonia.ReactiveUI" Version="0.10.12" />
|
||||
<!--<PackageVersion Include="Avalonia.Direct2D1" Version="0.10.12" />-->
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.12" />
|
||||
<PackageVersion Include="Avalonia.Xaml.Behaviors" Version="0.10.12" />
|
||||
<!--<PackageVersion Include="Avalonia.AvaloniaEdit" Version="0.10.4" />-->
|
||||
<!--<PackageVersion Include="CefNet.Avalonia" Version="93.1.21240.857" />-->
|
||||
<!--<PackageVersion Include="LibVLCSharp.Avalonia" Version="3.5.1" />-->
|
||||
<!--<PackageVersion Include="FluentAvaloniaUI" Version="1.1.6" />-->
|
||||
<PackageVersion Include="JumpLists.Win32.Avalonia" Version="0.0.1" />
|
||||
<!--<PackageVersion Include="FluentAvaloniaUI" Version="1.1.8" />-->
|
||||
<PackageVersion Include="JumpLists.Win32.Avalonia" Version="1.0.0" />
|
||||
<!--Avalonia End-->
|
||||
|
||||
<!-- Xamarin.Android -->
|
||||
<PackageVersion Include="Xamarin.Google.Dagger" Version="2.39.1" />
|
||||
<PackageVersion Include="Xamarin.Google.MLKit.BarcodeScanning" Version="116.1.2.2" />
|
||||
<PackageVersion Include="Xamarin.Google.MLKit.BarcodeScanning" Version="116.1.2.3" />
|
||||
<PackageVersion Include="Plugin.CurrentActivity" Version="2.1.0.4" />
|
||||
<PackageVersion Include="de.hdodenhof.circleimageview" Version="3.1.0" />
|
||||
<PackageVersion Include="Square.OkHttp3" Version="4.9.2" />
|
||||
<PackageVersion Include="Square.Picasso" Version="2.71828.0" />
|
||||
<PackageVersion Include="LeakCanaryBinding" Version="1.5.1.1" />
|
||||
<!--<PackageVersion Include="LeakCanaryBinding" Version="1.5.1.1" />-->
|
||||
<!--<PackageVersion Include="TencentOpenApiSDK.Android" Version="3.5.4.11" />-->
|
||||
|
||||
<!-- Xamarin.AndroidX -->
|
||||
<PackageVersion Include="Xamarin.AndroidX.Migration" Version="1.0.8" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.4" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Emoji.AppCompat" Version="1.1.0.5" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Fragment" Version="1.3.6.3" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Fragment" Version="1.3.6.4" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Navigation.Fragment" Version="2.3.5.3" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.RecyclerView" Version="1.2.1.3" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.ViewPager2" Version="1.0.0.12" />
|
||||
<PackageVersion Include="Xamarin.Google.Android.Material" Version="1.4.0.4" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.SwipeRefreshLayout" Version="1.1.0.5" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.ConstraintLayout" Version="2.1.1.2" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.RecyclerView" Version="1.2.1.4" />
|
||||
<PackageVersion Include="Xamarin.Google.Android.Material" Version="1.4.0.5" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.SwipeRefreshLayout" Version="1.1.0.6" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.ConstraintLayout" Version="2.1.2.1" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Navigation.UI" Version="2.3.5.3" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.ViewPager2" Version="1.0.0.12" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Activity" Version="1.3.1.2" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.ViewPager2" Version="1.0.0.13" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Activity" Version="1.4.0.1" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Biometric" Version="1.1.0.6" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.ExifInterface" Version="1.3.3.2" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Legacy.Support.Core.UI" Version="1.0.0.11" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Lifecycle.LiveData" Version="2.3.1.3" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Camera.Camera2" Version="1.0.2" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Camera.Lifecycle" Version="1.0.2" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.ExifInterface" Version="1.3.3.3" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Legacy.Support.Core.UI" Version="1.0.0.12" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Lifecycle.LiveData" Version="2.4.0.1" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Camera.Camera2" Version="1.0.2.2" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Camera.Lifecycle" Version="1.0.2.2" />
|
||||
<PackageVersion Include="Xamarin.AndroidX.Camera.View" Version="1.0.0.5-alpha20" />
|
||||
<!--<PackageVersion Include="Xamarin.AndroidX.Window" Version="1.0.0.3-beta02" />-->
|
||||
|
||||
<!-- Xamarin.iOS -->
|
||||
<PackageVersion Include="Toast.iOS" Version="1.2.0" />
|
||||
|
||||
+22
-14
@@ -13,7 +13,7 @@ English | [简体中文](./README.md)
|
||||
[](https://github.com/rmbadmin/SteamTools/network/members)
|
||||

|
||||
[](https://github.com/BeyondDimension/SteamTools/search?l=c%23)
|
||||
[](https://github.com/dotnet/core/blob/main/release-notes/6.0/6.0.0/6.0.0.md?WT.mc_id=dotnet-35129-website)
|
||||
[](https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-6)
|
||||
[](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10)
|
||||
|
||||
[](https://github.com/AvaloniaUI/Avalonia)
|
||||
@@ -33,6 +33,7 @@ English | [简体中文](./README.md)
|
||||
- [Gitee Releases](https://gitee.com/rmbgame/SteamTools/releases)
|
||||
- [Official WebSite](https://steampp.net)
|
||||
- [](https://www.microsoft.com/store/apps/9MTCFHS560NG)
|
||||
- [Arch Linux Package](https://aur.archlinux.org/packages/steam%2B%2B-bin)(By [zhanghua000](https://github.com/zhanghua000))
|
||||
|
||||
## ✨ Functions
|
||||
1. Local reverse proxy for Steam's community webpage to enable normal access in **the Chinese Mainland, Mainland of China**
|
||||
@@ -52,8 +53,8 @@ English | [简体中文](./README.md)
|
||||
OS | Version | Architectures | Lifecycle
|
||||
--------------------------------------|-------------------------|-----------------|----------
|
||||
[Windows Client][Windows-client] | 7 SP1(**\***), 8.1 | x64 | [Windows][Windows-lifecycle]
|
||||
[Windows 10 Client][Windows-client] | Version 1607+(**\***) | x64 | [Windows][Windows-lifecycle]
|
||||
[Windows Server][Windows-Server] | 2012+ | x64 | [Windows Server][Windows-Server-lifecycle]
|
||||
[Windows 10 Client][Windows-client] | Version 1607+(**\***) | x64 | [Windows][Windows-lifecycle]
|
||||
[Windows Server][Windows-Server] | 2012+ | x64 | [Windows Server][Windows-Server-lifecycle]
|
||||
|
||||
**\*** Windows 7 SP1 is supported with [Extended Security Updates](https://docs.microsoft.com/troubleshoot/windows-client/windows-7-eos-faq/windows-7-extended-security-updates-faq) installed.
|
||||
**\*** Microsoft Store/Desktop Bridge Version 1809+
|
||||
@@ -69,15 +70,16 @@ OS | Version | Architectures
|
||||
|
||||
OS | Version | Architectures | Lifecycle
|
||||
--------------------------------------|-----------------------|-------------------|----------
|
||||
[Alpine Linux][Alpine] | 3.13+ | x64, Arm64, Arm32 | [Alpine][Alpine-lifecycle]
|
||||
[Alpine Linux][Alpine] | 3.13+ | x64, Arm64 | [Alpine][Alpine-lifecycle]
|
||||
[CentOS][CentOS] | 7+ | x64 | [CentOS][CentOS-lifecycle]
|
||||
[Debian][Debian] | 10+ | x64, Arm64, Arm32 | [Debian][Debian-lifecycle]
|
||||
[Debian][Debian] | 10+ | x64, Arm64 | [Debian][Debian-lifecycle]
|
||||
[Fedora][Fedora] | 33+ | x64 | [Fedora][Fedora-lifecycle]
|
||||
[openSUSE][OpenSUSE] | 15+ | x64 | [OpenSUSE][OpenSUSE-lifecycle]
|
||||
[Red Hat Enterprise Linux][RHEL] | 7+ | x64, Arm64 | [Red Hat][RHEL-lifecycle]
|
||||
[SUSE Enterprise Linux (SLES)][SLES] | 12 SP2+ | x64 | [SUSE][SLES-lifecycle]
|
||||
[Ubuntu][Ubuntu] | 16.04, 18.04, 20.04+ | x64, Arm64, Arm32 | [Ubuntu][Ubuntu-lifecycle]
|
||||
[Deepin / UOS][Deepin] | 20+ | x64 | [Deepin][Deepin-lifecycle]
|
||||
[Ubuntu][Ubuntu] | 16.04, 18.04, 20.04+ | x64, Arm64 | [Ubuntu][Ubuntu-lifecycle]
|
||||
[Deepin / UOS][Deepin] | 20+ | x64 | [Deepin][Deepin-lifecycle]
|
||||
[Arch Linux][Arch] | | x64 |
|
||||
|
||||
[Alpine]: https://alpinelinux.org/
|
||||
[Alpine-lifecycle]: https://wiki.alpinelinux.org/wiki/Alpine_Linux:Releases
|
||||
@@ -109,28 +111,32 @@ OS | Version | Architectures
|
||||
[Ubuntu-pm]: https://docs.microsoft.com/dotnet/core/install/linux-package-manager-ubuntu-2004
|
||||
[Deepin]: https://www.deepin.org/
|
||||
[Deepin-lifecycle]: https://www.deepin.org/release-notes
|
||||
[Arch]: https://archlinux.org/
|
||||
|
||||
### macOS
|
||||
|
||||
OS | Version | Architectures |
|
||||
------------------------------|---------------------------|-------------------|
|
||||
[macOS][macOS] | 10.14+ | x64 |
|
||||
[macOS][macOS] | 10.14+ | x64 |
|
||||
|
||||
[macOS]: https://support.apple.com/macos
|
||||
|
||||
### Android
|
||||
|
||||
OS | Version | Architectures |
|
||||
------------------------------|-------------------------|-------------------|
|
||||
[Android][Android] | 5.0(API 21)+ | [x86](https://developer.android.google.cn/ndk/guides/abis?hl=zh_cn#x86), [Arm32](https://developer.android.google.cn/ndk/guides/abis?hl=zh_cn#v7a), [Arm64](https://developer.android.google.cn/ndk/guides/abis?hl=zh_cn#arm64-v8a) |
|
||||
OS | Version | Architectures |
|
||||
------------------------------|-------------------------|--------------------------------------------------------------------|
|
||||
[Android][Android] | 5.0(API 21)+ | [x64][Android-x64], [Arm64][Android-Arm64], [Arm32][Android-Arm32] |
|
||||
|
||||
[Android]: https://support.google.com/android
|
||||
[Android-x64]: https://developer.android.google.cn/ndk/guides/abis?hl=zh_cn#86-64
|
||||
[Android-Arm32]: https://developer.android.google.cn/ndk/guides/abis?hl=zh_cn#v7a
|
||||
[Android-Arm64]: https://developer.android.google.cn/ndk/guides/abis?hl=zh_cn#arm64-v8a
|
||||
|
||||
### ~~iOS / iPadOS~~
|
||||
|
||||
OS | Version | Architectures |
|
||||
------------------------------|-------------------------|-------------------|
|
||||
[iOS][iOS] | 10.0+ | x64, Arm32, Arm64 |
|
||||
[iOS][iOS] | 10.0+ | x64, Arm64, Arm32 |
|
||||
|
||||
[iOS]: https://support.apple.com/ios
|
||||
|
||||
@@ -170,7 +176,7 @@ Read what we [milestones](https://github.com/BeyondDimension/SteamTools/mileston
|
||||
- [NUnit VS Templates](https://marketplace.visualstudio.com/items?itemName=NUnitDevelopers.NUnitTemplatesforVisualStudio)
|
||||
|
||||
[OpenJDK 11](https://docs.microsoft.com/java/openjdk/download#openjdk-11)
|
||||
[Android Studio 2020.3.1 Or Higher](https://developer.android.com/studio/)
|
||||
[Android Studio 2021.1.1 Or Higher](https://developer.android.com/studio/)
|
||||
[Xcode 13 Or Higher](https://developer.apple.com/xcode/)
|
||||
|
||||
## 📄 Thanks to the following Open Source Projects
|
||||
@@ -194,11 +200,13 @@ Read what we [milestones](https://github.com/BeyondDimension/SteamTools/mileston
|
||||
* [DnsClient.NET](https://github.com/MichaCo/DnsClient.NET)
|
||||
* [Costura.Fody](https://github.com/Fody/Costura)
|
||||
* [MessagePack-CSharp](https://github.com/neuecc/MessagePack-CSharp)
|
||||
* [CSharpVitamins.ShortGuid](https://github.com/AigioL/CSharpVitamins.ShortGuid)
|
||||
* [Nito.Comparers](https://github.com/StephenCleary/Comparers)
|
||||
* [Nito.Disposables](https://github.com/StephenCleary/Disposables)
|
||||
* [Crc32.NET](https://github.com/force-net/Crc32.NET)
|
||||
* [gfoidl.Base64](https://github.com/gfoidl/Base64)
|
||||
* [sqlite-net-pcl](https://github.com/praeclarum/sqlite-net)
|
||||
* [AutoMapper](https://github.com/AutoMapper/AutoMapper)
|
||||
* [Polly](https://github.com/App-vNext/Polly)
|
||||
* [TaskScheduler](https://github.com/dahall/taskscheduler)
|
||||
* [SharpZipLib](https://github.com/icsharpcode/SharpZipLib)
|
||||
@@ -252,4 +260,4 @@ Read what we [milestones](https://github.com/BeyondDimension/SteamTools/mileston
|
||||
* [command-line-api](https://github.com/dotnet/command-line-api)
|
||||
* [.NET Runtime](https://github.com/dotnet/runtime)
|
||||
* [Fluent UI System Icons](https://github.com/microsoft/fluentui-system-icons)
|
||||
* [Material design icons](https://github.com/google/material-design-icons)
|
||||
* [Material design icons](https://github.com/google/material-design-icons)
|
||||
@@ -13,7 +13,7 @@
|
||||
[](https://github.com/BeyondDimension/SteamTools/network/members)
|
||||

|
||||
[](https://github.com/BeyondDimension/SteamTools/search?l=c%23)
|
||||
[](https://github.com/dotnet/core/blob/main/release-notes/6.0/6.0.0/6.0.0.md?WT.mc_id=dotnet-35129-website)
|
||||
[](https://docs.microsoft.com/zh-cn/dotnet/core/whats-new/dotnet-6)
|
||||
[](https://docs.microsoft.com/zh-cn/dotnet/csharp/whats-new/csharp-10)
|
||||
|
||||
[](https://github.com/AvaloniaUI/Avalonia)
|
||||
@@ -34,6 +34,7 @@
|
||||
- [Gitee Releases](https://gitee.com/rmbgame/SteamTools/releases)
|
||||
- [Official WebSite](https://steampp.net)
|
||||
- [](https://www.microsoft.com/store/apps/9MTCFHS560NG)
|
||||
- [Arch Linux Package](https://aur.archlinux.org/packages/steam%2B%2B-bin)(By [zhanghua000](https://github.com/zhanghua000))
|
||||
|
||||
## ✨ 功能
|
||||
1. 反代 Steam 的社区网页使其能正常访问
|
||||
@@ -61,8 +62,8 @@
|
||||
OS | Version | Architectures | Lifecycle
|
||||
--------------------------------------|-------------------------|-----------------|----------
|
||||
[Windows Client][Windows-client] | 7 SP1(**\***), 8.1 | x64 | [Windows][Windows-lifecycle]
|
||||
[Windows 10 Client][Windows-client] | Version 1607+(**\***) | x64 | [Windows][Windows-lifecycle]
|
||||
[Windows Server][Windows-Server] | 2012+ | x64 | [Windows Server][Windows-Server-lifecycle]
|
||||
[Windows 10 Client][Windows-client] | Version 1607+(**\***) | x64 | [Windows][Windows-lifecycle]
|
||||
[Windows Server][Windows-Server] | 2012+ | x64 | [Windows Server][Windows-Server-lifecycle]
|
||||
|
||||
**\*** Windows 7 SP1 is supported with [Extended Security Updates](https://docs.microsoft.com/troubleshoot/windows-client/windows-7-eos-faq/windows-7-extended-security-updates-faq) installed.
|
||||
**\*** Microsoft Store/Desktop Bridge Version 1809+
|
||||
@@ -78,15 +79,16 @@ OS | Version | Architectures
|
||||
|
||||
OS | Version | Architectures | Lifecycle
|
||||
--------------------------------------|-----------------------|-------------------|----------
|
||||
[Alpine Linux][Alpine] | 3.13+ | x64, Arm64, Arm32 | [Alpine][Alpine-lifecycle]
|
||||
[Alpine Linux][Alpine] | 3.13+ | x64, Arm64 | [Alpine][Alpine-lifecycle]
|
||||
[CentOS][CentOS] | 7+ | x64 | [CentOS][CentOS-lifecycle]
|
||||
[Debian][Debian] | 10+ | x64, Arm64, Arm32 | [Debian][Debian-lifecycle]
|
||||
[Debian][Debian] | 10+ | x64, Arm64 | [Debian][Debian-lifecycle]
|
||||
[Fedora][Fedora] | 33+ | x64 | [Fedora][Fedora-lifecycle]
|
||||
[openSUSE][OpenSUSE] | 15+ | x64 | [OpenSUSE][OpenSUSE-lifecycle]
|
||||
[Red Hat Enterprise Linux][RHEL] | 7+ | x64, Arm64 | [Red Hat][RHEL-lifecycle]
|
||||
[SUSE Enterprise Linux (SLES)][SLES] | 12 SP2+ | x64 | [SUSE][SLES-lifecycle]
|
||||
[Ubuntu][Ubuntu] | 16.04, 18.04, 20.04+ | x64, Arm64, Arm32 | [Ubuntu][Ubuntu-lifecycle]
|
||||
[Deepin / UOS][Deepin] | 20+ | x64 | [Deepin][Deepin-lifecycle]
|
||||
[Ubuntu][Ubuntu] | 16.04, 18.04, 20.04+ | x64, Arm64 | [Ubuntu][Ubuntu-lifecycle]
|
||||
[Deepin / UOS][Deepin] | 20+ | x64 | [Deepin][Deepin-lifecycle]
|
||||
[Arch Linux][Arch] | | x64 |
|
||||
|
||||
[Alpine]: https://alpinelinux.org/
|
||||
[Alpine-lifecycle]: https://wiki.alpinelinux.org/wiki/Alpine_Linux:Releases
|
||||
@@ -118,22 +120,24 @@ OS | Version | Architectures
|
||||
[Ubuntu-pm]: https://docs.microsoft.com/dotnet/core/install/linux-package-manager-ubuntu-2004
|
||||
[Deepin]: https://www.deepin.org/
|
||||
[Deepin-lifecycle]: https://www.deepin.org/release-notes
|
||||
[Arch]: https://archlinux.org/
|
||||
|
||||
### macOS
|
||||
|
||||
OS | Version | Architectures |
|
||||
------------------------------|---------------------------|-------------------|
|
||||
[macOS][macOS] | 10.14+ | x64 |
|
||||
[macOS][macOS] | 10.14+ | x64 |
|
||||
|
||||
[macOS]: https://support.apple.com/macos
|
||||
|
||||
### Android
|
||||
|
||||
OS | Version | Architectures |
|
||||
------------------------------|-------------------------|-------------------|
|
||||
[Android][Android] | 5.0(API 21)+ | [x86](https://developer.android.google.cn/ndk/guides/abis?hl=zh_cn#x86), [Arm32](https://developer.android.google.cn/ndk/guides/abis?hl=zh_cn#v7a), [Arm64](https://developer.android.google.cn/ndk/guides/abis?hl=zh_cn#arm64-v8a) |
|
||||
OS | Version | Architectures |
|
||||
------------------------------|-------------------------|--------------------------------------------------------------------|
|
||||
[Android][Android] | 5.0(API 21)+ | [x64][Android-x64], [Arm64][Android-Arm64], [Arm32][Android-Arm32] |
|
||||
|
||||
[Android]: https://support.google.com/android
|
||||
[Android-x64]: https://developer.android.google.cn/ndk/guides/abis?hl=zh_cn#86-64
|
||||
[Android-Arm32]: https://developer.android.google.cn/ndk/guides/abis?hl=zh_cn#v7a
|
||||
[Android-Arm64]: https://developer.android.google.cn/ndk/guides/abis?hl=zh_cn#arm64-v8a
|
||||
|
||||
### ~~iOS / iPadOS~~
|
||||
|
||||
@@ -179,7 +183,7 @@ OS | Version | Architectures |
|
||||
- [NUnit VS Templates](https://marketplace.visualstudio.com/items?itemName=NUnitDevelopers.NUnitTemplatesforVisualStudio)
|
||||
|
||||
[OpenJDK 11](https://docs.microsoft.com/zh-cn/java/openjdk/download#openjdk-11)
|
||||
[Android Studio 2020.3.1 或更高版本](https://developer.android.google.cn/studio/)
|
||||
[Android Studio 2021.1.1 或更高版本](https://developer.android.google.cn/studio/)
|
||||
[Xcode 13 或更高版本](https://developer.apple.com/xcode/)
|
||||
|
||||
## 🏗️ [项目结构](./src/README.md)
|
||||
@@ -214,11 +218,13 @@ OS | Version | Architectures |
|
||||
* [DnsClient.NET](https://github.com/MichaCo/DnsClient.NET)
|
||||
* [Costura.Fody](https://github.com/Fody/Costura)
|
||||
* [MessagePack-CSharp](https://github.com/neuecc/MessagePack-CSharp)
|
||||
* [CSharpVitamins.ShortGuid](https://github.com/AigioL/CSharpVitamins.ShortGuid)
|
||||
* [Nito.Comparers](https://github.com/StephenCleary/Comparers)
|
||||
* [Nito.Disposables](https://github.com/StephenCleary/Disposables)
|
||||
* [Crc32.NET](https://github.com/force-net/Crc32.NET)
|
||||
* [gfoidl.Base64](https://github.com/gfoidl/Base64)
|
||||
* [sqlite-net-pcl](https://github.com/praeclarum/sqlite-net)
|
||||
* [AutoMapper](https://github.com/AutoMapper/AutoMapper)
|
||||
* [Polly](https://github.com/App-vNext/Polly)
|
||||
* [TaskScheduler](https://github.com/dahall/taskscheduler)
|
||||
* [SharpZipLib](https://github.com/icsharpcode/SharpZipLib)
|
||||
|
||||
+33
-2
@@ -164,7 +164,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ST.Client.Desktop.UnitTest"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ST.Client.UnitTest.Resources", "tests\ST.Client.UnitTest.Resources\ST.Client.UnitTest.Resources.csproj", "{FB2CA9B4-71F1-48E8-B9AE-FF4375F4FE43}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Diagnostics", "src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj", "{E84FF7BC-FC2A-424E-8780-7DA9B487E23F}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Diagnostics", "src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj", "{E84FF7BC-FC2A-424E-8780-7DA9B487E23F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Skia.Internals", "src\Avalonia.Skia.Internals\Avalonia.Skia.Internals.csproj", "{47C151DA-D2B1-43F4-A04E-B454E2134852}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -1794,6 +1796,34 @@ Global
|
||||
{E84FF7BC-FC2A-424E-8780-7DA9B487E23F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E84FF7BC-FC2A-424E-8780-7DA9B487E23F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E84FF7BC-FC2A-424E-8780-7DA9B487E23F}.Release|x86.Build.0 = Release|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Release|x64.Build.0 = Release|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -1857,9 +1887,10 @@ Global
|
||||
{5D9E0EAA-2419-426B-BAB4-EA496C1FF7E9} = {CC46B486-00D7-49E6-ABA3-FDF8F4868D2B}
|
||||
{FB2CA9B4-71F1-48E8-B9AE-FF4375F4FE43} = {CC46B486-00D7-49E6-ABA3-FDF8F4868D2B}
|
||||
{E84FF7BC-FC2A-424E-8780-7DA9B487E23F} = {910D6A0F-42AB-4B65-B532-852E864EC006}
|
||||
{47C151DA-D2B1-43F4-A04E-B454E2134852} = {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.
+12
-13
@@ -1,30 +1,29 @@
|
||||
# 下载指南
|
||||
- Desktop(桌面端)
|
||||
- Windows
|
||||
- 如果你使用 Intel、AMD 的 x64 芯片的 Mac(较为**普遍**),则下载文件名中带有 **win_x64** 的文件
|
||||
- 如果你使用 ARM64 芯片的 PC(极为**稀有**),例如 **Surface Pro X**,则下载文件名中带有 **win_x64** 的文件可通过 Win11 x86 模拟运行
|
||||
- 如果你使用 Intel、AMD 的 x64 芯片的 PC(较为**普遍**),则下载文件名中带有 **win_x64** 的文件
|
||||
- 如果你已安装了 [ASP.NET Core Runtime 6.0.1](https://dotnet.microsoft.com/download/dotnet/6.0) 则下载文件名中带有 **fde** 的文件,可减少磁盘占用空间
|
||||
- [在 Windows 上安装 .NET](https://docs.microsoft.com/en-us/dotnet/core/install/windows)
|
||||
- 在 Windows 上不需要 Hosting Bundle 和 IIS support
|
||||
- [下载安装程序并手动安装(aspnetcore-runtime-6.0.1-win-x64.exe)](https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-aspnetcore-6.0.1-windows-x64-installer)
|
||||
- [使用 PowerShell 自动化安装](https://docs.microsoft.com/en-us/dotnet/core/install/windows?tabs=net60#install-with-powershell-automation)
|
||||
- macOS
|
||||
- 如果你使用 Intel、AMD 的 x64 芯片的 Mac(较为**普遍**),则下载文件名中带有 **macos_x64** 的文件
|
||||
- 如果你使用 ARM64 芯片的 Mac(较为**稀有**),例如 **M1**,则下载文件名中带有 **macos_x64** 的文件可通过 [Rosetta 2](https://support.apple.com/zh-cn/HT211861) 运行
|
||||
- 如果你使用 ARM64 芯片的 Mac(较为**稀有**),例如 **M1**,则下载文件名中带有 **macos_arm64** 的文件
|
||||
- Linux
|
||||
- 如果你使用 Intel、AMD 的 x64 芯片的 PC(较为**普遍**)则下载文件名中带有 **linux_x64** 的文件
|
||||
- 如果你使用 ARM64 芯片的 PC(较为**稀有**)例如 **Raspberry Pi Model 3+**,则下载文件名中带有 **linux_arm64** 的文件
|
||||
- Shared
|
||||
- 如果你已安装了 [ASP.NET Core Runtime 6.0.0](https://dotnet.microsoft.com/download/dotnet/6.0) 则下载文件名中带有 **fde** 的文件,可减少磁盘占用空间
|
||||
- [在 Windows 上安装 .NET](https://docs.microsoft.com/en-us/dotnet/core/install/windows)
|
||||
- 在 Windows 上不需要 Hosting Bundle 和 IIS support
|
||||
- [下载安装程序并手动安装(aspnetcore-runtime-6.0.0-win-x64.exe)](https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-aspnetcore-6.0.0-windows-x64-installer)
|
||||
- [使用 PowerShell 自动化安装](https://docs.microsoft.com/en-us/dotnet/core/install/windows?tabs=net60#install-with-powershell-automation)
|
||||
- [在 Linux 上安装 .NET](https://docs.microsoft.com/en-us/dotnet/core/install/linux)
|
||||
- 推荐 [通过 Snap 安装 .NET Runtime](https://docs.microsoft.com/zh-cn/dotnet/core/install/linux-snap)
|
||||
- ```sudo snap install dotnet-runtime-60 --classic```
|
||||
- Mobile(移动端)
|
||||
- Android
|
||||
- 如果你使用 ARM64 芯片的设备(较为**普遍**)则下载文件名中带有 **android_arm64_v8a** 的文件
|
||||
- 如果你使用 ARM32 芯片的设备(较为**稀有**)通常为 **14** 年下半年之前生产的设备,则下载文件名中带有 **android_armeabi_v7a** 的文件
|
||||
- 如果你使用 Intel、AMD 的 x86 芯片的设备(较为**稀有**)则下载文件名中带有 **android_x86** 的文件
|
||||
- 如果你使用 Intel、AMD 的 x64 芯片的设备(较为**稀有**)则下载文件名中带有 **android_x64** 的文件
|
||||
|
||||
<!--
|
||||
- 如果你使用 ARM64 芯片的 PC(极为**稀有**),例如 **Surface Pro X**,则下载文件名中带有 **win_x64** 的文件可通过 Win11 x86 模拟运行
|
||||
- **[暂未支持]** ~~如果你使用 ARM64 芯片的 PC(极为**稀有**),例如 **Surface Pro X**,则下载文件名中带有 **win_arm64** 的文件~~
|
||||
- **[暂未支持]** ~~如果你使用 ARM64 芯片的 Mac(较为**稀有**),例如 **M1**,则下载文件名中带有 **macos_arm64** 的文件~~
|
||||
- [在 Linux 上安装 .NET](https://docs.microsoft.com/en-us/dotnet/core/install/linux)
|
||||
- 推荐 [通过 Snap 安装 .NET Runtime](https://docs.microsoft.com/zh-cn/dotnet/core/install/linux-snap)
|
||||
- ```sudo snap install dotnet-runtime-60 --classic```
|
||||
-->
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"msbuild-sdks": {
|
||||
"MSBuild.Sdk.Extras": "3.0.38"
|
||||
"MSBuild.Sdk.Extras": "3.0.44"
|
||||
},
|
||||
"sdk": {
|
||||
"version": "6.0.100"
|
||||
"version": "6.0.101"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,9 +33,9 @@ emit_plist() {
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>net.steampp.app</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.6.3</string>
|
||||
<string>2.6.5</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2.6.3.0</string>
|
||||
<string>2.6.5.0</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.14</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ $proj_path = "$RootPath\src\ST.Client.Desktop.Avalonia.App\ST.Client.Avalonia.Ap
|
||||
$publishtool_dir = "$RootPath\src\ST.Tools.Publish"
|
||||
$publishtool_exe = "$publishtool_dir\bin\Release\net6.0\p.exe"
|
||||
|
||||
$build_pubxmls = "fd-win-x64","win-x64","osx-x64","linux-x64","linux-arm64"
|
||||
$build_pubxmls = "fd-win-x64","win-x64","osx-x64","linux-x64","linux-arm64","osx-arm64"
|
||||
|
||||
function Build-PublishTool
|
||||
{
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Submodule references/ArchiSteamFarm updated: 5992b095d6...a40c9bb0a2
Submodule references/FluentAvalonia updated: 19d99bf175...af86596b10
Submodule references/Titanium-Web-Proxy updated: f7cf7a0c72...29256a4b4f
Submodule references/dotnet-packaging updated: fb0ad36640...3f7bd3c61a
+2
-2
@@ -51,13 +51,13 @@
|
||||
- [尚未公证](https://support.apple.com/zh-cn/guide/mac-help/mh40616/10.15/mac/10.15),这会影响 macOS Catalina(版本 10.15)以上版本
|
||||
- Linux
|
||||
- **Hosts 代理模式**可能无法配置成功,推荐使用**系统代理模式**
|
||||
- 系统代理模式下discord更新下载加速有问题暂不可用
|
||||
- 系统代理模式下 Discord 更新下载加速不可用
|
||||
- 窗口弹出位置不正确
|
||||
- 鼠标指针浮动样式不正确
|
||||
- Windows
|
||||
- Windows 11
|
||||
- 在 CPU 不受支持的 Win11 上无法启动,Windows 日志中显示 ```Failed to create CoreCLR, HRESULT: 0x80004005```
|
||||
- 仅 .NET 6.0 受此影响,在几周后的 Insider 中会修复,见 [issue](https://github.com/dotnet/core/issues/6733)
|
||||
- 仅 .NET 6.0 受此影响,在内部版本 22509 中修复,见 [issue](https://github.com/dotnet/core/issues/6733)
|
||||
- **解决方案:** 可尝试使用旧版本 例如 v2.3.0
|
||||
- Windows 7
|
||||
- 先决条件
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="Ninject" Version="3.3.4" />
|
||||
<PackageReference Include="Portable.BouncyCastle" Version="1.8.10" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.1" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||
<PackageReference Include="System.Management" Version="5.0.0" />
|
||||
<PackageReference Include="TaskScheduler" Version="2.9.1" />
|
||||
</ItemGroup>
|
||||
|
||||
+22
-12
@@ -106,6 +106,10 @@ namespace System.Application.UI
|
||||
#endif
|
||||
b.AppendLine();
|
||||
|
||||
b.Append("[os.name] ");
|
||||
b.Append(DeviceInfo2.OSNameValue);
|
||||
b.AppendLine();
|
||||
|
||||
b.Append("[app.ver] ");
|
||||
#if __ANDROID__
|
||||
GetAppDisplayVersion(activity, b);
|
||||
@@ -142,6 +146,10 @@ namespace System.Application.UI
|
||||
b.Append(ApplicationUpdateServiceBaseImpl.UpdateChannelType);
|
||||
b.AppendLine();
|
||||
|
||||
b.Append("[app.install] ");
|
||||
b.Append(platformService.IsInstall);
|
||||
b.AppendLine();
|
||||
|
||||
b.Append("[deploy.mode] ");
|
||||
b.Append(IApplication.Instance.DeploymentMode);
|
||||
b.AppendLine();
|
||||
@@ -237,7 +245,9 @@ namespace System.Application.UI
|
||||
#if __ANDROID__
|
||||
b.Append("[screen] ");
|
||||
var metrics = new DisplayMetrics();
|
||||
#pragma warning disable CS0618 // 类型或成员已过时
|
||||
activity.WindowManager?.DefaultDisplay?.GetRealMetrics(metrics);
|
||||
#pragma warning restore CS0618 // 类型或成员已过时
|
||||
GetScreen(activity, metrics, b);
|
||||
static void GetScreen(Context context, DisplayMetrics metrics, StringBuilder b)
|
||||
{
|
||||
@@ -404,18 +414,18 @@ namespace System.Application.UI
|
||||
// b.Append(DeviceSecurityCheckUtil.IsEmulator.ToLowerString());
|
||||
// b.AppendLine();
|
||||
//}
|
||||
b.Append("[device.gl.renderer] ");
|
||||
b.Append(GLES20.GlGetString(GLES20.GlRenderer) ?? "");
|
||||
b.AppendLine();
|
||||
b.Append("[device.gl.vendor] ");
|
||||
b.Append(GLES20.GlGetString(GLES20.GlVendor) ?? "");
|
||||
b.AppendLine();
|
||||
b.Append("[device.gl.version] ");
|
||||
b.Append(GLES20.GlGetString(GLES20.GlVersion) ?? "");
|
||||
b.AppendLine();
|
||||
b.Append("[device.gl.extensions] ");
|
||||
b.Append(GLES20.GlGetString(GLES20.GlExtensions) ?? "");
|
||||
b.AppendLine();
|
||||
//b.Append("[device.gl.renderer] ");
|
||||
//b.Append(GLES20.GlGetString(GLES20.GlRenderer) ?? "");
|
||||
//b.AppendLine();
|
||||
//b.Append("[device.gl.vendor] ");
|
||||
//b.Append(GLES20.GlGetString(GLES20.GlVendor) ?? "");
|
||||
//b.AppendLine();
|
||||
//b.Append("[device.gl.version] ");
|
||||
//b.Append(GLES20.GlGetString(GLES20.GlVersion) ?? "");
|
||||
//b.AppendLine();
|
||||
//b.Append("[device.gl.extensions] ");
|
||||
//b.Append(GLES20.GlGetString(GLES20.GlExtensions) ?? "");
|
||||
//b.AppendLine();
|
||||
b.Append("[device.biometric] ");
|
||||
b.Append(IBiometricService.Instance.IsSupportedAsync().Result.ToLowerString());
|
||||
b.AppendLine();
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>false</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>$(MSBuildProjectDirectory)\..\..\avalonia.snk</AssemblyOriginatorKeyFile>
|
||||
<!--https://github.com/AvaloniaUI/Avalonia/blob/0.10.10/build/SharedVersion.props#L18-->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>false</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>$(MSBuildProjectDirectory)\..\..\avalonia.snk</AssemblyOriginatorKeyFile>
|
||||
<!--https://github.com/AvaloniaUI/Avalonia/blob/0.10.10/build/SharedVersion.props#L18-->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<RootNamespace>Avalonia.Skia</RootNamespace>
|
||||
<AssemblyName>Avalonia.Skia.UnitTests</AssemblyName>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>false</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>$(MSBuildProjectDirectory)\..\..\avalonia.snk</AssemblyOriginatorKeyFile>
|
||||
<!--https://github.com/AvaloniaUI/Avalonia/blob/0.10.10/build/SharedVersion.props#L18-->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia.Skia" Aliases="AvaloniaSkia" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,40 @@
|
||||
// Replace font management to implement custom default font
|
||||
extern alias AvaloniaSkia;
|
||||
|
||||
using Avalonia.Media;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using FontManagerImpl = AvaloniaSkia::Avalonia.Skia.FontManagerImpl;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Avalonia.Platform
|
||||
{
|
||||
public interface IFontManagerImpl2 : IFontManagerImpl
|
||||
{
|
||||
protected IFontManagerImpl Impl { get; }
|
||||
|
||||
string DefaultFontFamilyName { get; }
|
||||
|
||||
Typeface OnCreateGlyphTypeface(Typeface typeface);
|
||||
|
||||
string IFontManagerImpl.GetDefaultFontFamilyName() => DefaultFontFamilyName;
|
||||
|
||||
IEnumerable<string> IFontManagerImpl.GetInstalledFontFamilyNames(bool checkForUpdates)
|
||||
{
|
||||
return Impl.GetInstalledFontFamilyNames(checkForUpdates);
|
||||
}
|
||||
|
||||
bool IFontManagerImpl.TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily, CultureInfo culture, out Typeface typeface)
|
||||
{
|
||||
return Impl.TryMatchCharacter(codepoint, fontStyle, fontWeight, fontFamily, culture, out typeface);
|
||||
}
|
||||
|
||||
IGlyphTypefaceImpl IFontManagerImpl.CreateGlyphTypeface(Typeface typeface)
|
||||
{
|
||||
typeface = OnCreateGlyphTypeface(typeface);
|
||||
return Impl.CreateGlyphTypeface(typeface);
|
||||
}
|
||||
|
||||
protected static IFontManagerImpl CreateFontManager() => new FontManagerImpl();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
extern alias AvaloniaSkia;
|
||||
|
||||
using Avalonia.Media;
|
||||
using SkiaSharp;
|
||||
using @this = AvaloniaSkia::Avalonia.Skia.SKTypefaceCollection;
|
||||
|
||||
namespace Avalonia.Skia
|
||||
{
|
||||
public sealed class SKTypefaceCollection
|
||||
{
|
||||
readonly @this @this;
|
||||
internal SKTypefaceCollection(@this @this)
|
||||
{
|
||||
this.@this = @this;
|
||||
}
|
||||
|
||||
public void AddTypeface(Typeface key, SKTypeface typeface)
|
||||
{
|
||||
@this.AddTypeface(key, typeface);
|
||||
}
|
||||
|
||||
public SKTypeface Get(Typeface typeface)
|
||||
{
|
||||
return @this.Get(typeface);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
extern alias AvaloniaSkia;
|
||||
|
||||
using Avalonia.Media;
|
||||
using @this = AvaloniaSkia::Avalonia.Skia.SKTypefaceCollectionCache;
|
||||
|
||||
namespace Avalonia.Skia
|
||||
{
|
||||
public class SKTypefaceCollectionCache
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the or add typeface collection.
|
||||
/// </summary>
|
||||
/// <param name="fontFamily">The font family.</param>
|
||||
/// <returns></returns>
|
||||
public static SKTypefaceCollection GetOrAddTypefaceCollection(FontFamily fontFamily)
|
||||
{
|
||||
return new(@this.GetOrAddTypefaceCollection(fontFamily));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>false</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>$(MSBuildProjectDirectory)\..\..\avalonia.snk</AssemblyOriginatorKeyFile>
|
||||
<!--https://github.com/AvaloniaUI/Avalonia/blob/0.10.10/build/SharedVersion.props#L18-->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>false</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>$(MSBuildProjectDirectory)\..\..\avalonia.snk</AssemblyOriginatorKeyFile>
|
||||
<!--https://github.com/AvaloniaUI/Avalonia/blob/0.10.10/build/SharedVersion.props#L18-->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>false</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>$(MSBuildProjectDirectory)\..\..\avalonia.snk</AssemblyOriginatorKeyFile>
|
||||
<!--https://github.com/AvaloniaUI/Avalonia/blob/0.10.10/build/SharedVersion.props#L18-->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>false</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>$(MSBuildProjectDirectory)\..\..\avalonia.snk</AssemblyOriginatorKeyFile>
|
||||
<!--https://github.com/AvaloniaUI/Avalonia/blob/0.10.10/build/SharedVersion.props#L18-->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -267,23 +267,28 @@
|
||||
// newRating++;
|
||||
// }
|
||||
|
||||
// try
|
||||
// {
|
||||
// var opengl = Android.Opengl.GLES20.GlGetString(Android.Opengl.GLES20.GlRenderer);
|
||||
// if (!string.IsNullOrWhiteSpace(opengl))
|
||||
// {
|
||||
//#pragma warning disable CS8604 // 可能的 null 引用参数。
|
||||
// if (contains(opengl, "Bluestacks") ||
|
||||
// contains(opengl, "Translator") ||
|
||||
// contains(opengl, "youwave")
|
||||
// )
|
||||
//#pragma warning restore CS8604 // 可能的 null 引用参数。
|
||||
// newRating += 10;
|
||||
// }
|
||||
// }
|
||||
// catch
|
||||
// {
|
||||
// }
|
||||
/* signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xdd0
|
||||
* Cause: null pointer dereference
|
||||
* backtrace:
|
||||
* #00 pc 0000000000005ff4 /system/lib64/libGLESv1_CM.so (glGetString+52)
|
||||
*/
|
||||
//// try
|
||||
//// {
|
||||
//// var opengl = Android.Opengl.GLES20.GlGetString(Android.Opengl.GLES20.GlRenderer);
|
||||
//// if (!string.IsNullOrWhiteSpace(opengl))
|
||||
//// {
|
||||
////#pragma warning disable CS8604 // 可能的 null 引用参数。
|
||||
//// if (contains(opengl, "Bluestacks") ||
|
||||
//// contains(opengl, "Translator") ||
|
||||
//// contains(opengl, "youwave")
|
||||
//// )
|
||||
////#pragma warning restore CS8604 // 可能的 null 引用参数。
|
||||
//// newRating += 10;
|
||||
//// }
|
||||
//// }
|
||||
//// catch
|
||||
//// {
|
||||
//// }
|
||||
|
||||
// try
|
||||
// {
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace System.Application.UI.Adapters
|
||||
|
||||
protected virtual ItemClickEventArgs<TViewModel>? GetItemClickEventArgs(View view, TViewHolder holder, View.LongClickEventArgs? longClickEventArgs = null)
|
||||
{
|
||||
var position = holder.AdapterPosition;
|
||||
var position = holder.BindingAdapterPosition;
|
||||
if (position < 0 || position >= ViewModels.Count) return null;
|
||||
var current = ViewModels[position];
|
||||
return new PlatformItemClickEventArgs<TViewModel>(view, position, current, longClickEventArgs);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Android.Views;
|
||||
using Android.Views;
|
||||
|
||||
namespace System.Application.UI.Adapters
|
||||
{
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Android.Content;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Graphics.Drawables;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using AndroidX.Annotations;
|
||||
using AndroidX.Core.Content;
|
||||
@@ -8,7 +10,7 @@ using AndroidApplication = Android.App.Application;
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System
|
||||
{
|
||||
public static class ContextExtensions
|
||||
public static partial class ContextExtensions
|
||||
{
|
||||
/// <inheritdoc cref="ContextCompat.GetSystemService(Context, Java.Lang.Class)"/>
|
||||
public static TService GetSystemService<TService>(this Context context) where TService : class, IJavaObject
|
||||
@@ -60,5 +62,12 @@ namespace System
|
||||
/// <returns></returns>
|
||||
public static Drawable GetDrawableCompat(this Context context, [DrawableRes] int id)
|
||||
=> ContextCompat.GetDrawable(context, id);
|
||||
|
||||
/// <inheritdoc cref="Android.Content.Res.Resources.GetDimensionPixelSize(int)"/>
|
||||
public static int GetDimensionPixelSize(this Context context, [DimenRes] int resId)
|
||||
{
|
||||
var value = context.Resources!.GetDimensionPixelSize(resId);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Android.Runtime;
|
||||
using Android.Runtime;
|
||||
using Java.Security.Cert;
|
||||
using System.IO;
|
||||
using Xamarin.Android.Net;
|
||||
@@ -14,7 +14,7 @@ namespace System
|
||||
/// <param name="handler"></param>
|
||||
/// <param name="certificateFileData">证书文件数据(文件流,内嵌资源流)</param>
|
||||
/// <param name="certificateFileType">证书文件类型,默认使用X.509</param>
|
||||
public static void AddTrustedCerts(
|
||||
public static void AddTrustedCert(
|
||||
this AndroidClientHandler handler,
|
||||
Stream certificateFileData,
|
||||
string certificateFileType = "X.509")
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace System.Application
|
||||
{
|
||||
if (format == ResValueFormat.String)
|
||||
{
|
||||
using var reader = new StreamReader(stream, Encoding.UTF8);
|
||||
using var reader = new StreamReader(stream, EncodingCache.UTF8NoBOM);
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
if (format == ResValueFormat.StringGuidD || format == ResValueFormat.StringGuidN)
|
||||
|
||||
@@ -30,13 +30,15 @@ namespace System.Application
|
||||
Unknown = -2,
|
||||
}
|
||||
|
||||
const string TAG = nameof(Browser2);
|
||||
|
||||
static void HandlerException(Exception e)
|
||||
{
|
||||
if (OnError == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Toast.Show(e, nameof(Browser2));
|
||||
e.LogAndShowT(TAG);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -69,18 +71,8 @@ namespace System.Application
|
||||
|
||||
static OpenResultCode OpenCoreByProcess(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
Process2.Start(url, useShellExecute: true);
|
||||
return OpenResultCode.StartedByProcess2;
|
||||
}
|
||||
catch (Win32Exception e)
|
||||
{
|
||||
// [Win32Exception: 找不到应用程序] 39次报告
|
||||
// 疑似缺失没有默认浏览器设置会导致此异常,可能与杀毒软件有关
|
||||
HandlerException(e);
|
||||
return OpenResultCode.Exception;
|
||||
}
|
||||
var r = Process2.OpenCoreByProcess(url, s => Toast.Show(s));
|
||||
return r ? OpenResultCode.StartedByProcess2 : OpenResultCode.Exception;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -135,65 +135,73 @@ namespace System.Application
|
||||
}
|
||||
}
|
||||
|
||||
public static string? OSName
|
||||
public static string OSName => OSNameValue.ToDisplayName();
|
||||
|
||||
static readonly Lazy<OSNames.Value> _OSNameValue = new(() =>
|
||||
{
|
||||
get
|
||||
if (OperatingSystem2.IsWindows)
|
||||
{
|
||||
if (OperatingSystem2.IsWindows)
|
||||
if (DeviceInfo.Platform == DevicePlatform.UWP)
|
||||
{
|
||||
if (DeviceInfo.Platform == DevicePlatform.UWP)
|
||||
{
|
||||
return "UWP";
|
||||
}
|
||||
else if (DesktopBridge.IsRunningAsUwp)
|
||||
{
|
||||
return "Windows Desktop Bridge";
|
||||
}
|
||||
//else if (OperatingSystem2.IsRunningAsUwp)
|
||||
//{
|
||||
// return "Windows Sparse Package";
|
||||
//}
|
||||
else
|
||||
{
|
||||
return "Windows";
|
||||
}
|
||||
return OSNames.Value.UWP;
|
||||
}
|
||||
else if (OperatingSystem2.IsAndroid)
|
||||
else if (DesktopBridge.IsRunningAsUwp)
|
||||
{
|
||||
if (OperatingSystem2.IsRunningOnWSA)
|
||||
{
|
||||
return "WSA";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Android";
|
||||
}
|
||||
return OSNames.Value.WindowsDesktopBridge;
|
||||
}
|
||||
else if (OperatingSystem2.IsIOS)
|
||||
else
|
||||
{
|
||||
if (DeviceInfo.Idiom == XEDeviceIdiom.Tablet)
|
||||
return "iPadOS";
|
||||
return "iOS";
|
||||
return OSNames.Value.Windows;
|
||||
}
|
||||
else if (OperatingSystem2.IsMacOS)
|
||||
{
|
||||
return "macOS";
|
||||
}
|
||||
else if (OperatingSystem2.IsTvOS)
|
||||
{
|
||||
return "tvOS";
|
||||
}
|
||||
else if (OperatingSystem2.IsWatchOS)
|
||||
{
|
||||
return "watchOS";
|
||||
}
|
||||
else if (OperatingSystem2.IsLinux)
|
||||
{
|
||||
return "Linux";
|
||||
}
|
||||
return default;
|
||||
}
|
||||
}
|
||||
else if (OperatingSystem2.IsAndroid)
|
||||
{
|
||||
if (OperatingSystem2.IsRunningOnWSA)
|
||||
{
|
||||
return OSNames.Value.WSA;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DeviceType == DeviceType.Virtual)
|
||||
{
|
||||
return OSNames.Value.AndroidVirtual;
|
||||
}
|
||||
return DeviceInfo.Idiom.Convert() switch
|
||||
{
|
||||
DeviceIdiom.Phone => OSNames.Value.AndroidPhone,
|
||||
DeviceIdiom.Tablet => OSNames.Value.AndroidTablet,
|
||||
DeviceIdiom.Desktop => OSNames.Value.AndroidDesktop,
|
||||
DeviceIdiom.TV => OSNames.Value.AndroidTV,
|
||||
DeviceIdiom.Watch => OSNames.Value.AndroidWatch,
|
||||
_ => OSNames.Value.AndroidUnknown,
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (OperatingSystem2.IsIOS)
|
||||
{
|
||||
if (DeviceInfo.Idiom == XEDeviceIdiom.Tablet)
|
||||
return OSNames.Value.iPadOS;
|
||||
return OSNames.Value.iOS;
|
||||
}
|
||||
else if (OperatingSystem2.IsMacOS)
|
||||
{
|
||||
return OSNames.Value.macOS;
|
||||
}
|
||||
else if (OperatingSystem2.IsTvOS)
|
||||
{
|
||||
return OSNames.Value.tvOS;
|
||||
}
|
||||
else if (OperatingSystem2.IsWatchOS)
|
||||
{
|
||||
return OSNames.Value.watchOS;
|
||||
}
|
||||
else if (OperatingSystem2.IsLinux)
|
||||
{
|
||||
return OSNames.Value.Linux;
|
||||
}
|
||||
return default;
|
||||
});
|
||||
public static OSNames.Value OSNameValue => _OSNameValue.Value;
|
||||
|
||||
/// <inheritdoc cref="DeviceInfo.DeviceType"/>
|
||||
public static DeviceType DeviceType
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Xamarin.Essentials;
|
||||
using BaseFilePickerFileType = Xamarin.Essentials.FilePickerFileType;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System.Application
|
||||
{
|
||||
partial class FilePicker2
|
||||
{
|
||||
public class SaveOptions : PickOptions
|
||||
{
|
||||
public string? InitialFileName { get; set; }
|
||||
}
|
||||
|
||||
public class FilePickerFileType : BaseFilePickerFileType
|
||||
{
|
||||
sealed class FixedFilePickerFileType : FilePickerFileType
|
||||
{
|
||||
readonly IEnumerable<string> fileTypes;
|
||||
|
||||
public FixedFilePickerFileType(IEnumerable<string> fileTypes)
|
||||
{
|
||||
this.fileTypes = fileTypes;
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> GetPlatformFileType(DevicePlatform _) => fileTypes;
|
||||
}
|
||||
|
||||
[return: NotNullIfNotNull("values")]
|
||||
public static FilePickerFileType? Parse(IEnumerable<string>? values) => values == null ? null : new FixedFilePickerFileType(values);
|
||||
|
||||
[return: NotNullIfNotNull("values")]
|
||||
public static implicit operator FilePickerFileType?(HashSet<string>? values) => Parse(values);
|
||||
|
||||
[return: NotNullIfNotNull("values")]
|
||||
public static implicit operator FilePickerFileType?(string[]? values) => Parse(values);
|
||||
|
||||
[return: NotNullIfNotNull("values")]
|
||||
public static implicit operator FilePickerFileType?(Collection<string>? values) => Parse(values);
|
||||
|
||||
[return: NotNullIfNotNull("values")]
|
||||
public static implicit operator FilePickerFileType?(List<string>? values) => Parse(values);
|
||||
|
||||
sealed class PlatformFilePickerFileType<TValue> : FilePickerFileType where TValue : IEnumerable<string>
|
||||
{
|
||||
readonly IEnumerable<KeyValuePair<Platform, TValue?>> fileTypes;
|
||||
|
||||
public PlatformFilePickerFileType(IEnumerable<KeyValuePair<Platform, TValue?>> fileTypes)
|
||||
{
|
||||
this.fileTypes = fileTypes;
|
||||
}
|
||||
|
||||
protected override IEnumerable<string>? GetPlatformFileType(DevicePlatform _)
|
||||
{
|
||||
var platform = DeviceInfo2.Platform;
|
||||
foreach (var fileType in fileTypes)
|
||||
{
|
||||
if (fileType.Key.HasFlag(platform))
|
||||
return fileType.Value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[return: NotNullIfNotNull("values")]
|
||||
public static FilePickerFileType? Parse<TValue>(IEnumerable<KeyValuePair<Platform, TValue?>> values) where TValue : IEnumerable<string> => values == null ? null : new PlatformFilePickerFileType<TValue>(values);
|
||||
|
||||
[return: NotNullIfNotNull("values")]
|
||||
public static implicit operator FilePickerFileType?(Dictionary<Platform, IEnumerable<string>?> values) => Parse(values);
|
||||
|
||||
[return: NotNullIfNotNull("values")]
|
||||
public static implicit operator FilePickerFileType?(Dictionary<Platform, string[]?> values) => Parse(values);
|
||||
|
||||
[return: NotNullIfNotNull("values")]
|
||||
public static implicit operator FilePickerFileType?(Dictionary<Platform, List<string>?> values) => Parse(values);
|
||||
|
||||
[return: NotNullIfNotNull("values")]
|
||||
public static implicit operator FilePickerFileType?(Dictionary<Platform, HashSet<string>?> values) => Parse(values);
|
||||
|
||||
[return: NotNullIfNotNull("values")]
|
||||
public static implicit operator FilePickerFileType?(Dictionary<Platform, Collection<string>?> values) => Parse(values);
|
||||
|
||||
public interface IFilePickerFileTypeWithName
|
||||
{
|
||||
IEnumerable<(string, IEnumerable<string>)> GetFileTypes();
|
||||
}
|
||||
|
||||
sealed class FixedFilePickerFileType<TValue> : FilePickerFileType, IFilePickerFileTypeWithName where TValue : IEnumerable<string>
|
||||
{
|
||||
readonly IEnumerable<(string name, TValue extensions)> fileTypes;
|
||||
|
||||
public FixedFilePickerFileType(IEnumerable<(string name, TValue extensions)> fileTypes)
|
||||
{
|
||||
this.fileTypes = fileTypes;
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> GetPlatformFileType(DevicePlatform _) => fileTypes.SelectMany(x => x.extensions);
|
||||
|
||||
IEnumerable<(string, IEnumerable<string>)> IFilePickerFileTypeWithName.GetFileTypes()
|
||||
{
|
||||
foreach (var item in fileTypes)
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[return: NotNullIfNotNull("values")]
|
||||
public static FilePickerFileType? Parse<TValue>(IEnumerable<(string name, TValue extensions)> values) where TValue : IEnumerable<string> => values == null ? null : new FixedFilePickerFileType<TValue>(values);
|
||||
|
||||
[return: NotNullIfNotNull("values")]
|
||||
public static implicit operator FilePickerFileType?((string name, IEnumerable<string> extensions)[] values) => Parse(values);
|
||||
|
||||
[return: NotNullIfNotNull("values")]
|
||||
public static implicit operator FilePickerFileType?((string name, List<string> extensions)[] values) => Parse(values);
|
||||
|
||||
[return: NotNullIfNotNull("values")]
|
||||
public static implicit operator FilePickerFileType?((string name, string[] extensions)[] values) => Parse(values);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Essentials;
|
||||
@@ -13,29 +14,8 @@ namespace System.Application
|
||||
/// <para><see cref="https://docs.microsoft.com/zh-cn/xamarin/essentials/file-picker"/></para>
|
||||
/// <para><see cref="https://github.com/xamarin/Essentials/blob/main/Xamarin.Essentials/FilePicker/FilePicker.shared.cs"/></para>
|
||||
/// </summary>
|
||||
public static class FilePicker2
|
||||
public static partial class FilePicker2
|
||||
{
|
||||
static readonly Lazy<bool> mIsSupportedFileExtensionFilter = new(() =>
|
||||
{
|
||||
if (Essentials.IsSupported)
|
||||
{
|
||||
return OperatingSystem2.IsWindows;
|
||||
}
|
||||
else
|
||||
{
|
||||
var s = IOpenFileDialogService.Instance;
|
||||
if (s != null)
|
||||
{
|
||||
return s.IsSupportedFileExtensionFilter;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
/// <summary>
|
||||
/// 是否支持文件扩展名过滤。
|
||||
/// </summary>
|
||||
public static bool IsSupportedFileExtensionFilter => mIsSupportedFileExtensionFilter.Value;
|
||||
|
||||
static readonly Lazy<bool> mIsSupportedSaveFileDialog = new(() =>
|
||||
{
|
||||
var s = ISaveFileDialogService.Instance;
|
||||
@@ -77,6 +57,8 @@ namespace System.Application
|
||||
}
|
||||
}
|
||||
|
||||
const string TAG = nameof(FilePicker2);
|
||||
|
||||
/// <summary>
|
||||
/// 启动文件选择器以选择单个文件。
|
||||
/// </summary>
|
||||
@@ -106,7 +88,7 @@ namespace System.Application
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Toast.Show(e, nameof(FilePicker2));
|
||||
e.LogAndShowT(TAG);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,7 +131,7 @@ namespace System.Application
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Toast.Show(e, nameof(FilePicker2));
|
||||
e.LogAndShowT(TAG);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,7 +204,7 @@ namespace System.Application
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Toast.Show(e, nameof(FilePicker2));
|
||||
e.LogAndShowT(TAG);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -265,7 +247,7 @@ namespace System.Application
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Toast.Show(e, nameof(FilePicker2));
|
||||
e.LogAndShowT(TAG);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -285,7 +267,7 @@ namespace System.Application
|
||||
/// <param name="options">要使用的文件保存弹窗选项,可能为空。</param>
|
||||
/// <returns>文件拾取结果对象,或当用户取消拾取时为空。</returns>
|
||||
/// <exception cref="PlatformNotSupportedException"></exception>
|
||||
public static async Task<FileResult?> SaveAsync(SaveOptions? options = null)
|
||||
public static async Task<SaveFileResult?> SaveAsync(SaveOptions? options = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -302,82 +284,69 @@ namespace System.Application
|
||||
}
|
||||
}
|
||||
|
||||
public class SaveOptions : PickOptions
|
||||
public sealed class SaveFileResult : IDisposable
|
||||
{
|
||||
public string? InitialFileName { get; set; }
|
||||
}
|
||||
readonly string? fullPath;
|
||||
readonly Stream? stream;
|
||||
readonly string? @string;
|
||||
bool disposedValue;
|
||||
|
||||
public class GeneralFilePickerFileType : FilePickerFileType
|
||||
{
|
||||
readonly IEnumerable<string> values;
|
||||
|
||||
public GeneralFilePickerFileType(IEnumerable<string> values) : base()
|
||||
public SaveFileResult(string fullPath)
|
||||
{
|
||||
this.values = values;
|
||||
this.fullPath = fullPath;
|
||||
}
|
||||
|
||||
public static implicit operator GeneralFilePickerFileType(string[] values) => new(values);
|
||||
|
||||
public static implicit operator GeneralFilePickerFileType(List<string> values) => new(values);
|
||||
|
||||
protected override IEnumerable<string> GetPlatformFileType(DevicePlatform _) => values;
|
||||
}
|
||||
|
||||
public class FilePickerFileType2 : FilePickerFileType
|
||||
{
|
||||
readonly IReadOnlyDictionary<Platform, IEnumerable<string>> fileTypes;
|
||||
|
||||
public FilePickerFileType2(IReadOnlyDictionary<Platform, IEnumerable<string>> fileTypes) : base() => this.fileTypes = fileTypes;
|
||||
|
||||
public static implicit operator FilePickerFileType2(Dictionary<Platform, IEnumerable<string>> fileTypes) => new(fileTypes);
|
||||
|
||||
protected override IEnumerable<string> GetPlatformFileType(DevicePlatform _)
|
||||
public SaveFileResult(Stream stream, string? @string = null)
|
||||
{
|
||||
if (fileTypes.TryGetValue(DeviceInfo2.Platform, out var type))
|
||||
return type;
|
||||
|
||||
throw new PlatformNotSupportedException("This platform does not support this file type.");
|
||||
this.stream = stream;
|
||||
this.@string = @string;
|
||||
}
|
||||
}
|
||||
|
||||
public class FilePickerFilter : GeneralFilePickerFileType
|
||||
{
|
||||
public IEnumerable<(string name, IEnumerable<string> extensions)> Filters { get; }
|
||||
|
||||
public FilePickerFilter(IEnumerable<(string name, IEnumerable<string> extensions)> filters) : base(filters.SelectMany(x => x.extensions))
|
||||
/// <summary>
|
||||
/// 打开写入流,在桌面平台上为 <see cref="FileStream"/>,在 Android 上为 Java.IO.OutputStream
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotSupportedException"></exception>
|
||||
public Stream OpenWrite()
|
||||
{
|
||||
Filters = filters;
|
||||
if (fullPath != null)
|
||||
return new FileStream(fullPath, FileMode.OpenOrCreate, FileAccess.Write);
|
||||
if (stream != null)
|
||||
return stream;
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Images
|
||||
{
|
||||
static readonly Lazy<FilePickerFileType> _FileTypes = new(() =>
|
||||
public override string ToString()
|
||||
{
|
||||
if (Essentials.IsSupported)
|
||||
{
|
||||
return FilePickerFileType.Images;
|
||||
}
|
||||
else if (OperatingSystem2.IsWindows)
|
||||
{
|
||||
GeneralFilePickerFileType fileTypes = new[] { FileEx.PNG, FileEx.JPG, FileEx.JPEG, FileEx.GIF, FileEx.BMP };
|
||||
return fileTypes;
|
||||
}
|
||||
else
|
||||
{
|
||||
GeneralFilePickerFileType fileTypes = new[] { FileEx.PNG, FileEx.JPG, FileEx.JPEG, FileEx.GIF };
|
||||
return fileTypes;
|
||||
}
|
||||
});
|
||||
if (fullPath != null)
|
||||
return fullPath;
|
||||
if (@string != null)
|
||||
return @string;
|
||||
return base.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="FilePickerFileType.Images"/>
|
||||
public static FilePickerFileType FileTypes => _FileTypes.Value;
|
||||
|
||||
/// <inheritdoc cref="PickOptions.Images"/>
|
||||
public static PickOptions PickOptions => new()
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
FileTypes = FileTypes,
|
||||
};
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// TODO: 释放托管状态(托管对象)
|
||||
stream?.Dispose();
|
||||
}
|
||||
|
||||
// TODO: 释放未托管的资源(未托管的对象)并重写终结器
|
||||
// TODO: 将大型字段设置为 null
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Xamarin.Essentials;
|
||||
|
||||
namespace System.Application.Services
|
||||
{
|
||||
public interface IDeviceInfoPlatformService
|
||||
{
|
||||
string Model => string.Empty;
|
||||
string Model { get; }
|
||||
|
||||
string Manufacturer => string.Empty;
|
||||
string Manufacturer { get; }
|
||||
|
||||
string Name => string.Empty;
|
||||
string Name { get; }
|
||||
|
||||
string VersionString => string.Empty;
|
||||
string VersionString { get; }
|
||||
|
||||
DeviceType DeviceType => DeviceType.Unknown;
|
||||
DeviceType DeviceType { get; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace System.Application.Services.Implementation
|
||||
{
|
||||
public abstract class DeviceInfoPlatformServiceImpl : IDeviceInfoPlatformService
|
||||
{
|
||||
public /*virtual*/ string Model => DeviceInfo.Model;
|
||||
|
||||
public /*virtual*/ string Manufacturer => DeviceInfo.Manufacturer;
|
||||
|
||||
public /*virtual*/ string Name => DeviceInfo.Name;
|
||||
|
||||
public /*virtual*/ string VersionString => DeviceInfo.VersionString;
|
||||
|
||||
public virtual DeviceType DeviceType => DeviceInfo.DeviceType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Essentials;
|
||||
using static System.Application.FilePicker2;
|
||||
@@ -13,7 +14,7 @@ namespace System.Application.Services
|
||||
|
||||
public interface IServiceBase
|
||||
{
|
||||
protected static void FormatExtensions(IEnumerable<string>? extensions, Action<string> add, bool trimLeadingPeriod = false)
|
||||
protected static IEnumerable<string> FormatExtensions(IEnumerable<string>? extensions, bool trimLeadingPeriod = false)
|
||||
{
|
||||
var hasAtLeastOneType = false;
|
||||
|
||||
@@ -21,24 +22,33 @@ namespace System.Application.Services
|
||||
{
|
||||
foreach (var extension in extensions)
|
||||
{
|
||||
var ext = FileEx.Clean(extension, trimLeadingPeriod);
|
||||
var ext = Clean(extension, trimLeadingPeriod);
|
||||
static string Clean(string extension, bool trimLeadingPeriod = false)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(extension))
|
||||
return string.Empty;
|
||||
|
||||
if (extension.StartsWith('.'))
|
||||
{
|
||||
if (!trimLeadingPeriod) return extension;
|
||||
return extension.TrimStart('.');
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!trimLeadingPeriod) return "." + extension;
|
||||
return extension;
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(ext))
|
||||
{
|
||||
add(ext);
|
||||
yield return ext;
|
||||
hasAtLeastOneType = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasAtLeastOneType)
|
||||
add("*");
|
||||
}
|
||||
|
||||
protected static List<string> FormatExtensions(IEnumerable<string>? extensions, bool trimLeadingPeriod = false)
|
||||
{
|
||||
var list = new List<string>();
|
||||
FormatExtensions(extensions, list.Add, trimLeadingPeriod);
|
||||
return list;
|
||||
yield return "*";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +65,7 @@ namespace System.Application.Services
|
||||
{
|
||||
static ISaveFileDialogService? Instance => DI.Get_Nullable<ISaveFileDialogService>();
|
||||
|
||||
Task<FileResult?> PlatformSaveAsync(SaveOptions? options);
|
||||
Task<SaveFileResult?> PlatformSaveAsync(SaveOptions? options);
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
-82
@@ -2,9 +2,7 @@ using SQLite;
|
||||
using System.Application.Entities;
|
||||
using System.Application.Repositories;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using NotNull = System.Diagnostics.CodeAnalysis.NotNullAttribute;
|
||||
using SQLiteNotNull = SQLite.NotNullAttribute;
|
||||
using SQLiteTable = SQLite.TableAttribute;
|
||||
|
||||
@@ -26,93 +24,33 @@ namespace System.Application.Services.Implementation
|
||||
if (sharedName == null) return GetId(key);
|
||||
return $"{key}_{sharedName}_S";
|
||||
}
|
||||
|
||||
public void PlatformClear(string? sharedName)
|
||||
{
|
||||
if (sharedName == null)
|
||||
{
|
||||
conn.Execute(Sql_Delete_All);
|
||||
}
|
||||
else
|
||||
{
|
||||
conn.Execute(Sql_Delete_Where_SharedName_Equals, sharedName);
|
||||
}
|
||||
conn.Execute(Sql_Delete_Where_SharedName_Equals, sharedName ?? string.Empty);
|
||||
}
|
||||
|
||||
//public void PlatformClear(string? sharedName)
|
||||
//{
|
||||
// if (sharedName == null)
|
||||
// {
|
||||
// conn.DeleteAll<Entity>();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// var items = conn.Table<Entity>().Where(x => x.SharedName == sharedName).ToArray();
|
||||
// foreach (var item in items)
|
||||
// {
|
||||
// conn.Delete(item);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//public bool PlatformContainsKey(string key, string? sharedName)
|
||||
//{
|
||||
// int r;
|
||||
// if (sharedName == null)
|
||||
// {
|
||||
// r = conn.Execute(Sql_Select_SharedName_Where_Key_Equals_And_SharedName_IsNull, GetId(key));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// r = conn.Execute(Sql_Select_SharedName_Where_Key_Equals_And_SharedName_Equals, GetId(key, sharedName), sharedName);
|
||||
// }
|
||||
// return r > 0;
|
||||
//}
|
||||
|
||||
public bool PlatformContainsKey(string key, string? sharedName)
|
||||
{
|
||||
var id = GetId(key, sharedName);
|
||||
var item = conn.Table<Entity>().Where(x => x.Id == id && x.SharedName == sharedName).FirstOrDefault();
|
||||
sharedName ??= string.Empty;
|
||||
var item = conn.Table<Entity>()
|
||||
.Where(x => x.Id == id && x.SharedName == sharedName)
|
||||
.FirstOrDefault();
|
||||
return item != null;
|
||||
}
|
||||
|
||||
//public T? PlatformGet<T>(string key, T? defaultValue, string? sharedName) where T : notnull, IConvertible
|
||||
//{
|
||||
// string? r;
|
||||
// if (sharedName == null)
|
||||
// {
|
||||
// r = conn.ExecuteScalar<string?>(Sql_Select_Value_Where_Key_Equals_And_SharedName_IsNull, GetId(key));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// r = conn.ExecuteScalar<string?>(Sql_Select_Value_Where_Key_Equals_And_SharedName_Equals, GetId(key, sharedName), sharedName);
|
||||
// }
|
||||
// if (r == null) return defaultValue;
|
||||
// var r2 = ConvertibleHelper.Convert<T>(r);
|
||||
// return r2;
|
||||
//}
|
||||
|
||||
public T? PlatformGet<T>(string key, T? defaultValue, string? sharedName) where T : notnull, IConvertible
|
||||
{
|
||||
var id = GetId(key, sharedName);
|
||||
var item = conn.Table<Entity>().Where(x => x.Id == id && x.SharedName == sharedName).FirstOrDefault();
|
||||
sharedName ??= string.Empty;
|
||||
var item = conn.Table<Entity>()
|
||||
.Where(x => x.Id == id && x.SharedName == sharedName)
|
||||
.FirstOrDefault();
|
||||
if (item == null) return defaultValue;
|
||||
var value = ConvertibleHelper.Convert<T>(item.Value);
|
||||
return value;
|
||||
}
|
||||
|
||||
//public void PlatformRemove(string key, string? sharedName)
|
||||
//{
|
||||
// if (sharedName == null)
|
||||
// {
|
||||
// conn.Execute(Sql_Delete_Where_Id_Equals_And_SharedName_IsNull, GetId(key));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// conn.Execute(Sql_Delete_Where_Id_Equals_And_SharedName_Equals, GetId(key, sharedName), sharedName);
|
||||
// }
|
||||
//}
|
||||
|
||||
public void PlatformRemove(string key, string? sharedName)
|
||||
{
|
||||
var id = GetId(key, sharedName);
|
||||
@@ -132,19 +70,16 @@ namespace System.Application.Services.Implementation
|
||||
{
|
||||
Id = id,
|
||||
Value = value.ToString(CultureInfo.InvariantCulture),
|
||||
SharedName = sharedName,
|
||||
SharedName = sharedName ?? string.Empty,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const string TableName = "0984415E";
|
||||
const string TableName = "1984415E";
|
||||
const string ColumnName_Id = "0F5E4BAA";
|
||||
const string ColumnName_Value = "4FC331D7";
|
||||
const string ColumnName_SharedName = "F6A739AA";
|
||||
|
||||
const string Sql_Delete_All =
|
||||
$"DELETE FROM \"{TableName}\"";
|
||||
|
||||
const string Sql_Delete_Where_SharedName_Equals =
|
||||
$"DELETE FROM \"{TableName}\" WHERE \"{ColumnName_SharedName}\" = ?";
|
||||
|
||||
@@ -174,19 +109,18 @@ namespace System.Application.Services.Implementation
|
||||
[Column(ColumnName_Id)]
|
||||
[PrimaryKey]
|
||||
[SQLiteNotNull]
|
||||
[NotNull, DisallowNull] // C# 8 not null
|
||||
public string? Id { get; set; }
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
[Column(ColumnName_Value)]
|
||||
[SQLiteNotNull]
|
||||
[NotNull, DisallowNull] // C# 8 not null
|
||||
public string? Value { get; set; }
|
||||
public string Value { get; set; } = string.Empty;
|
||||
#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
|
||||
|
||||
[Column(ColumnName_SharedName)]
|
||||
public string? SharedName { get; set; }
|
||||
[SQLiteNotNull]
|
||||
public string SharedName { get; set; } = string.Empty;
|
||||
|
||||
string DebuggerDisplay() => SharedName == null ? $"{Id}, {Value}" : $"{Id}, {Value}, {SharedName}";
|
||||
string DebuggerDisplay() => SharedName == string.Empty ? $"{Id}, {Value}" : $"{Id}, {Value}, {SharedName}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace System.Application.Services
|
||||
namespace System.Application.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 通知重要性级别
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
namespace System.Application.UI.Adapters
|
||||
namespace System.Application.UI.Adapters
|
||||
{
|
||||
/// <summary>
|
||||
/// 列表项点击事件参数
|
||||
/// </summary>
|
||||
/// <typeparam name="TViewModel"></typeparam>
|
||||
#pragma warning disable IDE1006 // 命名样式
|
||||
public interface ItemClickEventArgs<TViewModel>
|
||||
#pragma warning restore IDE1006 // 命名样式
|
||||
/// <inheritdoc cref="ItemClickEventArgs{TViewModel}"/>
|
||||
public interface ItemClickEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// 位置
|
||||
@@ -16,8 +12,21 @@
|
||||
/// <summary>
|
||||
/// 当前项
|
||||
/// </summary>
|
||||
TViewModel Current { get; }
|
||||
object Current { get; }
|
||||
|
||||
bool Handled { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 列表项点击事件参数
|
||||
/// </summary>
|
||||
/// <typeparam name="TViewModel"></typeparam>
|
||||
public interface ItemClickEventArgs<TViewModel> : ItemClickEventArgs
|
||||
{
|
||||
/// <inheritdoc cref="ItemClickEventArgs.Current"/>
|
||||
new TViewModel Current { get; }
|
||||
|
||||
object ItemClickEventArgs.Current => Current;
|
||||
}
|
||||
#pragma warning restore IDE1006 // 命名样式
|
||||
}
|
||||
@@ -20,6 +20,6 @@ namespace System.Net.Http
|
||||
}
|
||||
}
|
||||
|
||||
public override Task<bool> IsConnectedAsync() => MainThread2.InvokeOnMainThreadAsync(() => IsConnected);
|
||||
//public override Task<bool> IsConnectedAsync() => MainThread2.InvokeOnMainThreadAsync(() => IsConnected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Application.Services;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace System
|
||||
@@ -21,41 +22,42 @@ namespace System
|
||||
toast.Show(text, duration);
|
||||
}
|
||||
|
||||
static void ShowLong(string text) => Show(text, ToastLength.Long);
|
||||
|
||||
#if DEBUG
|
||||
[Obsolete("use e.LogAndShowT(..", true)]
|
||||
public static void Show(Exception e,
|
||||
string? tag = null, LogLevel level = LogLevel.Error,
|
||||
string? msg = null, params object?[] args) => e.LogAndShowT(tag, level, "", msg, args);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 通过 <see cref="Exception"/> 显示 Toast 并纪录日志,传入 <see cref="LogLevel.None"/> 可不写日志
|
||||
/// 通过 <see cref="Exception"/> 纪录日志并在 Toast 上显示,传入 <see cref="LogLevel.None"/> 可不写日志
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="tag"></param>
|
||||
/// <param name="level"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void Show(Exception e, string? tag = null, LogLevel level = LogLevel.Error, string? msg = null, params string[] args)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tag)) tag = nameof(Toast);
|
||||
var has_msg = !string.IsNullOrWhiteSpace(msg);
|
||||
var has_args = args.Any_Nullable();
|
||||
if (level < LogLevel.None)
|
||||
{
|
||||
var logger = Log.CreateLogger(tag);
|
||||
if (has_msg)
|
||||
{
|
||||
if (has_args)
|
||||
{
|
||||
logger.Log(level, e, msg!, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Log(level, e, msg!);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Log(level, e, null);
|
||||
}
|
||||
}
|
||||
/// <param name="memberName"></param>
|
||||
public static void LogAndShowT(this Exception? e,
|
||||
string? tag = null, LogLevel level = LogLevel.Error,
|
||||
[CallerMemberName] string memberName = "",
|
||||
string? msg = null, params object?[] args)
|
||||
=> ExceptionExtensions.LogAndShow(e,
|
||||
ShowLong,
|
||||
string.IsNullOrWhiteSpace(tag) ? nameof(Toast) : tag, level,
|
||||
memberName,
|
||||
msg, args);
|
||||
|
||||
var text = ExceptionExtensions.GetAllMessageCore(e, has_msg, has_args, msg, args);
|
||||
Show(text, ToastLength.Long);
|
||||
}
|
||||
/// <inheritdoc cref="LogAndShowT(Exception, string?, LogLevel, string, string?, string[])"/>
|
||||
public static void LogAndShowT(this Exception? e,
|
||||
ILogger logger, LogLevel level = LogLevel.Error,
|
||||
[CallerMemberName] string memberName = "",
|
||||
string? msg = null, params object?[] args) => ExceptionExtensions.LogAndShow(e,
|
||||
ShowLong,
|
||||
logger, level,
|
||||
memberName, msg, args);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Application.Models;
|
||||
using System.Application.Models;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace System.Application.Models
|
||||
{
|
||||
@@ -15,7 +16,7 @@ namespace System
|
||||
{
|
||||
public static class ExplicitHasValueExtensions
|
||||
{
|
||||
public static bool HasValue(this IExplicitHasValue? obj)
|
||||
public static bool HasValue([NotNullWhen(true)] this IExplicitHasValue? obj)
|
||||
{
|
||||
return obj != null && obj.ExplicitHasValue();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
namespace System.Application
|
||||
{
|
||||
public static class OSNames
|
||||
{
|
||||
const string WindowsDesktopBridge = "Windows Desktop Bridge";
|
||||
const string Android = "Android";
|
||||
|
||||
public enum Value : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Universal Windows Platform / 通用 Windows 平台
|
||||
/// </summary>
|
||||
UWP = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Windows 桌面桥应用
|
||||
/// <para>桌面桥应用是使用桌面桥转换为通用 Windows 平台 (UWP) 应用的 Windows 桌面应用程序。 转换后,将以面向 Windows 10 桌面版的 UWP 应用包(.appx 或 .appxbundle)的形式打包、维护和部署 Windows 桌面应用程序。</para>
|
||||
/// </summary>
|
||||
WindowsDesktopBridge,
|
||||
|
||||
/// <summary>
|
||||
/// 桌面 Win32 应用
|
||||
/// </summary>
|
||||
Windows,
|
||||
|
||||
/// <summary>
|
||||
/// 适用于 Android™ 的 Windows 子系统
|
||||
/// </summary>
|
||||
WSA,
|
||||
|
||||
/// <summary>
|
||||
/// 未知设备类型的 Android
|
||||
/// </summary>
|
||||
AndroidUnknown,
|
||||
|
||||
/// <summary>
|
||||
/// https://www.apple.com.cn/ipados
|
||||
/// </summary>
|
||||
iPadOS,
|
||||
|
||||
/// <summary>
|
||||
/// https://developer.apple.com/cn/ios
|
||||
/// </summary>
|
||||
iOS,
|
||||
|
||||
/// <summary>
|
||||
/// https://www.apple.com.cn/macos
|
||||
/// </summary>
|
||||
macOS,
|
||||
|
||||
/// <summary>
|
||||
/// https://developer.apple.com/cn/tvos
|
||||
/// </summary>
|
||||
tvOS,
|
||||
|
||||
/// <summary>
|
||||
/// https://www.apple.com.cn/watchos
|
||||
/// </summary>
|
||||
watchOS,
|
||||
|
||||
/// <summary>
|
||||
/// GNU/Linux
|
||||
/// </summary>
|
||||
Linux,
|
||||
|
||||
/// <summary>
|
||||
/// Android 手机
|
||||
/// </summary>
|
||||
AndroidPhone,
|
||||
|
||||
/// <summary>
|
||||
/// Android 平板
|
||||
/// </summary>
|
||||
AndroidTablet,
|
||||
|
||||
/// <summary>
|
||||
/// Android 桌面端
|
||||
/// </summary>
|
||||
AndroidDesktop,
|
||||
|
||||
/// <summary>
|
||||
/// https://developer.android.google.cn/training/tv
|
||||
/// </summary>
|
||||
AndroidTV,
|
||||
|
||||
/// <summary>
|
||||
/// https://developer.android.google.cn/training/wearables
|
||||
/// </summary>
|
||||
AndroidWatch,
|
||||
|
||||
/// <summary>
|
||||
/// Android 模拟器
|
||||
/// </summary>
|
||||
AndroidVirtual,
|
||||
}
|
||||
|
||||
public static string ToDisplayName(this Value value) => value == default ? string.Empty :
|
||||
(value.IsAndroid() ? Android :
|
||||
(value switch
|
||||
{
|
||||
Value.WindowsDesktopBridge => WindowsDesktopBridge,
|
||||
_ => value.ToString(),
|
||||
}));
|
||||
|
||||
public static Value Parse(string value)
|
||||
{
|
||||
if (Enum.TryParse<Value>(value, true, out var valueEnum)) return valueEnum;
|
||||
if (string.Equals(value, WindowsDesktopBridge, StringComparison.OrdinalIgnoreCase))
|
||||
return Value.WindowsDesktopBridge;
|
||||
return default;
|
||||
}
|
||||
|
||||
public static bool IsAndroid(this Value value) => value switch
|
||||
{
|
||||
Value.WSA or
|
||||
Value.AndroidUnknown or
|
||||
Value.AndroidPhone or
|
||||
Value.AndroidTablet or
|
||||
Value.AndroidDesktop or
|
||||
Value.AndroidTV or
|
||||
Value.AndroidWatch or
|
||||
Value.AndroidVirtual => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Application.Entities;
|
||||
using System.Application.Entities;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
@@ -119,7 +119,7 @@ namespace System.Application.Repositories
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// 集合 或 <paramref name="predicate" /> 为 <see langword="null" />
|
||||
/// </exception>
|
||||
Task<TEntity?> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate);
|
||||
Task<TEntity?> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellation = default);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace System.Application.Services
|
||||
string? requestUri,
|
||||
Func<HttpRequestMessage> requestFactory,
|
||||
string? accept,
|
||||
bool enableForward,
|
||||
//bool enableForward,
|
||||
CancellationToken cancellationToken,
|
||||
Action<HttpResponseMessage>? handlerResponse = null,
|
||||
Action<HttpResponseMessage>? handlerResponseByIsNotSuccessStatusCode = null,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
@@ -9,7 +9,7 @@ namespace System.Application.Services.Implementation
|
||||
/// <inheritdoc cref="IModelValidator"/>
|
||||
public class ModelValidator : IModelValidator
|
||||
{
|
||||
static readonly MethodInfo matchValidateMethod = typeof(ModelValidator).GetMethod(nameof(MatchValidate), BindingFlags.NonPublic | BindingFlags.Static);
|
||||
static readonly MethodInfo matchValidateMethod = typeof(ModelValidator).GetMethod(nameof(MatchValidate), BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
static readonly IDictionary<Type, object> validators = new Dictionary<Type, object>();
|
||||
|
||||
public static void AddColumnValidate<T>(Func<T, string?> validate)
|
||||
|
||||
@@ -29,5 +29,7 @@ namespace System
|
||||
public const string DateCN2 = "yyyy年M月d日";
|
||||
|
||||
public const string RFC1123 = "r";
|
||||
|
||||
public const string File = "yyyy-MM-dd HHmmssfffffff";
|
||||
}
|
||||
}
|
||||
@@ -2,56 +2,195 @@ using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System
|
||||
{
|
||||
public static class ExceptionExtensions
|
||||
{
|
||||
static bool JavaInterop => OperatingSystem2.IsAndroid;
|
||||
|
||||
/// <summary>
|
||||
/// 获取异常是否为已知类型,通常不为 <see cref="ExceptionKnownType.Unknown"/> 的异常不需要纪录日志
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
/// <returns></returns>
|
||||
public static ExceptionKnownType GetKnownType(this Exception e)
|
||||
{
|
||||
if (e is TaskCanceledException)
|
||||
{
|
||||
return ExceptionKnownType.TaskCanceled;
|
||||
}
|
||||
else if (e is OperationCanceledException)
|
||||
{
|
||||
return ExceptionKnownType.OperationCanceled;
|
||||
}
|
||||
if (JavaInterop)
|
||||
{
|
||||
var typeName = e.GetType().FullName;
|
||||
switch (typeName)
|
||||
{
|
||||
case "Java.Security.Cert.CertificateNotYetValidException":
|
||||
return ExceptionKnownType.CertificateNotYetValid;
|
||||
case "Java.IO.IOException":
|
||||
if (e.Message == "Canceled")
|
||||
{
|
||||
return ExceptionKnownType.Canceled;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
e = e.InnerException;
|
||||
if (e != null)
|
||||
{
|
||||
return GetKnownType(e);
|
||||
}
|
||||
return ExceptionKnownType.Unknown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否为取消操作异常
|
||||
/// </summary>
|
||||
/// <param name="exception"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsCanceledException(this Exception e)
|
||||
public static bool IsCanceledException(this ExceptionKnownType value)
|
||||
=> value == ExceptionKnownType.Canceled ||
|
||||
value == ExceptionKnownType.OperationCanceled ||
|
||||
value == ExceptionKnownType.TaskCanceled;
|
||||
|
||||
///// <summary>
|
||||
///// 是否为取消操作异常
|
||||
///// </summary>
|
||||
///// <param name="exception"></param>
|
||||
///// <returns></returns>
|
||||
//[Obsolete("use GetKnownType", true)]
|
||||
//public static bool IsCanceledException(this Exception e) => GetKnownType(e).IsCanceledException();
|
||||
|
||||
/// <summary>
|
||||
/// 通过 <see cref="Exception"/> 纪录日志并在 UI 上显示,传入 <see cref="LogLevel.None"/> 可不写日志
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
/// <param name="show"></param>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="tag"></param>
|
||||
/// <param name="level"></param>
|
||||
/// <param name="memberName"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void LogAndShow(Exception? e,
|
||||
Action<string>? show,
|
||||
string tag, LogLevel level = LogLevel.Error,
|
||||
string memberName = "",
|
||||
string? msg = null, params object?[] args) => LogAndShowCore(e, show, tag, logger: null, level, memberName, msg, args);
|
||||
|
||||
/// <inheritdoc cref="LogAndShow(Exception, Action{string}?, string, LogLevel, string?,string, string[])"/>
|
||||
public static void LogAndShow(Exception? e,
|
||||
Action<string>? show,
|
||||
ILogger? logger, LogLevel level = LogLevel.Error,
|
||||
string memberName = "",
|
||||
string? msg = null, params object?[] args) => LogAndShowCore(e, show, tag: null, logger, level, memberName, msg, args);
|
||||
|
||||
static void LogAndShowCore(Exception? e,
|
||||
Action<string>? show,
|
||||
string? tag = null, ILogger? logger = null, LogLevel level = LogLevel.Error,
|
||||
string memberName = "",
|
||||
string? msg = null, params object?[] args)
|
||||
{
|
||||
if (e is TaskCanceledException || e is OperationCanceledException)
|
||||
bool has_msg = !string.IsNullOrWhiteSpace(msg);
|
||||
if (!has_msg)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (OperatingSystem2.IsAndroid)
|
||||
{
|
||||
if (e.Message == "Canceled" && e.GetType().FullName == "Java.IO.IOException")
|
||||
if (!string.IsNullOrWhiteSpace(memberName))
|
||||
{
|
||||
return true;
|
||||
msg = $"{memberName} Error";
|
||||
has_msg = true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
var has_args = args.Any_Nullable();
|
||||
var has_e = e != null;
|
||||
if (has_e)
|
||||
{
|
||||
var knownType = e!.GetKnownType();
|
||||
if (knownType != ExceptionKnownType.Unknown) level = LogLevel.None;
|
||||
}
|
||||
var has_log_level = level < LogLevel.None;
|
||||
if (has_log_level)
|
||||
{
|
||||
if (logger == null && tag != null)
|
||||
{
|
||||
logger = Log.CreateLogger(tag);
|
||||
}
|
||||
if (logger != null)
|
||||
{
|
||||
if (has_args)
|
||||
{
|
||||
logger.Log(level, e, msg!, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Log(level, e, msg!);
|
||||
}
|
||||
}
|
||||
}
|
||||
show?.Invoke(GetShowMsg());
|
||||
string GetShowMsg()
|
||||
{
|
||||
if (has_e) return GetAllMessageCore(e!, has_msg, has_args, msg, args);
|
||||
if (has_msg)
|
||||
{
|
||||
if (has_args)
|
||||
{
|
||||
return msg!.Format(args);
|
||||
}
|
||||
else
|
||||
{
|
||||
return msg!;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取异常中所有错误信息
|
||||
/// </summary>
|
||||
/// <param name="e">当前捕获的异常</param>
|
||||
/// <param name="has_msg"></param>
|
||||
/// <param name="has_args"></param>
|
||||
/// <param name="msg">可选的消息,将写在第一行</param>
|
||||
/// <param name="args">可选的消息参数</param>
|
||||
/// <returns></returns>
|
||||
public static string GetAllMessage(this Exception e, string? msg = null, params string[] args)
|
||||
public static string GetAllMessage(this Exception e, string? msg = null, params object?[] args)
|
||||
{
|
||||
var has_msg = !string.IsNullOrWhiteSpace(msg);
|
||||
var has_args = args.Any_Nullable();
|
||||
return GetAllMessageCore(e, has_msg, has_args, msg, args);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="GetAllMessage(Exception, string?, string[])" />
|
||||
public static string GetAllMessageCore(Exception e, bool has_msg, bool has_args, string? msg = null, params string[] args)
|
||||
/// <inheritdoc cref="GetAllMessage(Exception, string?, string[])"/>
|
||||
static string GetAllMessageCore(Exception e,
|
||||
bool has_msg, bool has_args,
|
||||
string? msg = null, params object?[] args)
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
|
||||
if (has_msg)
|
||||
{
|
||||
if (has_args)
|
||||
{
|
||||
sb.AppendFormat(msg, args);
|
||||
try
|
||||
{
|
||||
sb.AppendFormat(msg, args);
|
||||
}
|
||||
catch
|
||||
{
|
||||
sb.Append(msg);
|
||||
foreach (var item in args)
|
||||
{
|
||||
sb.Append(' ');
|
||||
sb.Append(item);
|
||||
}
|
||||
}
|
||||
sb.AppendLine();
|
||||
}
|
||||
else
|
||||
@@ -59,31 +198,45 @@ namespace System
|
||||
sb.AppendLine(msg!);
|
||||
}
|
||||
}
|
||||
var e_str = e.ToString();
|
||||
sb.AppendLine(e_str);
|
||||
var stackTrace = e.StackTrace;
|
||||
if (!string.IsNullOrWhiteSpace(stackTrace) &&
|
||||
!e_str.Contains(stackTrace))
|
||||
{
|
||||
sb.AppendLine(stackTrace);
|
||||
}
|
||||
|
||||
var exception = e;
|
||||
ushort i = 0;
|
||||
while (exception != null && i++ < byte.MaxValue)
|
||||
{
|
||||
var exception_message = exception.Message;
|
||||
if (!string.IsNullOrWhiteSpace(exception_message) &&
|
||||
!e_str.Contains(exception_message))
|
||||
if (!string.IsNullOrWhiteSpace(exception_message))
|
||||
{
|
||||
sb.AppendLine(exception_message);
|
||||
}
|
||||
exception = exception.InnerException;
|
||||
|
||||
}
|
||||
|
||||
var text = sb.ToString().Trim();
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异常已知类型,通常不为 <see cref="Unknown"/> 的异常不需要纪录日志
|
||||
/// </summary>
|
||||
public enum ExceptionKnownType : byte
|
||||
{
|
||||
Unknown,
|
||||
|
||||
/// <summary>
|
||||
/// 取消操作异常
|
||||
/// </summary>
|
||||
Canceled,
|
||||
|
||||
/// <summary>
|
||||
/// 证书时间验证错误异常,通常为本地时间不正确导致 SSL 握手失败或服务端证书失效
|
||||
/// </summary>
|
||||
CertificateNotYetValid,
|
||||
|
||||
/// <inheritdoc cref="OperationCanceledException"/>
|
||||
OperationCanceled,
|
||||
|
||||
/// <inheritdoc cref="TaskCanceledException"/>
|
||||
TaskCanceled,
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,51 @@
|
||||
using System.Common;
|
||||
using System.Common;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System
|
||||
{
|
||||
public static class GuidExtensions
|
||||
{
|
||||
// N 32 digits:
|
||||
// 00000000000000000000000000000000
|
||||
// D 32 digits separated by hyphens:
|
||||
// 00000000-0000-0000-0000-000000000000
|
||||
// B 32 digits separated by hyphens, enclosed in braces:
|
||||
// {00000000-0000-0000-0000-000000000000}
|
||||
// P 32 digits separated by hyphens, enclosed in parentheses:
|
||||
// (00000000-0000-0000-0000-000000000000)
|
||||
// X Four hexadecimal values enclosed in braces, where the fourth value is a subset of eight hexadecimal values that is also enclosed in braces:
|
||||
// {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}
|
||||
|
||||
// S Base64Url ShortGuid
|
||||
|
||||
/// <summary>
|
||||
/// 返回 <see cref="Guid"/> 结构的此实例值的字符串表示形式(格式N)
|
||||
/// 返回 <see cref="Guid"/> 结构的此实例值的字符串表示形式,格式:N
|
||||
/// </summary>
|
||||
/// <param name="guid"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToStringN(this Guid guid) => guid.ToString(Constants.N);
|
||||
|
||||
/// <summary>
|
||||
/// 返回 <see cref="Guid"/> 结构的此实例值的字符串表示形式(格式N)
|
||||
/// 返回 <see cref="Guid"/> 结构的此实例值的字符串表示形式,格式:N
|
||||
/// </summary>
|
||||
/// <param name="guid"></param>
|
||||
/// <returns></returns>
|
||||
public static string? ToStringN(this Guid? guid) => guid.HasValue ? guid.Value.ToStringN() : null;
|
||||
|
||||
/// <summary>
|
||||
/// 返回 <see cref="Guid"/> 结构的此实例值的字符串表示形式,格式:<see cref="ShortGuid"/>
|
||||
/// </summary>
|
||||
/// <param name="guid"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToStringS(this Guid guid) => ShortGuid.Encode(guid);
|
||||
|
||||
/// <summary>
|
||||
/// 返回 <see cref="Guid"/> 结构的此实例值的字符串表示形式,格式:<see cref="ShortGuid"/>
|
||||
/// </summary>
|
||||
/// <param name="guid"></param>
|
||||
/// <returns></returns>
|
||||
public static string? ToStringS(this Guid? guid) => guid.HasValue ? guid.Value.ToStringS() : null;
|
||||
|
||||
static bool TryParseGuid_Format_D(string? input, out Guid result) => Guid.TryParse(input, out result);
|
||||
|
||||
static bool TryParseGuid_Format_N(string? input, out Guid result) => Guid.TryParseExact(input, Constants.N, out result);
|
||||
|
||||
@@ -90,10 +90,6 @@ namespace System
|
||||
return stream.ReadStringInternalDynamic(Encoding.UTF8, '\0');
|
||||
}
|
||||
|
||||
static readonly Lazy<Encoding> _UTF8NoBOM = new(() => new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true));
|
||||
|
||||
internal static Encoding UTF8NoBOM => _UTF8NoBOM.Value;
|
||||
|
||||
const int DefaultBufferSize = 1024;
|
||||
|
||||
public static StreamWriter GetWriter(this Stream stream, Encoding? encoding = null, int bufferSize = -1, bool leaveOpen = false)
|
||||
@@ -108,7 +104,7 @@ namespace System
|
||||
{
|
||||
if (encoding == null)
|
||||
{
|
||||
encoding = UTF8NoBOM;
|
||||
encoding = EncodingCache.UTF8NoBOM;
|
||||
}
|
||||
if (bufferSize == -1)
|
||||
{
|
||||
|
||||
@@ -109,19 +109,13 @@ namespace System
|
||||
|
||||
public const string XML = ".xml";
|
||||
|
||||
public static string Clean(string extension, bool trimLeadingPeriod = false)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(extension))
|
||||
return string.Empty;
|
||||
public const string PFX = ".pfx";
|
||||
|
||||
extension = extension.TrimStart('*');
|
||||
extension = extension.TrimStart('.');
|
||||
public const string CER = ".cer";
|
||||
|
||||
if (!trimLeadingPeriod)
|
||||
extension = "." + extension;
|
||||
public const string DAT = ".dat";
|
||||
|
||||
return extension;
|
||||
}
|
||||
public const string maFile = ".maFile";
|
||||
|
||||
public static bool IsSupportedTextReader(string extension) =>
|
||||
string.Equals(extension, JSON, StringComparison.OrdinalIgnoreCase) ||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
// https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/IO/EncodingCache.cs
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
public static class EncodingCache
|
||||
{
|
||||
public static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
|
||||
}
|
||||
}
|
||||
@@ -167,6 +167,41 @@ namespace System
|
||||
IOPath.getAppDataDirectory = getAppDataDirectory;
|
||||
IOPath.getCacheDirectory = getCacheDirectory;
|
||||
}
|
||||
|
||||
protected static void InitFileSystemWithMigrations(
|
||||
string destAppDataPath, string destCachePath,
|
||||
string sourceAppDataPath, string sourceCachePath)
|
||||
{
|
||||
var paths = new[] { destAppDataPath, destCachePath, };
|
||||
var dict_paths = paths.ToDictionary(x => x, Directory.Exists);
|
||||
|
||||
if (dict_paths.Values.All(x => !x))
|
||||
{
|
||||
var old_paths = new[] { sourceAppDataPath, sourceCachePath, };
|
||||
if (old_paths.All(Directory.Exists)) // 迁移之前根目录上的文件夹
|
||||
{
|
||||
for (int i = 0; i < old_paths.Length; i++)
|
||||
{
|
||||
var path = paths[i];
|
||||
var old_path = old_paths[i];
|
||||
Directory.Move(old_path, path);
|
||||
dict_paths[path] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in dict_paths)
|
||||
{
|
||||
if (!item.Value)
|
||||
{
|
||||
Directory.CreateDirectory(item.Key);
|
||||
}
|
||||
}
|
||||
|
||||
InitFileSystem(GetAppDataDirectory, GetCacheDirectory);
|
||||
string GetAppDataDirectory() => destAppDataPath;
|
||||
string GetCacheDirectory() => destCachePath;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -413,5 +448,8 @@ namespace System
|
||||
var t1 = Math.Round(t, 2);
|
||||
return Convert.ToInt32(t1 * 100);
|
||||
}
|
||||
|
||||
public const char UnixDirectorySeparatorChar = '/';
|
||||
public const char WinDirectorySeparatorChar = '\\';
|
||||
}
|
||||
}
|
||||
@@ -82,5 +82,7 @@ namespace System
|
||||
public const string All = "*/*";
|
||||
|
||||
public const string _7Z = "application/x-7z-compressed";
|
||||
|
||||
public const string CER = "application/x-x509-ca-cert";
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ namespace System.Net.Http
|
||||
{
|
||||
request.AllowAutoRedirect = true;
|
||||
request.MaximumAutomaticRedirections = 1000;
|
||||
request.Timeout = DefaultTimeoutTotalMilliseconds;
|
||||
request.Timeout = DefaultTimeoutMilliseconds;
|
||||
#if NETSTANDARD
|
||||
// .NET Core 3+ 上已由 HttpClient.DefaultProxy 生效
|
||||
// https://docs.microsoft.com/zh-cn/dotnet/api/system.net.http.httpclient.defaultproxy?view=net-6.0
|
||||
|
||||
@@ -5,11 +5,9 @@ namespace System.Net.Http
|
||||
/// <summary>
|
||||
/// 默认超时时间,45 秒
|
||||
/// </summary>
|
||||
public static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(45);
|
||||
|
||||
static readonly Lazy<int> mDefaultTimeoutTotalMilliseconds = new(() => DefaultTimeout.TotalMilliseconds.ToInt32());
|
||||
public static readonly TimeSpan DefaultTimeout = TimeSpan.FromMilliseconds(DefaultTimeoutMilliseconds);
|
||||
|
||||
/// <inheritdoc cref="DefaultTimeout"/>
|
||||
public static int DefaultTimeoutTotalMilliseconds => mDefaultTimeoutTotalMilliseconds.Value;
|
||||
public const int DefaultTimeoutMilliseconds = 45000;
|
||||
}
|
||||
}
|
||||
@@ -30,8 +30,25 @@ namespace System.Net.Sockets
|
||||
/// <summary>
|
||||
/// 检查指定的端口是否被占用
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsUsePort(IPAddress address, int port)
|
||||
{
|
||||
try
|
||||
{
|
||||
var listener = new TcpListener(address, port);
|
||||
listener.Start();
|
||||
listener.Stop();
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IsUsePort(IPAddress, int)"/>
|
||||
public static bool IsUsePort(int port)
|
||||
{
|
||||
try
|
||||
@@ -42,17 +59,7 @@ namespace System.Net.Sockets
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
var listener = new TcpListener(IPAddress.Loopback, port);
|
||||
listener.Start();
|
||||
listener.Stop();
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return IsUsePort(IPAddress.Loopback, port);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +69,7 @@ namespace System.Net.Sockets
|
||||
/// <param name="port"></param>
|
||||
/// <returns></returns>
|
||||
#if NET5_0_OR_GREATER
|
||||
[SupportedOSPlatform("Windows")]
|
||||
[SupportedOSPlatform("Windows7.0")]
|
||||
#endif
|
||||
public static Process? GetProcessByTcpPort(ushort port)
|
||||
=> GetAllTcpConnections().FirstOrDefault(x => x.LocalPort == port)?.Process;
|
||||
@@ -81,7 +88,7 @@ namespace System.Net.Sockets
|
||||
/// is insufficient memory to satisfy the request.
|
||||
/// </exception>
|
||||
#if NET5_0_OR_GREATER
|
||||
[SupportedOSPlatform("Windows")]
|
||||
[SupportedOSPlatform("Windows7.0")]
|
||||
#endif
|
||||
public static List<TcpProcessRecord> GetAllTcpConnections()
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Properties;
|
||||
|
||||
namespace System
|
||||
{
|
||||
@@ -80,5 +81,21 @@ namespace System
|
||||
p.UseShellExecute = useShellExecute;
|
||||
return Process.Start(p);
|
||||
}
|
||||
|
||||
public static bool OpenCoreByProcess(string url, Action<string>? onError = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Start(url, useShellExecute: true);
|
||||
return true;
|
||||
}
|
||||
catch (Win32Exception e)
|
||||
{
|
||||
// [Win32Exception: 找不到应用程序] 39次报告
|
||||
// 疑似缺失没有默认浏览器设置会导致此异常,可能与杀毒软件有关
|
||||
onError?.Invoke(SR.OpenCoreByProcess_Win32Exception_.Format(Convert.ToString(e.NativeErrorCode, 16)));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-1
@@ -19,7 +19,7 @@ namespace System.Properties {
|
||||
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
|
||||
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
|
||||
// (以 /str 作为命令选项),或重新生成 VS 项目。
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class SR {
|
||||
@@ -231,6 +231,15 @@ namespace System.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 打开 Web 链接失败 0x{0},请设置系统默认浏览器后再尝试 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string OpenCoreByProcess_Win32Exception_ {
|
||||
get {
|
||||
return ResourceManager.GetString("OpenCoreByProcess_Win32Exception_", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 她 的本地化字符串。
|
||||
/// </summary>
|
||||
|
||||
@@ -205,4 +205,8 @@
|
||||
<value>Unsupported mobile phone number segment ({0})</value>
|
||||
<comment>Comment=;Author=MicrosoftTranslator</comment>
|
||||
</data>
|
||||
<data name="OpenCoreByProcess_Win32Exception_" xml:space="preserve">
|
||||
<value>Open web link fails 0x{0}, please set the system default browser before trying</value>
|
||||
<comment>Comment=;Author=MicrosoftTranslator</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -205,4 +205,8 @@
|
||||
<value>Segmento de número de teléfono móvil no compatible ({0})</value>
|
||||
<comment>Comment=;Author=MicrosoftTranslator</comment>
|
||||
</data>
|
||||
<data name="OpenCoreByProcess_Win32Exception_" xml:space="preserve">
|
||||
<value>Abrir enlace web falla 0x{0}, configure el navegador predeterminado del sistema antes de intentarlo</value>
|
||||
<comment>Comment=;Author=MicrosoftTranslator</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -205,4 +205,8 @@
|
||||
<value>Segmento di numero di cellulare non supportato ({0})</value>
|
||||
<comment>Comment=;Author=MicrosoftTranslator</comment>
|
||||
</data>
|
||||
<data name="OpenCoreByProcess_Win32Exception_" xml:space="preserve">
|
||||
<value>Il collegamento Web aperto non riesce 0x{0}, impostare il browser predefinito del sistema prima di provare</value>
|
||||
<comment>Comment=;Author=MicrosoftTranslator</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -205,4 +205,8 @@
|
||||
<value>サポートされていない携帯電話番号セグメント ({0})</value>
|
||||
<comment>Comment=;Author=MicrosoftTranslator</comment>
|
||||
</data>
|
||||
<data name="OpenCoreByProcess_Win32Exception_" xml:space="preserve">
|
||||
<value>Web リンクを開くと 0x {0}に失敗しました</value>
|
||||
<comment>Comment=;Author=MicrosoftTranslator</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -205,4 +205,8 @@
|
||||
<value>지원되지 않는 휴대폰 번호 세그먼트({0})</value>
|
||||
<comment>Comment=;Author=MicrosoftTranslator</comment>
|
||||
</data>
|
||||
<data name="OpenCoreByProcess_Win32Exception_" xml:space="preserve">
|
||||
<value>웹 링크를 열지 못한 경우 0x{0} 시스템 기본 브라우저를 설정한 다음 다시 시도하십시오</value>
|
||||
<comment>Comment=;Author=MicrosoftTranslator</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -175,6 +175,9 @@
|
||||
<value>Collection was of a fixed size.</value>
|
||||
<comment>https://github.com/dotnet/runtime/blob/v5.0.0-rc.1.20451.14/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx#L2893</comment>
|
||||
</data>
|
||||
<data name="OpenCoreByProcess_Win32Exception_" xml:space="preserve">
|
||||
<value>打开 Web 链接失败 0x{0},请设置系统默认浏览器后再尝试</value>
|
||||
</data>
|
||||
<data name="She" xml:space="preserve">
|
||||
<value>她</value>
|
||||
</data>
|
||||
|
||||
@@ -205,4 +205,8 @@
|
||||
<value>Неподдерживаемый сегмент номера мобильного телефона ({0})</value>
|
||||
<comment>Comment=;Author=MicrosoftTranslator</comment>
|
||||
</data>
|
||||
<data name="OpenCoreByProcess_Win32Exception_" xml:space="preserve">
|
||||
<value>Открытие веб-ссылки завершается неудачей {0} 0x, установите системный браузер по умолчанию и повторите попытку</value>
|
||||
<comment>Comment=;Author=MicrosoftTranslator</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -205,4 +205,8 @@
|
||||
<value>不受支援的手機號碼號段({0})</value>
|
||||
<comment>Comment=;Author=MicrosoftTranslator</comment>
|
||||
</data>
|
||||
<data name="OpenCoreByProcess_Win32Exception_" xml:space="preserve">
|
||||
<value>打開 Web 連結失敗 0x{0},請設置系統預設瀏覽器後再嘗試</value>
|
||||
<comment>Comment=;Author=MicrosoftTranslator</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace System.Properties
|
||||
/// <item> macOS:\packaging\build-osx-app.sh <CFBundleVersion> <CFBundleShortVersionString> </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public const string Version = "2.6.3";
|
||||
public const string Version = "2.6.5";
|
||||
|
||||
//public const string InfoVersion = Version + "-beta";
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ using static System.Serializable;
|
||||
using SJsonSerializer = System.Text.Json.JsonSerializer;
|
||||
using SJsonSerializerOptions = System.Text.Json.JsonSerializerOptions;
|
||||
using SJsonIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System
|
||||
{
|
||||
@@ -159,6 +160,22 @@ namespace System
|
||||
public static byte[] SMP(Type type, object value, CancellationToken cancellationToken = default)
|
||||
=> MessagePackSerializer.Serialize(type, value, options: lz4Options, cancellationToken: cancellationToken);
|
||||
|
||||
/// <inheritdoc cref="SMP{T}(T, CancellationToken)"/>
|
||||
public static void SMP<T>(Stream stream, T value, CancellationToken cancellationToken = default)
|
||||
=> MessagePackSerializer.Serialize(stream, value, options: lz4Options, cancellationToken: cancellationToken);
|
||||
|
||||
/// <inheritdoc cref="SMP{T}(T, CancellationToken)"/>
|
||||
public static void SMP(Type type, Stream stream, object value, CancellationToken cancellationToken = default)
|
||||
=> MessagePackSerializer.Serialize(type, stream, value, options: lz4Options, cancellationToken: cancellationToken);
|
||||
|
||||
/// <inheritdoc cref="SMP{T}(T, CancellationToken)"/>
|
||||
public static Task SMPAsync<T>(Stream stream, T value, CancellationToken cancellationToken = default)
|
||||
=> MessagePackSerializer.SerializeAsync(stream, value, options: lz4Options, cancellationToken: cancellationToken);
|
||||
|
||||
/// <inheritdoc cref="SMP{T}(T, CancellationToken)"/>
|
||||
public static Task SMPAsync(Type type, Stream stream, object value, CancellationToken cancellationToken = default)
|
||||
=> MessagePackSerializer.SerializeAsync(type, stream, value, options: lz4Options, cancellationToken: cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// (Serialize)MessagePack 序列化 + Base64Url Encode
|
||||
/// </summary>
|
||||
@@ -240,6 +257,17 @@ namespace System
|
||||
public static object? DMP(Type type, Stream buffer, CancellationToken cancellationToken = default)
|
||||
=> MessagePackSerializer.Deserialize(type, buffer, options: lz4Options, cancellationToken: cancellationToken);
|
||||
|
||||
/// <inheritdoc cref="DMP{T}(Stream, CancellationToken)"/>
|
||||
public static async ValueTask<T?> DMPAsync<T>(Stream buffer, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var r = await MessagePackSerializer.DeserializeAsync<T>(buffer, options: lz4Options, cancellationToken: cancellationToken);
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="DMP{T}(Stream, CancellationToken)"/>
|
||||
public static ValueTask<object?> DMPAsync(Type type, Stream buffer, CancellationToken cancellationToken = default)
|
||||
=> MessagePackSerializer.DeserializeAsync(type, buffer, options: lz4Options, cancellationToken: cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// (Deserialize)MessagePack 反序列化 + Base64Url Decode
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,428 @@
|
||||
// MIT License
|
||||
// Copyright(c) 2007 Dave Transom
|
||||
// https://github.com/AigioL/CSharpVitamins.ShortGuid/blob/master/CSharpVitamins.ShortGuid/ShortGuid.cs
|
||||
// https://github.com/csharpvitamins/CSharpVitamins.ShortGuid/blob/master/CSharpVitamins.ShortGuid/ShortGuid.cs
|
||||
using System.Diagnostics;
|
||||
#if !NET40
|
||||
using gfoidl.Base64;
|
||||
#endif
|
||||
|
||||
namespace System
|
||||
{
|
||||
/// <summary>
|
||||
/// A convenience wrapper struct for dealing with URL-safe Base64 encoded globally unique identifiers (GUID),
|
||||
/// making a shorter string value (22 vs 36 characters long).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// What is URL-safe Base64? That's just a Base64 string with well known special characters replaced (/, +)
|
||||
/// or removed (==).
|
||||
///
|
||||
/// <para>NB: As of version 2.0.0, <see cref="Decode(string)"/> performs a sanity check when decoding
|
||||
/// strings to ensure they haven't been tampered with, i.e. allowing the end of a Base64 string to be tweaked
|
||||
/// where it still produces that same byte array to create the underlying Guid. Effectively there is "unused
|
||||
/// space" in the Base64 string which is ignored, but will now result in an <see cref="FormatException"/> being
|
||||
/// thrown.</para>
|
||||
///
|
||||
/// <para>ShortGuid will never produce an invalid string, however if one is supplied it, could result in an
|
||||
/// unintended collision where multiple URL-safe Base64 strings can point to the same Guid. To avoid this
|
||||
/// uncertainty, a round-trip check is performed to ensure a 1-1 match with the input string.</para>
|
||||
///
|
||||
/// <para>Stick with version 1.1.0 if you require the old behaviour with opt-in strict parsing.</para>
|
||||
/// </remarks>
|
||||
[DebuggerDisplay("{Value}")]
|
||||
public struct ShortGuid
|
||||
{
|
||||
/// <summary>
|
||||
/// A read-only instance of the ShortGuid struct whose value is guaranteed to be all zeroes i.e. equivalent
|
||||
/// to <see cref="Guid.Empty"/>.
|
||||
/// </summary>
|
||||
public static readonly ShortGuid Empty = new(Guid.Empty);
|
||||
|
||||
public const int StringLength = 22;
|
||||
|
||||
readonly Guid underlyingGuid;
|
||||
readonly string encodedString;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance with the given URL-safe Base64 encoded string.
|
||||
/// <para>See also <seealso cref="TryParse(string, out ShortGuid)"/> which will try to coerce the
|
||||
/// the value from URL-safe Base64 or normal Guid string.</para>
|
||||
/// </summary>
|
||||
/// <param name="value">A 22 character URL-safe Base64 encoded string to decode.</param>
|
||||
public ShortGuid(string value)
|
||||
{
|
||||
encodedString = value;
|
||||
underlyingGuid = Decode(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance with the given <see cref="System.Guid"/>.
|
||||
/// </summary>
|
||||
/// <param name="guid">The <see cref="System.Guid"/> to encode.</param>
|
||||
public ShortGuid(Guid guid)
|
||||
{
|
||||
encodedString = Encode(guid);
|
||||
underlyingGuid = guid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying <see cref="System.Guid"/> for the encoded ShortGuid.
|
||||
/// </summary>
|
||||
public Guid Guid => underlyingGuid;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the encoded string value of the <see cref="Guid"/> as a URL-safe Base64 string.
|
||||
/// </summary>
|
||||
public string Value => encodedString;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the encoded URL-safe Base64 string.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString() => encodedString;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this instance and a specified object represent the same type and value.
|
||||
/// <para>Compares for equality against other string, Guid and ShortGuid types.</para>
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is ShortGuid shortGuid)
|
||||
{
|
||||
return underlyingGuid.Equals(shortGuid.underlyingGuid);
|
||||
}
|
||||
|
||||
if (obj is Guid guid)
|
||||
{
|
||||
return underlyingGuid.Equals(guid);
|
||||
}
|
||||
|
||||
if (obj is string str)
|
||||
{
|
||||
// Try a ShortGuid string.
|
||||
if (TryDecode(str, out guid))
|
||||
return underlyingGuid.Equals(guid);
|
||||
|
||||
// Try a guid string.
|
||||
if (Guid.TryParse(str, out guid))
|
||||
return underlyingGuid.Equals(guid);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the hash code for the underlying <see cref="System.Guid"/>.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override int GetHashCode() => underlyingGuid.GetHashCode();
|
||||
|
||||
/// <summary>
|
||||
/// Initialises a new instance of a ShortGuid using <see cref="Guid.NewGuid()"/>.
|
||||
/// <para>Equivalent of calling: <code>`new ShortGuid(Guid.NewGuid())`</code></para>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static ShortGuid NewGuid() => new(Guid.NewGuid());
|
||||
|
||||
/// <summary>
|
||||
/// Encodes the given value as an encoded ShortGuid string. The encoding is similar to Base64, with
|
||||
/// some non-URL safe characters replaced, and padding removed, resulting in a 22 character string.
|
||||
/// </summary>
|
||||
/// <param name="value">Any valid <see cref="System.Guid"/> string.</param>
|
||||
/// <returns>A 22 character ShortGuid URL-safe Base64 string.</returns>
|
||||
public static string Encode(string value)
|
||||
{
|
||||
var guid = new Guid(value);
|
||||
return Encode(guid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes the given <see cref="System.Guid"/> as an encoded ShortGuid string. The encoding is
|
||||
/// similar to Base64, with some non-URL safe characters replaced, and padding removed, resulting
|
||||
/// in a 22 character string.
|
||||
/// </summary>
|
||||
/// <param name="guid">The <see cref="System.Guid"/> to encode.</param>
|
||||
/// <returns>A 22 character ShortGuid URL-safe Base64 string.</returns>
|
||||
public static string Encode(Guid guid)
|
||||
{
|
||||
#if NET40
|
||||
string encoded = Convert.ToBase64String(guid.ToByteArray());
|
||||
|
||||
encoded = encoded
|
||||
.Replace("/", "_")
|
||||
.Replace("+", "-");
|
||||
|
||||
return encoded.Substring(0, StringLength);
|
||||
#else
|
||||
return Base64.Url.Encode(guid.ToByteArray());
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the given value from a 22 character URL-safe Base64 string to a <see cref="System.Guid"/>.
|
||||
/// <para>Supports: ShortGuid format only.</para>
|
||||
/// <para>See also <seealso cref="TryDecode(string, out Guid)"/> or <seealso cref="TryParse(string, out Guid)"/>.</para>
|
||||
/// </summary>
|
||||
/// <param name="value">A 22 character URL-safe Base64 encoded string to decode.</param>
|
||||
/// <returns>A new <see cref="System.Guid"/> instance from the parsed string.</returns>
|
||||
/// <exception cref="FormatException">
|
||||
/// If <paramref name="value"/> is not a valid Base64 string (<seealso cref="Convert.FromBase64String(string)"/>)
|
||||
/// or if the decoded guid doesn't strictly match the input <paramref name="value"/>.
|
||||
/// </exception>
|
||||
public static Guid Decode(string value) => DecodeCore(value);
|
||||
|
||||
static Guid DecodeCore(string? value)
|
||||
{
|
||||
// avoid parsing larger strings/blobs
|
||||
if (value?.Length != StringLength)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"A ShortGuid must be exactly 22 characters long. Received a {value?.Length ?? 0} character string.",
|
||||
paramName: nameof(value)
|
||||
);
|
||||
}
|
||||
|
||||
#if NET40
|
||||
string base64 = value
|
||||
.Replace("_", "/")
|
||||
.Replace("-", "+") + "==";
|
||||
|
||||
byte[] blob = Convert.FromBase64String(base64);
|
||||
#else
|
||||
byte[] blob = Base64.Url.Decode(value.AsSpan());
|
||||
#endif
|
||||
var guid = new Guid(blob);
|
||||
|
||||
var sanityCheck = Encode(guid);
|
||||
if (sanityCheck != value)
|
||||
{
|
||||
throw new FormatException(
|
||||
$"Invalid strict ShortGuid encoded string. The string '{value}' is valid URL-safe Base64, " +
|
||||
$"but failed a round-trip test expecting '{sanityCheck}'."
|
||||
);
|
||||
}
|
||||
|
||||
return guid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Supports ShortGuid format only.</para>
|
||||
/// <para>Attempts to decode the given value from a 22 character URL-safe Base64 string to
|
||||
/// a <see cref="System.Guid"/>.</para>
|
||||
/// <para>The difference between TryParse and TryDecode:</para>
|
||||
/// <list type="number">
|
||||
/// <item>
|
||||
/// <term><see cref="TryParse(string, out ShortGuid)"/></term>
|
||||
/// <description>Supports: Guid & ShortGuid;</description>
|
||||
/// <description>Tries to parse first as a <see cref="ShortGuid"/>, then as a
|
||||
/// <see cref="System.Guid"/>, outputs the <see cref="ShortGuid"/> instance.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term><see cref="TryParse(string, out Guid)"/></term>
|
||||
/// <description>Supports: Guid & ShortGuid;</description>
|
||||
/// <description>Tries to parse first as a <see cref="ShortGuid"/>, then as a
|
||||
/// <see cref="System.Guid"/>, outputs the underlying <see cref="System.Guid"/>.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term><see cref="TryDecode(string, out Guid)"/></term>
|
||||
/// <description>Supports: ShortGuid;</description>
|
||||
/// <description>Tries to decode a 22 character URL-safe Base64 string as a
|
||||
/// <see cref="ShortGuid"/> only, but outputs the result as a <see cref="System.Guid"/> - this method.</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
/// <param name="value">The ShortGuid encoded string to decode.</param>
|
||||
/// <param name="guid">A new <see cref="System.Guid"/> instance from the parsed string.</param>
|
||||
/// <returns>A boolean indicating if the decode was successful.</returns>
|
||||
public static bool TryDecode(string? value, out Guid guid)
|
||||
{
|
||||
try
|
||||
{
|
||||
guid = DecodeCore(value);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
guid = Guid.Empty;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Supports ShortGuid & Guid formats.</para>
|
||||
/// <para>Tries to parse the value from either a 22 character URL-safe Base64 string or
|
||||
/// a <see cref="System.Guid"/> string, and outputs a <see cref="ShortGuid"/> instance.</para>
|
||||
/// <para>The difference between TryParse and TryDecode:</para>
|
||||
/// <list type="number">
|
||||
/// <item>
|
||||
/// <term><see cref="TryParse(string, out ShortGuid)"/></term>
|
||||
/// <description>Supports: Guid & ShortGuid; </description>
|
||||
/// <description>Tries to parse first as a <see cref="ShortGuid"/>, then as a
|
||||
/// <see cref="System.Guid"/>, outputs the <see cref="ShortGuid"/> instance - this method.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term><see cref="TryParse(string, out Guid)"/></term>
|
||||
/// <description>Supports: Guid & ShortGuid;</description>
|
||||
/// <description>Tries to parse first as a <see cref="ShortGuid"/>, then as a
|
||||
/// <see cref="System.Guid"/>, outputs the <see cref="System.Guid"/>.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term><see cref="TryDecode(string, out Guid)"/></term>
|
||||
/// <description>Supports: ShortGuid;</description>
|
||||
/// <description>Tries to decode a 22 character URL-safe Base64 string as a
|
||||
/// <see cref="ShortGuid"/> only, but outputs the result as a <see cref="System.Guid"/>.</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
/// <param name="value">The ShortGuid encoded string or string representation of a Guid.</param>
|
||||
/// <param name="shortGuid">A new <see cref="ShortGuid"/> instance from the parsed string.</param>
|
||||
public static bool TryParse(string? value, out ShortGuid shortGuid)
|
||||
{
|
||||
// Try a ShortGuid string.
|
||||
if (TryDecode(value, out var guid))
|
||||
{
|
||||
shortGuid = guid;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try a Guid string.
|
||||
if (value.TryParseGuid(out var guid2))
|
||||
{
|
||||
shortGuid = guid2;
|
||||
return true;
|
||||
}
|
||||
|
||||
shortGuid = Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Supports ShortGuid & Guid formats.</para>
|
||||
/// <para>Tries to parse the value either a 22 character URL-safe Base64 string or
|
||||
/// <see cref="System.Guid"/> string, and outputs the <see cref="Guid"/> value.</para>
|
||||
/// <para>The difference between TryParse and TryDecode:</para>
|
||||
/// <list type="number">
|
||||
/// <item>
|
||||
/// <term><see cref="TryParse(string, out ShortGuid)"/></term>
|
||||
/// <description>Supports: Guid & ShortGuid;</description>
|
||||
/// <description>Tries to parse first as a <see cref="ShortGuid"/>, then as a
|
||||
/// <see cref="System.Guid"/>, outputs the <see cref="ShortGuid"/> instance.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term><see cref="TryParse(string, out Guid)"/></term>
|
||||
/// <description>Supports: Guid & ShortGuid;</description>
|
||||
/// <description>Tries to parse first as a <see cref="ShortGuid"/>, then as a
|
||||
/// <see cref="System.Guid"/>, outputs the <see cref="System.Guid"/> - this method.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term><see cref="TryDecode(string, out Guid)"/></term>
|
||||
/// <description>Supports: ShortGuid;</description>
|
||||
/// <description>Tries to decode a 22 character URL-safe Base64 string as a
|
||||
/// <see cref="ShortGuid"/> only, outputting the result as a <see cref="System.Guid"/>.</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
/// <param name="value">The ShortGuid encoded string or string representation of a Guid.</param>
|
||||
/// <param name="guid">A new <see cref="System.Guid"/> instance from the parsed string.</param>
|
||||
/// <returns>A boolean indicating if the parse was successful.</returns>
|
||||
public static bool TryParse(string? value, out Guid guid)
|
||||
{
|
||||
// Try a ShortGuid string.
|
||||
if (TryDecode(value, out guid))
|
||||
return true;
|
||||
|
||||
// Try a Guid string.
|
||||
if (value.TryParseGuid(out guid))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
guid = Guid.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
#region Operators
|
||||
|
||||
/// <summary>
|
||||
/// Determines if both ShortGuid instances have the same underlying <see cref="System.Guid"/> value.
|
||||
/// </summary>
|
||||
public static bool operator ==(ShortGuid x, ShortGuid y)
|
||||
{
|
||||
if (ReferenceEquals(x, null))
|
||||
return ReferenceEquals(y, null);
|
||||
|
||||
return x.underlyingGuid == y.underlyingGuid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if both instances have the same underlying <see cref="System.Guid"/> value.
|
||||
/// </summary>
|
||||
public static bool operator ==(ShortGuid x, Guid y)
|
||||
{
|
||||
if (ReferenceEquals(x, null))
|
||||
return ReferenceEquals(y, null);
|
||||
|
||||
return x.underlyingGuid == y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if both instances have the same underlying <see cref="System.Guid"/> value.
|
||||
/// </summary>
|
||||
public static bool operator ==(Guid x, ShortGuid y) => y == x; // NB: order of arguments
|
||||
|
||||
/// <summary>
|
||||
/// Determines if both ShortGuid instances do not have the same underlying <see cref="System.Guid"/> value.
|
||||
/// </summary>
|
||||
public static bool operator !=(ShortGuid x, ShortGuid y) => !(x == y);
|
||||
|
||||
/// <summary>
|
||||
/// Determines if both instances do not have the same underlying <see cref="System.Guid"/> value.
|
||||
/// </summary>
|
||||
public static bool operator !=(ShortGuid x, Guid y) => !(x == y);
|
||||
|
||||
/// <summary>
|
||||
/// Determines if both instances do not have the same underlying <see cref="System.Guid"/> value.
|
||||
/// </summary>
|
||||
public static bool operator !=(Guid x, ShortGuid y) => !(x == y);
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts the ShortGuid to its string equivalent.
|
||||
/// </summary>
|
||||
public static implicit operator string(ShortGuid shortGuid) => shortGuid.encodedString;
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts the ShortGuid to its <see cref="System.Guid"/> equivalent.
|
||||
/// </summary>
|
||||
public static implicit operator Guid(ShortGuid shortGuid) => shortGuid.underlyingGuid;
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts the string to a ShortGuid.
|
||||
/// </summary>
|
||||
public static implicit operator ShortGuid(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return Empty;
|
||||
|
||||
if (TryParse(value, out ShortGuid shortGuid))
|
||||
return shortGuid;
|
||||
|
||||
throw new FormatException("ShortGuid should contain 22 Base64 characters or "
|
||||
+ "Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts the <see cref="System.Guid"/> to a ShortGuid.
|
||||
/// </summary>
|
||||
public static implicit operator ShortGuid(Guid guid)
|
||||
{
|
||||
if (guid == Guid.Empty)
|
||||
return Empty;
|
||||
|
||||
return new ShortGuid(guid);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
using System.IO.FileFormats;
|
||||
using System.IO.FileFormats;
|
||||
using System.Application.Columns;
|
||||
|
||||
namespace System.Application.Entities
|
||||
namespace System.Application.Entities.Abstractions
|
||||
{
|
||||
public interface IImage : IEntity<Guid>, ISoftDeleted
|
||||
{
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace System.Application.Entities.Abstractions
|
||||
{
|
||||
public interface IUser : IEntity<Guid>
|
||||
{
|
||||
/// <summary>
|
||||
/// 昵称
|
||||
/// </summary>
|
||||
string? NickName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户名
|
||||
/// </summary>
|
||||
string? UserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 封禁到解封日期
|
||||
/// </summary>
|
||||
DateTimeOffset? BanEnd { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
using System.Application.Columns;
|
||||
using System.Application.Columns;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace System.Application.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证码纪录表(短信或邮箱)
|
||||
/// </summary>
|
||||
/// <typeparam name="TSendSmsRequestType"></typeparam>
|
||||
/// <typeparam name="TAuthMessageType"></typeparam>
|
||||
public class AuthMessageRecord<TSendSmsRequestType, TAuthMessageType> : IEntity<Guid>, ICreationTime
|
||||
where TSendSmsRequestType : notnull, Enum
|
||||
where TAuthMessageType : notnull, Enum
|
||||
where TSendSmsRequestType : struct, Enum
|
||||
where TAuthMessageType : struct, Enum
|
||||
{
|
||||
[Key] // EF 主键
|
||||
public Guid Id { get; set; }
|
||||
@@ -30,8 +34,7 @@ namespace System.Application.Entities
|
||||
/// 调用方IP地址
|
||||
/// </summary>
|
||||
[Required] // EF not null
|
||||
[NotNull, DisallowNull] // C# 8 not null
|
||||
public string? IPAddress { get; set; }
|
||||
public string IPAddress { get; set; } = string.Empty;
|
||||
|
||||
#region 提供商内容
|
||||
|
||||
@@ -40,8 +43,7 @@ namespace System.Application.Entities
|
||||
/// 比如网易/阿里云
|
||||
/// </summary>
|
||||
[Required] // EF not null
|
||||
[NotNull, DisallowNull] // C# 8 not null
|
||||
public string? Channel { get; set; }
|
||||
public string Channel { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 第三方提供商返回的内容
|
||||
@@ -66,8 +68,7 @@ namespace System.Application.Entities
|
||||
/// 短信内容通常仅含有验证码数字,不包括其他的提示语文字
|
||||
/// </summary>
|
||||
[Required] // EF not null
|
||||
[NotNull, DisallowNull] // C# 8 not null
|
||||
public string? Content { get; set; }
|
||||
public string Content { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 是否校验过
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace System.Application.Entities
|
||||
{
|
||||
public interface IUser<TKey> where TKey : IEquatable<TKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// 昵称
|
||||
/// </summary>
|
||||
string? NickName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Application.Columns;
|
||||
using System.Application.Entities.Abstractions;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
@@ -13,10 +14,9 @@ namespace System.Application.Entities
|
||||
/// </summary>
|
||||
[Table("Images")]
|
||||
[DebuggerDisplay("{DebuggerDisplay(),nq}")]
|
||||
public class Image<TUserKey, TUser, TUploadImageType> : IImage, INEWSEQUENTIALID
|
||||
where TUser : IUser<TUserKey>
|
||||
where TUserKey : IEquatable<TUserKey>
|
||||
where TUploadImageType : Enum
|
||||
public class Image<TUser, TUploadImageType> : IImage, INEWSEQUENTIALID
|
||||
where TUser : IUser
|
||||
where TUploadImageType : struct, Enum
|
||||
{
|
||||
string DebuggerDisplay() => $"{Id}, {Type}, {SHA384}";
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace System.Application.Entities
|
||||
/// <summary>
|
||||
/// 上传纪录
|
||||
/// </summary>
|
||||
public virtual List<ImageUploadRecord<TUserKey, TUser, TUploadImageType>>? Records { get; set; }
|
||||
public virtual List<ImageUploadRecord<TUser, TUploadImageType>>? Records { get; set; }
|
||||
|
||||
public string GetFileName() => SHA384 + Type.GetExtension();
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Application.Columns;
|
||||
using System.Application.Columns;
|
||||
using System.Application.Entities.Abstractions;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace System.Application.Entities
|
||||
{
|
||||
@@ -11,12 +11,11 @@ namespace System.Application.Entities
|
||||
/// </summary>
|
||||
[Table("ImageUploadRecords")]
|
||||
[DebuggerDisplay("{DebuggerDisplay(),nq}")]
|
||||
public class ImageUploadRecord<TUserKey, TUser, TUploadImageType> : INEWSEQUENTIALID, ICreationTime
|
||||
where TUser : IUser<TUserKey>
|
||||
where TUserKey : IEquatable<TUserKey>
|
||||
where TUploadImageType : Enum
|
||||
public class ImageUploadRecord<TUser, TUploadImageType> : INEWSEQUENTIALID, ICreationTime
|
||||
where TUser : IUser
|
||||
where TUploadImageType : struct, Enum
|
||||
{
|
||||
string DebuggerDisplay() => $"{ImagesId}, {Type}, {User?.NickName ?? (UserId?.ToString())}";
|
||||
string DebuggerDisplay() => $"{ImagesId}, {Type}, {User?.NickName ?? (UserId.ToString())}";
|
||||
|
||||
[Key] // EF 主键
|
||||
public Guid Id { get; set; }
|
||||
@@ -25,8 +24,7 @@ namespace System.Application.Entities
|
||||
/// 上传的用户ID
|
||||
/// </summary>
|
||||
[Required] // EF not null
|
||||
[NotNull, DisallowNull] // C# 8 not null
|
||||
public TUserKey? UserId { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 上传的用户
|
||||
@@ -44,7 +42,6 @@ namespace System.Application.Entities
|
||||
/// 上传图片类型(用途)
|
||||
/// </summary>
|
||||
[Required] // EF not null
|
||||
[NotNull, DisallowNull] // C# 8 not null
|
||||
public TUploadImageType? Type { get; set; }
|
||||
public TUploadImageType Type { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
using System.Application.Columns;
|
||||
using System.Application.Columns;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace System.Application.Entities
|
||||
{
|
||||
@@ -15,12 +14,10 @@ namespace System.Application.Entities
|
||||
public sealed class KeyValuePair : IEntity<string>, ISoftDeleted
|
||||
{
|
||||
[Key] // EF 主键
|
||||
[NotNull, DisallowNull] // C# 8 not null
|
||||
public string? Id { get; set; }
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
[Required] // EF not null
|
||||
[NotNull, DisallowNull] // C# 8 not null
|
||||
public string? Value { get; set; }
|
||||
public string Value { get; set; } = string.Empty;
|
||||
|
||||
public bool SoftDeleted { get; set; }
|
||||
|
||||
|
||||
@@ -19,6 +19,36 @@ namespace System.Application
|
||||
public static void Main<TStartup>(string projectName,
|
||||
string[] args, Action<string>? writeLine = null) where TStartup : class
|
||||
{
|
||||
var logger = NLogBuilder.ConfigureNLog(InitNLogConfig()).GetCurrentClassLogger();
|
||||
try
|
||||
{
|
||||
logger.Info("init main");
|
||||
var host = CreateHostBuilder<TStartup>(args).Build();
|
||||
DI.Init(host.Services);
|
||||
|
||||
writeLine ??= Console.WriteLine;
|
||||
Console.OutputEncoding = Encoding.Unicode; // 使用UTF16编码输出控制台文字
|
||||
Version = typeof(TStartup).Assembly.GetName().Version?.ToString() ?? string.Empty;
|
||||
|
||||
UnityConsoleOutputHead.Write(writeLine,
|
||||
projectName,
|
||||
Version,
|
||||
RuntimeVersion,
|
||||
CentralProcessorName);
|
||||
|
||||
host.Run();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
//NLog: catch setup errors
|
||||
logger.Error(exception, "Stopped program because of exception");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
|
||||
NLog.LogManager.Shutdown();
|
||||
}
|
||||
static LoggingConfiguration InitNLogConfig()
|
||||
{
|
||||
// https://github.com/NLog/NLog/wiki/Getting-started-with-ASP.NET-Core-5
|
||||
@@ -62,36 +92,6 @@ namespace System.Application
|
||||
|
||||
return xmlConfig;
|
||||
}
|
||||
var logger = NLogBuilder.ConfigureNLog(InitNLogConfig()).GetCurrentClassLogger();
|
||||
try
|
||||
{
|
||||
logger.Info("init main");
|
||||
var host = CreateHostBuilder<TStartup>(args).Build();
|
||||
DI.Init(host.Services);
|
||||
|
||||
writeLine ??= Console.WriteLine;
|
||||
Console.OutputEncoding = Encoding.Unicode; // 使用UTF16编码输出控制台文字
|
||||
Version = typeof(TStartup).Assembly.GetName().Version?.ToString() ?? string.Empty;
|
||||
|
||||
UnityConsoleOutputHead.Write(writeLine,
|
||||
projectName,
|
||||
Version,
|
||||
RuntimeVersion,
|
||||
CentralProcessorName);
|
||||
|
||||
host.Run();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
//NLog: catch setup errors
|
||||
logger.Error(exception, "Stopped program because of exception");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
|
||||
NLog.LogManager.Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder<TStartup>(string[] args) where TStartup : class =>
|
||||
|
||||
+2
-2
@@ -98,13 +98,13 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
## 📁 存储空间
|
||||
- AppData
|
||||
- Microsoft Store ```%USERPROFILE%\AppData\Local\Packages\4651ED44255E.47979655102CE_k6txddmbb6c52\LocalState```
|
||||
- Windows ```\AppData```
|
||||
- Windows ```\AppData``` or ```%LocalAppData%\Steam++```
|
||||
- macOS ```~/Library/Steam++```
|
||||
- Linux ```$XDG_DATA_HOME/Steam++``` or ```$HOME/.local/share/Steam++```
|
||||
- Android ```/data/data/net.steampp.app/files```
|
||||
- Cache
|
||||
- Microsoft Store ```%USERPROFILE%\AppData\Local\Packages\4651ED44255E.47979655102CE_k6txddmbb6c52\LocalCache```
|
||||
- Windows ```\Cache```
|
||||
- Windows ```\Cache``` or ```%Tmp%\Steam++```
|
||||
- macOS ```~/Library/Caches/Steam++```
|
||||
- Linux ```$XDG_CACHE_HOME/Steam++``` or ```$HOME/.cache/Steam++```
|
||||
- Android ```/data/data/net.steampp.app/cache```
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using System.Application.Columns;
|
||||
using System.Application.Entities;
|
||||
@@ -53,7 +53,7 @@ namespace System.Application.Repositories
|
||||
public Repository(TDbContext dbContext) : base(dbContext)
|
||||
{
|
||||
Entity = dbContext.Set<TEntity>();
|
||||
mEntityType = new Lazy<IEntityType>(() => dbContext.Model.FindEntityType(entityType));
|
||||
mEntityType = new Lazy<IEntityType>(() => dbContext.Model.FindEntityType(entityType).ThrowIsNull(nameof(entityType)));
|
||||
mTableName = new Lazy<string>(() => EFCoreUtils.GetTableNameByClrType(dbContext.Database, EntityType));
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace System.Application.Repositories
|
||||
|
||||
#region 查(通用查询)
|
||||
|
||||
public virtual Task<TEntity?> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate) => FirstOrDefaultAsync(predicate, default);
|
||||
//public virtual Task<TEntity?> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate) => FirstOrDefaultAsync(predicate, default);
|
||||
|
||||
/// <inheritdoc cref="FirstOrDefaultAsync(Expression{Func{TEntity, bool}})"/>
|
||||
public virtual async Task<TEntity?> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace System
|
||||
{
|
||||
public static class ColumnTypeNames
|
||||
{
|
||||
public const string Decimal_18_4 = "decimal(18,4)";
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using System.Application.Repositories;
|
||||
@@ -41,7 +41,7 @@ namespace System
|
||||
public static string GetTableNameByClrType(DbContext context, Type entityType)
|
||||
{
|
||||
var entityType_ = context.Model.FindEntityType(entityType);
|
||||
return GetTableNameByClrType(context.Database, entityType_);
|
||||
return GetTableNameByClrType(context.Database, entityType_.ThrowIsNull(nameof(entityType)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace System.Application.Repositories
|
||||
|
||||
#region 查(通用查询)
|
||||
|
||||
public virtual async Task<TEntity?> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate)
|
||||
public virtual async Task<TEntity?> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var dbConnection = await GetDbConnection().ConfigureAwait(false);
|
||||
return await AttemptAndRetry(() => dbConnection.FindAsync(predicate)).ConfigureAwait(false);
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
using Android.Graphics.Drawables;
|
||||
using Android.Widget;
|
||||
using R = System.Application.Resource;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="CheckBox"/> 扩展
|
||||
/// </summary>
|
||||
public static partial class CheckBoxExtensions
|
||||
{
|
||||
public static void SetChecked(this CheckBox checkBox, bool? value)
|
||||
{
|
||||
if (checkBox.GetChecked() == value) return;
|
||||
|
||||
Drawable? drawable = null;
|
||||
if (value.HasValue)
|
||||
{
|
||||
checkBox.Checked = value.Value;
|
||||
if (checkBox.GetTag(R.Id.checkbox_indeterminate_state) is Drawable d && checkBox.ButtonDrawable != d) drawable = d;
|
||||
checkBox.SetTag(R.Id.checkbox_indeterminate_state, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (checkBox.GetTag(R.Id.checkbox_indeterminate_state) == null)
|
||||
checkBox.SetTag(R.Id.checkbox_indeterminate_state, checkBox.ButtonDrawable);
|
||||
drawable = checkBox.Context!.GetDrawableCompat(R.Drawable.bg_checkbox_indeterminate);
|
||||
}
|
||||
if (drawable != null) checkBox.SetButtonDrawable(drawable);
|
||||
}
|
||||
|
||||
public static bool? GetChecked(this CheckBox checkBox)
|
||||
{
|
||||
var state = checkBox.GetTag(R.Id.checkbox_indeterminate_state);
|
||||
if (state != null) return null;
|
||||
return checkBox.Checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using NativeService = System.Application.Services.Native.IServiceBase;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System
|
||||
{
|
||||
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
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System.Application.UI.Activities;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
#if __XAMARIN_FORMS__
|
||||
using Xamarin.Forms.Platform.Android;
|
||||
#endif
|
||||
using Fragment = AndroidX.Fragment.App.Fragment;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
@@ -13,9 +14,11 @@ namespace System
|
||||
/// </summary>
|
||||
public static partial class FragmentExtensions
|
||||
{
|
||||
#if __XAMARIN_FORMS__
|
||||
public static bool IsEmbeddedForms(this Fragment? fragment)
|
||||
{
|
||||
return fragment?.Activity is FormsAppCompatActivity;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
using Android.Content;
|
||||
using Android.Graphics;
|
||||
using Android.Graphics.Drawables;
|
||||
using AndroidX.Annotations;
|
||||
using AndroidX.RecyclerView.Widget;
|
||||
using System.Application;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System
|
||||
{
|
||||
/// <summary>
|
||||
/// 对 <see cref="RecyclerView"/> 类的扩展函数集
|
||||
/// </summary>
|
||||
public static class RecycleViewExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 设置使用线性布局管理列表,默认纵向,使用 <see cref="LinearLayoutManager2"/>
|
||||
/// </summary>
|
||||
/// <param name="recycleView"></param>
|
||||
/// <param name="orientation"></param>
|
||||
/// <param name="reverseLayout"></param>
|
||||
public static void SetLinearLayoutManager(this RecyclerView recycleView, int orientation = LinearLayoutManager.Vertical, bool reverseLayout = false)
|
||||
{
|
||||
var layout = new LinearLayoutManager2(recycleView.Context!, orientation, reverseLayout);
|
||||
recycleView.SetLayoutManager(layout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加纵向列表组项间距,使用 <see cref="VerticalGroupItemDecoration(int)"/>
|
||||
/// </summary>
|
||||
/// <param name="recycleView"></param>
|
||||
/// <param name="height"></param>
|
||||
public static void AddVerticalGroupItemDecoration(this RecyclerView recycleView, int height)
|
||||
{
|
||||
VerticalGroupItemDecoration itemDecoration = new(height);
|
||||
recycleView.AddItemDecoration(itemDecoration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加纵向列表组项间距,使用 <see cref="VerticalGroupItemDecoration(int)"/>
|
||||
/// </summary>
|
||||
/// <param name="recycleView"></param>
|
||||
/// <param name="heightResId"></param>
|
||||
public static void AddVerticalGroupItemDecorationRes(this RecyclerView recycleView, [DimenRes] int heightResId)
|
||||
{
|
||||
var height = recycleView.Context!.GetDimensionPixelSize(heightResId);
|
||||
recycleView.AddVerticalGroupItemDecoration(height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加纵向列表项间距,使用 <see cref="VerticalItemDecoration(int)"/>
|
||||
/// </summary>
|
||||
/// <param name="recycleView"></param>
|
||||
/// <param name="height"></param>
|
||||
public static void AddVerticalItemDecoration(this RecyclerView recycleView, int height)
|
||||
{
|
||||
VerticalItemDecoration itemDecoration = new(height);
|
||||
recycleView.AddItemDecoration(itemDecoration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加纵向列表项间距,使用 <see cref="VerticalItemDecoration(int)"/>
|
||||
/// </summary>
|
||||
/// <param name="recycleView"></param>
|
||||
/// <param name="heightResId"></param>
|
||||
public static void AddVerticalItemDecorationRes(this RecyclerView recycleView, [DimenRes] int heightResId)
|
||||
{
|
||||
var height = recycleView.Context!.GetDimensionPixelSize(heightResId);
|
||||
recycleView.AddVerticalItemDecoration(height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加纵向列表项间距,使用 <see cref="VerticalItemDecoration2(int, int, bool)"/>
|
||||
/// </summary>
|
||||
/// <param name="recycleView"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="paddingBottom"></param>
|
||||
/// <param name="noTop"></param>
|
||||
public static void AddVerticalItemDecoration(this RecyclerView recycleView, int height, int paddingBottom, bool noTop = false)
|
||||
{
|
||||
VerticalItemDecoration2 itemDecoration = new(height, paddingBottom, noTop);
|
||||
recycleView.AddItemDecoration(itemDecoration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加纵向列表项间距,使用 <see cref="VerticalItemDecoration2.Get(Context, int, int, bool)"/>
|
||||
/// </summary>
|
||||
/// <param name="recycleView"></param>
|
||||
/// <param name="heightResId"></param>
|
||||
/// <param name="paddingBottomResId"></param>
|
||||
/// <param name="noTop"></param>
|
||||
public static void AddVerticalItemDecorationRes(this RecyclerView recycleView, [DimenRes] int heightResId, [DimenRes] int paddingBottomResId = default, bool noTop = false)
|
||||
{
|
||||
var itemDecoration = VerticalItemDecoration2.Get(recycleView.Context!, heightResId, paddingBottomResId, noTop);
|
||||
recycleView.AddItemDecoration(itemDecoration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加分割线,由 <see cref="Drawable"/> 绘制
|
||||
/// </summary>
|
||||
/// <param name="recycleView"></param>
|
||||
/// <param name="drawable"></param>
|
||||
/// <param name="orientation"></param>
|
||||
public static void AddDividerItemDecoration(this RecyclerView recycleView, Drawable drawable, int orientation = DividerItemDecoration.Vertical)
|
||||
{
|
||||
DividerItemDecoration itemDecoration = new(recycleView.Context!, orientation);
|
||||
itemDecoration.Drawable = drawable;
|
||||
recycleView.AddItemDecoration(itemDecoration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加分割线,由 <see cref="DrawableResAttribute"/> 绘制
|
||||
/// </summary>
|
||||
/// <param name="recycleView"></param>
|
||||
/// <param name="drawableResId"></param>
|
||||
/// <param name="orientation"></param>
|
||||
public static void AddDividerItemDecorationRes(this RecyclerView recycleView, [DrawableRes] int drawableResId, int orientation = DividerItemDecoration.Vertical)
|
||||
{
|
||||
var drawable = recycleView.Context!.GetDrawableCompat(drawableResId);
|
||||
recycleView.AddDividerItemDecoration(drawable, orientation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加分割线,由 <see cref="Color"/> 绘制
|
||||
/// </summary>
|
||||
/// <param name="recycleView"></param>
|
||||
/// <param name="color"></param>
|
||||
/// <param name="size"></param>
|
||||
/// <param name="orientation"></param>
|
||||
public static void AddDividerItemDecoration(this RecyclerView recycleView, int colorArgb, int size = 1, int orientation = DividerItemDecoration.Vertical)
|
||||
{
|
||||
GradientDrawable shape = new();
|
||||
shape.SetColor(colorArgb);
|
||||
if (orientation == DividerItemDecoration.Vertical)
|
||||
{
|
||||
shape.SetSize(0, size);
|
||||
}
|
||||
else if (orientation == DividerItemDecoration.Horizontal)
|
||||
{
|
||||
shape.SetSize(size, 0);
|
||||
}
|
||||
recycleView.AddDividerItemDecoration(shape, orientation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加分割线,由 <see cref="ColorResAttribute"/> 绘制
|
||||
/// </summary>
|
||||
/// <param name="recycleView"></param>
|
||||
/// <param name="colorResId"></param>
|
||||
/// <param name="size"></param>
|
||||
/// <param name="orientation"></param>
|
||||
public static void AddDividerItemDecorationRes(this RecyclerView recycleView, [ColorRes] int colorResId, int size = 1, int orientation = DividerItemDecoration.Vertical)
|
||||
{
|
||||
var colorArgb = recycleView.Context!.GetColorCompat(colorResId);
|
||||
recycleView.AddDividerItemDecoration(colorArgb, size, orientation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加分割线
|
||||
/// </summary>
|
||||
/// <param name="recycleView"></param>
|
||||
/// <param name="orientation"></param>
|
||||
public static void AddDividerItemDecorationRes(this RecyclerView recycleView, int orientation = DividerItemDecoration.Vertical)
|
||||
{
|
||||
var colorResId = Resource.Color.bg_divider;
|
||||
recycleView.AddDividerItemDecorationRes(colorResId, orientation: orientation);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user