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
3024using GeneralUpdate .Avalonia .Android ;
31- using GeneralUpdate .Avalonia .Android .Abstractions ;
3225using 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-
4227var 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
5236var 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
6145var check = await bootstrap .ValidateAsync (packageInfo , " 2.2.1" , CancellationToken .None );
6246if (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