Skip to content

Commit 8f42e04

Browse files
JusterZhuclaude
andcommitted
feat: add NuGet package icon, banner, and update component READMEs
- Add PackageIcon (GeneralUpdate.png) and ApplicationIcon (GeneralUpdate.ico) to csproj - Include icon files and component README.md in NuGet package - Add banner image to all three READMEs (en/zh-CN/default) - Rewrite READMEs to reflect actual API surface: - CreateDefault factory signatures and default DI wiring - IAndroidBootstrap method table with return types - Model hierarchy (UpdateOperationResult base + 4 subtypes) - Enum values (UpdateState, UpdateFailureReason) - Event payloads - Update csproj to pack component README.md instead of root README Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 2f8b812 commit 8f42e04

8 files changed

Lines changed: 310 additions & 351 deletions

File tree

imgs/GeneralUpdate.ico

16.6 KB
Binary file not shown.

imgs/GeneralUpdate.png

15.9 KB
Loading

imgs/GeneralUpdate128.png

19.1 KB
Loading

imgs/banner.png

3.1 MB
Loading

src/GeneralUpdate.Avalonia.Android/GeneralUpdate.Avalonia.Android.csproj

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<TargetFramework>net10.0-android</TargetFramework>
4+
<ApplicationIcon>GeneralUpdate.ico</ApplicationIcon>
5+
<PackageIcon>GeneralUpdate.png</PackageIcon>
46
<SupportedOSPlatformVersion>26.0</SupportedOSPlatformVersion>
57
<Nullable>enable</Nullable>
68
<ImplicitUsings>enable</ImplicitUsings>
@@ -34,8 +36,10 @@
3436
<PackageReference Include="Xamarin.AndroidX.Core" Version="1.13.1.2" />
3537
</ItemGroup>
3638

37-
<!-- Include README in the package -->
39+
<!-- Include package assets -->
3840
<ItemGroup>
39-
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
41+
<None Include="../../imgs/GeneralUpdate.ico" Pack="true" PackagePath="\" />
42+
<None Include="../../imgs/GeneralUpdate.png" Pack="true" PackagePath="\" />
43+
<None Include="README.md" Pack="true" PackagePath="\" />
4044
</ItemGroup>
4145
</Project>
Lines changed: 85 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,123 @@
1+
<p align="center">
2+
<img src="https://raw.githubusercontent.com/GeneralLibrary/GeneralUpdate.Avalonia/main/imgs/banner.png" alt="GeneralUpdate.Avalonia">
3+
</p>
4+
15
# GeneralUpdate.Avalonia.Android
26

3-
UI-free Android auto-update core for Avalonia 12+ apps (`net8.0-android`).
7+
UI-free Android auto-update core for Avalonia 12+ apps (`net10.0-android`).
48

59
## Features
610

7-
- Android-only update core (`net8.0-android`; compatible design for `net9.0-android`)
811
- No built-in UI (host app owns dialogs/progress/error rendering)
9-
- End-to-end orchestration: validate → download/resume → hash verify → installer launch
10-
- Resumable HTTP download with sidecar metadata and streaming writes
11-
- Replaceable abstractions for version comparison, downloader, hash validator, installer, logger, and event dispatcher
12-
13-
## Core API
14-
15-
- `GeneralUpdateBootstrap.CreateDefault(...)`
16-
- `IAndroidBootstrap.ValidateAsync(...)`
17-
- `IAndroidBootstrap.DownloadAndVerifyAsync(...)`
18-
- `IAndroidBootstrap.LaunchInstallerAsync(...)`
19-
20-
Events:
21-
22-
- `AddListenerValidate`
23-
- `AddListenerDownloadProgressChanged`
24-
- `AddListenerUpdateCompleted`
25-
- `AddListenerUpdateFailed`
12+
- End-to-end orchestration: validate → resume-download → SHA-256 verify → install
13+
- Resumable HTTP download with sidecar metadata and smoothed speed reporting
14+
- Replaceable abstractions for every pipeline stage
15+
- Operation serialization — concurrent calls are gated, safe from any thread
2616

2717
## Quick Start
2818

19+
```bash
20+
dotnet add package GeneralUpdate.Avalonia.Android
21+
```
22+
2923
```csharp
3024
using GeneralUpdate.Avalonia.Android;
31-
using GeneralUpdate.Avalonia.Android.Abstractions;
3225
using GeneralUpdate.Avalonia.Android.Models;
3326

34-
public sealed class AvaloniaUiDispatcher : IUpdateEventDispatcher
35-
{
36-
public void Dispatch(Action callback)
37-
{
38-
Avalonia.Threading.Dispatcher.UIThread.Post(callback);
39-
}
40-
}
41-
4227
var options = new AndroidUpdateOptions
4328
{
44-
DownloadDirectoryPath = Path.Combine(Android.App.Application.Context.CacheDir!.AbsolutePath!, "update"),
29+
DownloadDirectoryPath = Path.Combine(
30+
Android.App.Application.Context.CacheDir!.AbsolutePath!, "update"),
4531
FileProviderAuthority = "com.example.app.generalupdate.fileprovider"
4632
};
4733

48-
var bootstrap = GeneralUpdateBootstrap.CreateDefault(
49-
options,
50-
eventDispatcher: new AvaloniaUiDispatcher());
34+
using IAndroidBootstrap bootstrap = GeneralUpdateBootstrap.CreateDefault(options);
5135

5236
var packageInfo = new UpdatePackageInfo
5337
{
54-
Version = "2.3.0",
38+
Version = "2.3.0",
5539
DownloadUrl = "https://example.com/app-release.apk",
56-
Sha256 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
57-
FileSize = 52_428_800,
58-
FileName = "app-release.apk"
40+
Sha256 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
41+
FileSize = 52_428_800,
42+
FileName = "app-release.apk"
5943
};
6044

6145
var check = await bootstrap.ValidateAsync(packageInfo, "2.2.1", CancellationToken.None);
6246
if (check.UpdateFound)
6347
{
64-
var prepared = await bootstrap.DownloadAndVerifyAsync(packageInfo, CancellationToken.None);
65-
if (prepared.Success && prepared.FilePath is not null)
48+
var result = await bootstrap.DownloadAndVerifyAsync(packageInfo, CancellationToken.None);
49+
if (result.Success && result.FilePath is not null)
6650
{
67-
await bootstrap.LaunchInstallerAsync(packageInfo, prepared.FilePath, CancellationToken.None);
51+
await bootstrap.LaunchInstallerAsync(packageInfo, result.FilePath, CancellationToken.None);
6852
}
6953
}
7054
```
7155

72-
## Event payloads
56+
## API
57+
58+
### Factory
59+
60+
`GeneralUpdateBootstrap.CreateDefault(options, contextProvider?, activityProvider?, httpClient?, versionComparer?, eventDispatcher?, logger?)`
61+
62+
Default wiring:
63+
64+
| Abstraction | Default |
65+
|---|---|
66+
| `IAndroidContextProvider` | `DefaultAndroidContextProvider` |
67+
| `IAndroidActivityProvider` | `NullAndroidActivityProvider` |
68+
| `IUpdateLogger` | `NoOpUpdateLogger` |
69+
| `IFileStorage` | `PhysicalFileStorage` |
70+
| `IUpdateDownloader` | `HttpResumableApkDownloader` |
71+
| `IHashValidator` | `Sha256HashValidator` |
72+
| `IApkInstaller` | `AndroidApkInstaller` |
73+
| `IVersionComparer` | `SystemVersionComparer` |
74+
75+
### IAndroidBootstrap Methods
76+
77+
| Method | Returns |
78+
|---|---|
79+
| `ValidateAsync(packageInfo, currentVersion, ct)` | `UpdateCheckResult` |
80+
| `DownloadAndVerifyAsync(packageInfo, ct)` | `UpdateOperationResult` |
81+
| `LaunchInstallerAsync(packageInfo, apkFilePath, ct)` | `InstallResult` |
82+
| `GetSnapshot()` | `UpdateStateSnapshot` |
83+
84+
### Events
85+
86+
| Event | Args |
87+
|---|---|
88+
| `AddListenerValidate` | `ValidateEventArgs` |
89+
| `AddListenerDownloadProgressChanged` | `DownloadProgressChangedEventArgs` |
90+
| `AddListenerUpdateCompleted` | `UpdateCompletedEventArgs` |
91+
| `AddListenerUpdateFailed` | `UpdateFailedEventArgs` |
92+
93+
### Model Hierarchy
94+
95+
```
96+
UpdateOperationResult (base)
97+
├── Success, State, FailureReason, Message, PackageInfo, FilePath, Exception
98+
├── UpdateCheckResult → + UpdateFound, CurrentVersion
99+
├── DownloadResult
100+
├── HashValidationResult → + ActualSha256, ExpectedSha256
101+
└── InstallResult
102+
```
103+
104+
### Enums
105+
106+
`UpdateState`: `None`, `Checking`, `UpdateAvailable`, `Downloading`, `Verifying`, `ReadyToInstall`, `Installing`, `Completed`, `Failed`, `Canceled`
73107

74-
- `ValidateEventArgs`: `PackageInfo`, `CurrentVersion`
75-
- `DownloadProgressChangedEventArgs`: speed, downloaded bytes, remaining bytes, total bytes, percentage, package info, status
76-
- `UpdateCompletedEventArgs` / `UpdateFailedEventArgs`: `Result` (`UpdateOperationResult`)
108+
`UpdateFailureReason`: `None`, `NetworkError`, `Canceled`, `InvalidMetadata`, `FileIoError`, `HashMismatch`, `ServerDoesNotSupportRange`, `InstallPermissionDenied`, `InstallLaunchFailed`, `VersionComparisonFailed`, `Unknown`
77109

78-
## Android FileProvider setup
110+
## Android FileProvider Setup
79111

80112
```xml
81113
<provider
82-
android:name="androidx.core.content.FileProvider"
83-
android:authorities="com.example.app.generalupdate.fileprovider"
84-
android:exported="false"
85-
android:grantUriPermissions="true">
86-
<meta-data
87-
android:name="android.support.FILE_PROVIDER_PATHS"
88-
android:resource="@xml/generalupdate_file_paths" />
114+
android:name="androidx.core.content.FileProvider"
115+
android:authorities="com.example.app.generalupdate.fileprovider"
116+
android:exported="false"
117+
android:grantUriPermissions="true">
118+
<meta-data
119+
android:name="android.support.FILE_PROVIDER_PATHS"
120+
android:resource="@xml/generalupdate_file_paths" />
89121
</provider>
90122
```
91123

@@ -94,7 +126,7 @@ if (check.UpdateFound)
94126
```xml
95127
<?xml version="1.0" encoding="utf-8"?>
96128
<paths>
97-
<cache-path name="update_cache" path="update/" />
98-
<files-path name="update_files" path="update/" />
129+
<cache-path name="update_cache" path="update/" />
130+
<files-path name="update_files" path="update/" />
99131
</paths>
100132
```

0 commit comments

Comments
 (0)