import 'dart:async'; import 'dart:io'; import 'dart:typed_data'; import 'package:filcnaplo/api/client.dart'; import 'package:filcnaplo/helpers/storage_helper.dart'; import 'package:filcnaplo/models/release.dart'; import 'package:open_file/open_file.dart'; import 'package:permission_handler/permission_handler.dart'; enum UpdateState { none, preparing, downloading, installing } typedef UpdateCallback = Function(double progress, UpdateState state); // ignore: todo // TODO: cleanup old apk files extension UpdateHelper on Release { Future install({UpdateCallback? updateCallback}) async { updateCallback!(-1, UpdateState.preparing); String downloads = await StorageHelper.downloadsPath(); File apk = File("$downloads/refilc-v$version.apk"); if (!await apk.exists()) { updateCallback(-1, UpdateState.downloading); var storagePerms = await Permission.storage.request().isGranted; if (storagePerms) { var bytes = await download(updateCallback: updateCallback); if (!await StorageHelper.write(apk.path, bytes)) { throw "failed to write apk: permission denied"; } } else { throw storagePerms; } } updateCallback(-1, UpdateState.installing); var installPerms = (await Permission.manageExternalStorage.request().isGranted && await Permission.requestInstallPackages.request().isGranted); if (installPerms) { var result = await OpenFile.open(apk.path); if (result.type != ResultType.done) { // ignore: avoid_print print("ERROR: installUpdate.openFile: ${result.message}"); throw result.message; } } updateCallback(-1, UpdateState.none); } Future download({UpdateCallback? updateCallback}) async { var response = await FilcAPI.downloadRelease(downloads.first); List> chunks = []; int downloaded = 0; var completer = Completer(); response?.stream.listen((List chunk) { updateCallback!( downloaded / (response.contentLength ?? 0), UpdateState.downloading); chunks.add(chunk); downloaded += chunk.length; }, onDone: () { // Save the file final Uint8List bytes = Uint8List(response.contentLength ?? 0); int offset = 0; for (List chunk in chunks) { bytes.setRange(offset, offset + chunk.length, chunk); offset += chunk.length; } completer.complete(bytes); }); return completer.future; } }