fix: deps and auto-update check

This commit is contained in:
2026-07-27 22:15:22 +02:00
parent 77c1747b40
commit 1d687235f4
5 changed files with 107 additions and 158 deletions
+2 -1
View File
@@ -21,12 +21,13 @@ class _KoolTabAppState extends State<KoolTabApp> {
} }
Future<void> _checkForUpdates() async { Future<void> _checkForUpdates() async {
final updater = AppUpdateUtil(serverUrl: "http://localhost:3000/"); final updater = AppUpdateUtil(serverUrl: "http://localhost:3000");
try { try {
final update = await updater.checkForUpdate(); final update = await updater.checkForUpdate();
if (update == null) { if (update == null) {
debugPrint("No update available: ");
return; return;
} }
+35 -111
View File
@@ -2,13 +2,13 @@ import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:crypto/crypto.dart'; import 'package:crypto/crypto.dart';
import 'package:flutter/cupertino.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
// import 'package:open_filex/open_filex.dart'; import 'package:open_filex/open_filex.dart';
// import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
class UpdateInfo { class UpdateInfo {
final bool update; final bool update;
final String version; final String version;
@@ -17,7 +17,6 @@ class UpdateInfo {
final String sha256; final String sha256;
final String download; final String download;
UpdateInfo({ UpdateInfo({
required this.update, required this.update,
required this.version, required this.version,
@@ -27,7 +26,6 @@ class UpdateInfo {
required this.download, required this.download,
}); });
factory UpdateInfo.fromJson(Map<String, dynamic> json) { factory UpdateInfo.fromJson(Map<String, dynamic> json) {
return UpdateInfo( return UpdateInfo(
update: json["update"] ?? false, update: json["update"] ?? false,
@@ -40,111 +38,66 @@ class UpdateInfo {
} }
} }
class AppUpdateUtil { class AppUpdateUtil {
final String serverUrl; final String serverUrl;
AppUpdateUtil({required this.serverUrl});
AppUpdateUtil({
required this.serverUrl,
});
/// Gets the installed app version /// Gets the installed app version
Future<String> currentVersion() async { Future<String> currentVersion() async {
// final info = final info = await PackageInfo.fromPlatform();
// await PackageInfo.fromPlatform(); debugPrint("Current app version: ${info.version}");
return info.version;
return "test";
} }
/// Checks the update server /// Checks the update server
Future<UpdateInfo?> checkForUpdate() async { Future<UpdateInfo?> checkForUpdate() async {
final version = final version = await currentVersion();
await currentVersion();
final url = Uri.parse("$serverUrl/api/update?version=$version");
final url = Uri.parse( final response = await http.get(url);
"$serverUrl/api/update?version=$version",
);
final response =
await http.get(url);
if (response.statusCode != 200) { if (response.statusCode != 200) {
throw Exception( throw Exception("Update server unavailable");
"Update server unavailable",
);
} }
final data = jsonDecode(response.body);
final data =
jsonDecode(response.body);
if (data["update"] != true) { if (data["update"] != true) {
return null; return null;
} }
return UpdateInfo.fromJson(data); return UpdateInfo.fromJson(data);
} }
/// Downloads the APK /// Downloads the APK
Future<File> downloadApk(UpdateInfo update, { Future<File> downloadApk(
UpdateInfo update, {
Function(double progress)? onProgress, Function(double progress)? onProgress,
}) async { }) async {
final url = final url = update.download.startsWith("http")
update.download.startsWith("http")
? update.download ? update.download
: "$serverUrl${update.download}"; : "$serverUrl${update.download}";
final request = http.Request("GET", Uri.parse(url));
final request = final response = await request.send();
http.Request(
"GET",
Uri.parse(url),
);
final response =
await request.send();
if (response.statusCode != 200) { if (response.statusCode != 200) {
throw Exception( throw Exception("APK download failed");
"APK download failed",
);
} }
final total = response.contentLength ?? 0;
final total =
response.contentLength ?? 0;
int received = 0; int received = 0;
final directory = await getTemporaryDirectory();
final directory = final file = File("${directory.path}/update.apk");
await getTemporaryDirectory();
final sink = file.openWrite();
final file =
File(
"${directory.path}/update.apk",
);
final sink =
file.openWrite();
await for (final chunk in response.stream) { await for (final chunk in response.stream) {
sink.add(chunk); sink.add(chunk);
@@ -152,9 +105,7 @@ class AppUpdateUtil {
received += chunk.length; received += chunk.length;
if (total > 0 && onProgress != null) { if (total > 0 && onProgress != null) {
onProgress( onProgress(received / total);
received / total,
);
} }
} }
@@ -163,25 +114,17 @@ class AppUpdateUtil {
return file; return file;
} }
/// Verifies APK checksum /// Verifies APK checksum
Future<bool> verifySha256(File file, Future<bool> verifySha256(File file, String expectedHash) async {
String expectedHash,) async { final bytes = await file.readAsBytes();
final bytes =
await file.readAsBytes();
final digest = final digest = sha256.convert(bytes);
sha256.convert(bytes);
return digest.toString() return digest.toString().toLowerCase() == expectedHash.toLowerCase();
.toLowerCase()
==
expectedHash.toLowerCase();
} }
/// Opens Android APK installer /// Opens Android APK installer
Future<void> installApk(File file,) async { Future<void> installApk(File file) async {
// final result = // final result =
// await OpenFilex.open( // await OpenFilex.open(
// file.path, // file.path,
@@ -195,48 +138,29 @@ class AppUpdateUtil {
// } // }
} }
/// Full update flow /// Full update flow
/// ///
/// Returns: /// Returns:
/// - null if no update exists /// - null if no update exists
/// - UpdateInfo if update is available /// - UpdateInfo if update is available
/// ///
Future<UpdateInfo?> update({ Future<UpdateInfo?> update({Function(double progress)? onProgress}) async {
final info = await checkForUpdate();
Function(double progress)? onProgress,
}) async {
final info =
await checkForUpdate();
if (info == null) { if (info == null) {
return null; return null;
} }
final apk = final apk = await downloadApk(info, onProgress: onProgress);
await downloadApk(
info,
onProgress: onProgress,
);
final valid = final valid = await verifySha256(apk, info.sha256);
await verifySha256(
apk,
info.sha256,
);
if (!valid) { if (!valid) {
throw Exception( throw Exception("APK checksum mismatch");
"APK checksum mismatch",
);
} }
await installApk( await installApk(apk);
apk,
);
return info; return info;
} }
}
}
+13 -13
View File
@@ -57,19 +57,19 @@ class ProductListViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
try { // try {
_products = await productService.getProducts(); // _products = await productService.getProducts();
//
debugPrint( // debugPrint(
_products.map((p) => '${p.name}: ${p.stockQuantity}').join('\n'), // _products.map((p) => '${p.name}: ${p.stockQuantity}').join('\n'),
); // );
} catch (_) { // } catch (_) {
_errorMessage = 'Could not load products.'; // _errorMessage = 'Could not load products.';
} finally { // } finally {
_hasLoaded = true; // _hasLoaded = true;
_isLoading = false; // _isLoading = false;
notifyListeners(); // notifyListeners();
} // }
} }
Future<void> addProduct({ Future<void> addProduct({
+53 -29
View File
@@ -225,6 +225,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.2.0"
ffi_leak_tracker:
dependency: transitive
description:
name: ffi_leak_tracker
sha256: "4093d4ef9ca06ffe2786e73bfb25e22aa92112b9bb4ec941f11e3e6b61489a97"
url: "https://pub.dev"
source: hosted
version: "0.1.2"
file: file:
dependency: transitive dependency: transitive
description: description:
@@ -298,50 +306,50 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_secure_storage name: flutter_secure_storage
sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" sha256: "7686b1d6a29985dcbb808c59518226e603e3bfa7c0ddfd1a0d00e4cda77c868e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "9.2.4" version: "10.3.1"
flutter_secure_storage_darwin:
dependency: transitive
description:
name: flutter_secure_storage_darwin
sha256: "82329fa5cdf343773b1b6897dea959105a29f092454259edff92f9f6637e8149"
url: "https://pub.dev"
source: hosted
version: "0.3.2"
flutter_secure_storage_linux: flutter_secure_storage_linux:
dependency: transitive dependency: transitive
description: description:
name: flutter_secure_storage_linux name: flutter_secure_storage_linux
sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 sha256: a5f35ddab43cf5c8215d2feb4ce1957851f28c5c37e6f04335066a0602087bf5
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.3" version: "3.0.1"
flutter_secure_storage_macos:
dependency: transitive
description:
name: flutter_secure_storage_macos
sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247"
url: "https://pub.dev"
source: hosted
version: "3.1.3"
flutter_secure_storage_platform_interface: flutter_secure_storage_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: flutter_secure_storage_platform_interface name: flutter_secure_storage_platform_interface
sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 sha256: "06686df417f34fe9f963ed217b7fdce250e2f637ddd6c374d7eb054549770633"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.2" version: "2.0.2"
flutter_secure_storage_web: flutter_secure_storage_web:
dependency: transitive dependency: transitive
description: description:
name: flutter_secure_storage_web name: flutter_secure_storage_web
sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 sha256: "073a62b3aeb866ab4ce795f960413948e51e5a42a9b0c8333b6daf5bb3208a1c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.1" version: "2.1.1"
flutter_secure_storage_windows: flutter_secure_storage_windows:
dependency: transitive dependency: transitive
description: description:
name: flutter_secure_storage_windows name: flutter_secure_storage_windows
sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 sha256: "471951813a97006d899db4948acc654a4f28c440083ea08178935ce20b173ec1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.2" version: "4.2.2"
flutter_slidable: flutter_slidable:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -393,7 +401,7 @@ packages:
source: hosted source: hosted
version: "2.0.2" version: "2.0.2"
http: http:
dependency: transitive dependency: "direct main"
description: description:
name: http name: http
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
@@ -512,14 +520,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.1" version: "1.0.1"
js:
dependency: transitive
description:
name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
source: hosted
version: "0.6.7"
json_annotation: json_annotation:
dependency: transitive dependency: transitive
description: description:
@@ -624,6 +624,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "9.4.1" version: "9.4.1"
open_filex:
dependency: "direct main"
description:
name: open_filex
sha256: "9976da61b6a72302cf3b1efbce259200cd40232643a467aac7370addf94d6900"
url: "https://pub.dev"
source: hosted
version: "4.7.0"
package_config: package_config:
dependency: transitive dependency: transitive
description: description:
@@ -632,6 +640,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.2.0"
package_info_plus:
dependency: "direct main"
description:
name: package_info_plus
sha256: "127e1751e37ffb2ff4658beeaca77bad0c27bf5f932bd3a501c2296926d4b481"
url: "https://pub.dev"
source: hosted
version: "10.2.1"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
sha256: db762cb2f4f25ee60fb6359773861b0f199e00b90d237bd85a76a1e806b46ef4
url: "https://pub.dev"
source: hosted
version: "4.1.0"
path: path:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -937,10 +961,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e sha256: ba6f4bba816c8d7e3c1580e170f3786d216951cc6b94babc3b814c08d2cb2738
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.15.0" version: "6.3.0"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
+4 -4
View File
@@ -44,11 +44,11 @@ dependencies:
path: ^1.9.1 path: ^1.9.1
flutter_slidable: ^4.0.3 flutter_slidable: ^4.0.3
intl: ^0.20.3 intl: ^0.20.3
flutter_secure_storage: ^9.0.0 flutter_secure_storage: 10.3.1
crypto: ^3.0.0 crypto: ^3.0.0
# http: ^1.6.0 http: ^1.6.0
# package_info_plus: ^10.2.1 package_info_plus: ^10.1.0
# open_filex: ^4.7.0 open_filex: ^4.7.0
# permission_handler: ^12.0.3 # permission_handler: ^12.0.3