feat: auto-updater
This commit is contained in:
@@ -58,6 +58,8 @@ class AppUpdateUtil {
|
||||
|
||||
final response = await http.get(url);
|
||||
|
||||
debugPrint("Response from update server: ${response.body}");
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception("Update server unavailable");
|
||||
}
|
||||
@@ -71,96 +73,100 @@ class AppUpdateUtil {
|
||||
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;
|
||||
}
|
||||
// /// 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) {
|
||||
// debugPrint("APK installer opened successfully");
|
||||
// }
|
||||
//
|
||||
//
|
||||
// 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