import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:manager_api_new/api.dart'; //import 'package:ota_update/ota_update.dart'; //import 'package:package_info/package_info.dart'; //import 'package:package_info_plus/package_info_plus.dart'; import 'package:provider/provider.dart'; import 'package:tablet_app/Models/tabletContext.dart'; import 'package:tablet_app/app_context.dart'; import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:tablet_app/constants.dart'; class DownloadConfigurationWidget extends StatefulWidget { DownloadConfigurationWidget(); @override State createState() => _DownloadConfigurationWidgetState(); } class _DownloadConfigurationWidgetState extends State { ValueNotifier currentResourceIndex = ValueNotifier(0); ValueNotifier currentResourceNbr = ValueNotifier(-1); bool isAlreadyDownloading = false; //OtaEvent? currentEvent; /*Future tryOtaUpdate() async { try { print('ABI Platform: ${await OtaUpdate().getAbi()}'); final info = await PackageInfo.fromPlatform(); var test = info.version; var test0 = info.appName; var test1 = info.buildNumber; var test4 = info.packageName; var versionDispo = 13; var versionDispoURL = 'https://drive.usercontent.google.com/download?id=1rO8eZBvmDcmARTOtUeGfeuN1buvafg6Z&export=download&authuser=0&confirm=t&uuid=8f923b32-bf10-4f98-abd6-7995cdc2af5e&at=APZUnTUSU7f9pLr8nN4hgW7wvEv6%3A1713883987919'; if(int.parse(test1) <= versionDispo) { OtaUpdate() .execute( versionDispoURL, destinationFilename: 'app-release_version_2_0_7.apk', //FOR NOW ANDROID ONLY - ABILITY TO VALIDATE CHECKSUM OF FILE: //sha256checksum: 'd6da28451a1e15cf7a75f2c3f151befad3b80ad0bb232ab15c20897e54f21478', ) .listen( (OtaEvent event) { print("OtaEvent"); print(event); //setState(() => currentEvent = event); }, ); } //LINK CONTAINS APK OF FLUTTER HELLO WORLD FROM FLUTTER SDK EXAMPLES /**/ // ignore: avoid_catches_without_on_clauses } catch (e) { print('Failed to make OTA update. Details: $e'); } }*/ Future download(BuildContext buildContext, TabletAppContext tabletAppContext) async { bool isAllLanguages = true; if(!isAlreadyDownloading) { isAlreadyDownloading = true; // HERE CHECK VERSION APK if(true) { Map statuses = await [ Permission.requestInstallPackages, ].request(); //tryOtaUpdate(); } ExportConfigurationDTO? exportConfigurationDTO; try{ // Retrieve all url from resource to download (get all resource from configuration en somme) exportConfigurationDTO = await tabletAppContext.clientAPI!.configurationApi!.configurationExport(tabletAppContext.configuration!.id!); // tabletAppContext.configuration!.id! // 65c5f0ee4c030e63ce16bff5 TODO Remove } catch(e) { print("Erreur lors du téléchargement de la configuration et de ses ressources !"); print(e); return false; } exportConfigurationDTO.resources!.forEach((element) { print(element.id); print(element.label); }); if(exportConfigurationDTO.resources != null && exportConfigurationDTO.resources!.isNotEmpty) { Map statuses = await [ Permission.storage, ].request(); //if(statuses[Permission.storage] == PermissionStatus.granted) { try{ try { Directory directory = Directory('${tabletAppContext.localPath}'); List allConfigurations = directory.listSync(); Directory configurationDirectory = Directory('${tabletAppContext.localPath}/${tabletAppContext.configuration!.id}'); if(!allConfigurations.any((configurationDirectory) => configurationDirectory.uri.pathSegments.any((element) => element == tabletAppContext.configuration!.id))) { // create directory print("Trying to create directory"); configurationDirectory.createSync(recursive: true); print('Répertoire créé avec succès.'); } } catch(e) { print("Listing failed, so try to create directory"); Directory configurationDirectory = Directory('${tabletAppContext.localPath}/${tabletAppContext.configuration!.id}'); configurationDirectory.createSync(recursive: true); print('Répertoire créé avec succès.'); } Directory configurationDirectory = Directory('${tabletAppContext.localPath}/${tabletAppContext.configuration!.id}'); List fileList = configurationDirectory.listSync(); for (var file in fileList) { print(file.uri.pathSegments.last); } var resourcesToDownload = exportConfigurationDTO.resources!.where((resource) => resource.type != ResourceType.ImageUrl && resource.type != ResourceType.VideoUrl && resource.type != ResourceType.JsonUrl && resource.url != null && !fileList.any((fileL) => fileL.uri.pathSegments.last.contains(resource.id!))); currentResourceNbr.value = resourcesToDownload.length; // foreach ou on va tout télécharger - avec un joli etape 0 / length - on peut rendre tout lent on s'en fou ça ne ce fait qu'une fois for (var resource in resourcesToDownload) { bool success = await downloadResource(tabletAppContext, resource, tabletAppContext.localPath!); if (success) { currentResourceIndex.value++; } else { print("NOT SUCCESSS"); } } // Delete others that are no more used var resourceToDelete = fileList.where((fileL) => !exportConfigurationDTO!.resources!.any((resource) => resource.id != null && fileL.uri.pathSegments.last.contains(resource.id!))); for (var resource in resourceToDelete) { print("resource to DELETE"); print(resource.path); // resource.deleteSync(); // Preserve call to firebase // TODO uncomment if needed } } catch(e) { print("ERRORRRR"); print(e); if(statuses[Permission.storage] != PermissionStatus.granted) { Fluttertoast.showToast( msg: "PermissionStatus not granted, issue may be linked to that. Please check os version (if less than 13, real issue).", toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, timeInSecForIosWeb: 1, backgroundColor: Colors.redAccent, textColor: Colors.white, fontSize: 16.0 ); } return false; } /*} else { print("PermissionStatus.granted NOT GRANTED"); Fluttertoast.showToast( msg: "PermissionStatus not granted", toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, timeInSecForIosWeb: 1, backgroundColor: Colors.redAccent, textColor: Colors.white, fontSize: 16.0 ); return false; }*/ } } return true; } @override Widget build(BuildContext context) { final appContext = Provider.of(context); TabletAppContext tabletAppContext = appContext.getContext(); Size size = MediaQuery.of(context).size; return Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Spacer(), ValueListenableBuilder( valueListenable: currentResourceNbr, builder: (context, valueNbr, _) { return ValueListenableBuilder( valueListenable: currentResourceIndex, builder: (context, valueIndex, _) { return valueNbr != -1 && valueNbr != 0 ? Center( child: Padding( padding: const EdgeInsets.only(bottom: 8.0), child: Text( "${valueIndex.toString()}/${valueNbr.toString()}", style: TextStyle(fontSize: 45, fontWeight: FontWeight.w500), ), ), ) : SizedBox(height: 0,width: 0); } ); } ), FutureBuilder(future: download(context, tabletAppContext), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { // Loader ou indicateur de chargement pendant la vérification Color primaryColor = tabletAppContext.configuration!.primaryColor != null ? new Color(int.parse(tabletAppContext.configuration!.primaryColor!.split('(0x')[1].split(')')[0], radix: 16)) : kTestSecondColor; return Center(child: CircularProgressIndicator(color: primaryColor)); } else { return ValueListenableBuilder( valueListenable: currentResourceNbr, builder: (context, valueNbr, _) { return ValueListenableBuilder( valueListenable: currentResourceIndex, builder: (context, valueIndex, _) { return Center( child: Text( valueIndex == valueNbr && valueNbr != -1 ? valueNbr == 0 ? "Tout est à jour" : "Mise à jour finie" : "Mise à jour en cours", style: TextStyle(fontSize: 25), ), ); } ); } ); } }), Spacer() ], ); } } Future downloadResource(TabletAppContext tabletAppContext, ResourceDTO resourceDTO, String localPath) async { try { // Téléchargement de la ressource depuis l'URL http.Response response = await http.get(Uri.parse(resourceDTO.url!)); if (response.statusCode == 200) { // Vérification de l'en-tête Content-Type String contentType = response.headers["content-type"] ?? ""; // Déduction de l'extension en fonction du Content-Type String extension = _getExtensionFromContentType(contentType); print("LOCAL PATTH"); print(localPath); File file = File('$localPath/${tabletAppContext.configuration!.id}/${resourceDTO.id}.$extension'); // Écriture du contenu téléchargé dans le fichier local await file.writeAsBytes(response.bodyBytes); return true; } else { print("Échec du téléchargement de la ressource - ${response.statusCode}"); return false; } } catch (e) { print("Erreur lors du téléchargement de la ressource !"); print(e); return false; } } String _getExtensionFromContentType(String contentType) { Map contentTypeToExtension = { "image/jpeg": "jpg", "image/jpg": "jpg", "image/png": "png", "image/gif": "gif", "audio/mp3": "mp3", "video/mp4": "mp4", "video/webm": "webm", "video/avi": "avi", "video/quicktime": "mov", "application/pdf": "pdf", "application/json": "json" }; return contentTypeToExtension[contentType] ?? "unknown"; }