add alot
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
// import 'package:open_filex/open_filex.dart';
|
||||
|
||||
// import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
|
||||
class UpdateInfo {
|
||||
final bool update;
|
||||
final String version;
|
||||
final String notes;
|
||||
final bool mandatory;
|
||||
final String sha256;
|
||||
final String download;
|
||||
|
||||
|
||||
UpdateInfo({
|
||||
required this.update,
|
||||
required this.version,
|
||||
required this.notes,
|
||||
required this.mandatory,
|
||||
required this.sha256,
|
||||
required this.download,
|
||||
});
|
||||
|
||||
|
||||
factory UpdateInfo.fromJson(Map<String, dynamic> json) {
|
||||
return UpdateInfo(
|
||||
update: json["update"] ?? false,
|
||||
version: json["version"] ?? "",
|
||||
notes: json["notes"] ?? "",
|
||||
mandatory: json["mandatory"] ?? false,
|
||||
sha256: json["sha256"] ?? "",
|
||||
download: json["download"] ?? "",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class AppUpdateUtil {
|
||||
|
||||
|
||||
final String serverUrl;
|
||||
|
||||
|
||||
AppUpdateUtil({
|
||||
required this.serverUrl,
|
||||
});
|
||||
|
||||
|
||||
/// Gets the installed app version
|
||||
Future<String> currentVersion() async {
|
||||
// final info =
|
||||
// await PackageInfo.fromPlatform();
|
||||
|
||||
return "test";
|
||||
}
|
||||
|
||||
|
||||
/// Checks the update server
|
||||
Future<UpdateInfo?> checkForUpdate() async {
|
||||
final version =
|
||||
await currentVersion();
|
||||
|
||||
|
||||
final url = Uri.parse(
|
||||
"$serverUrl/api/update?version=$version",
|
||||
);
|
||||
|
||||
|
||||
final response =
|
||||
await http.get(url);
|
||||
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception(
|
||||
"Update server unavailable",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
final data =
|
||||
jsonDecode(response.body);
|
||||
|
||||
|
||||
if (data["update"] != true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return UpdateInfo.fromJson(data);
|
||||
}
|
||||
|
||||
|
||||
/// Downloads the APK
|
||||
Future<File> downloadApk(UpdateInfo update, {
|
||||
|
||||
Function(double progress)? onProgress,
|
||||
|
||||
}) async {
|
||||
final url =
|
||||
update.download.startsWith("http")
|
||||
? update.download
|
||||
: "$serverUrl${update.download}";
|
||||
|
||||
|
||||
final request =
|
||||
http.Request(
|
||||
"GET",
|
||||
Uri.parse(url),
|
||||
);
|
||||
|
||||
|
||||
final response =
|
||||
await request.send();
|
||||
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception(
|
||||
"APK download failed",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
final total =
|
||||
response.contentLength ?? 0;
|
||||
|
||||
|
||||
int received = 0;
|
||||
|
||||
|
||||
final directory =
|
||||
await getTemporaryDirectory();
|
||||
|
||||
|
||||
final file =
|
||||
File(
|
||||
"${directory.path}/update.apk",
|
||||
);
|
||||
|
||||
|
||||
final sink =
|
||||
file.openWrite();
|
||||
|
||||
await for (final chunk in response.stream) {
|
||||
sink.add(chunk);
|
||||
|
||||
received += chunk.length;
|
||||
|
||||
if (total > 0 && onProgress != null) {
|
||||
onProgress(
|
||||
received / total,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await sink.close();
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
/// Verifies APK checksum
|
||||
Future<bool> verifySha256(File file,
|
||||
String expectedHash,) async {
|
||||
final bytes =
|
||||
await file.readAsBytes();
|
||||
|
||||
final digest =
|
||||
sha256.convert(bytes);
|
||||
|
||||
return digest.toString()
|
||||
.toLowerCase()
|
||||
==
|
||||
expectedHash.toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
/// Opens Android APK installer
|
||||
Future<void> installApk(File file,) async {
|
||||
// final result =
|
||||
// await OpenFilex.open(
|
||||
// file.path,
|
||||
// );
|
||||
//
|
||||
//
|
||||
// if (result.type != ResultType.done) {
|
||||
// throw Exception(
|
||||
// "Could not open APK installer",
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
/// Full update flow
|
||||
///
|
||||
/// Returns:
|
||||
/// - null if no update exists
|
||||
/// - UpdateInfo if update is available
|
||||
///
|
||||
Future<UpdateInfo?> update({
|
||||
|
||||
Function(double progress)? onProgress,
|
||||
|
||||
}) async {
|
||||
final info =
|
||||
await checkForUpdate();
|
||||
|
||||
if (info == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final apk =
|
||||
await downloadApk(
|
||||
info,
|
||||
onProgress: onProgress,
|
||||
);
|
||||
|
||||
final valid =
|
||||
await verifySha256(
|
||||
apk,
|
||||
info.sha256,
|
||||
);
|
||||
|
||||
if (!valid) {
|
||||
throw Exception(
|
||||
"APK checksum mismatch",
|
||||
);
|
||||
}
|
||||
|
||||
await installApk(
|
||||
apk,
|
||||
);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user