diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 7469f95..6f09ca3 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,8 @@ + + + { AudioPlayer player = AudioPlayer(); - late Uint8List audiobytes; + Uint8List? audiobytes = null; bool isplaying = false; bool audioplayed = false; int currentpos = 0; @@ -37,6 +39,10 @@ class _AudioPlayerFloatingContainerState extends State fileToUint8List(File file) async { + List bytes = await file.readAsBytes(); + return Uint8List.fromList(bytes); + } + @override Widget build(BuildContext context) { final appContext = Provider.of(context); diff --git a/lib/Components/cached_custom_resource.dart b/lib/Components/cached_custom_resource.dart new file mode 100644 index 0000000..3bbff05 --- /dev/null +++ b/lib/Components/cached_custom_resource.dart @@ -0,0 +1,122 @@ +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:manager_api/api.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:provider/provider.dart'; +import 'package:tablet_app/Components/audio_player.dart'; +import 'package:tablet_app/Components/video_viewer.dart'; +import 'package:tablet_app/Components/video_viewer_youtube.dart'; +import 'package:tablet_app/Models/tabletContext.dart'; +import 'package:tablet_app/app_context.dart'; + +class CachedCustomResource extends StatelessWidget { + final ResourceDTO resourceDTO; + final bool isAuto; + final bool webView; + final BoxFit fit; + + CachedCustomResource({ + required this.resourceDTO, + required this.isAuto, + required this.webView, + this.fit = BoxFit.cover, + }); + + @override + Widget build(BuildContext context) { + final appContext = Provider.of(context); + TabletAppContext tabletAppContext = appContext.getContext(); + Size size = MediaQuery.of(context).size; + + Color primaryColor = new Color(int.parse(tabletAppContext.configuration!.primaryColor!.split('(0x')[1].split(')')[0], radix: 16)); + + if(resourceDTO.type == ResourceType.ImageUrl || resourceDTO.type == ResourceType.VideoUrl) + { + // Image Url or Video Url don't care, just get resource + if(resourceDTO.type == ResourceType.ImageUrl) { + return CachedNetworkImage( + imageUrl: resourceDTO.url!, + fit: BoxFit.fill, + progressIndicatorBuilder: (context, url, downloadProgress) => + CircularProgressIndicator(value: downloadProgress.progress, color: primaryColor), + errorWidget: (context, url, error) => Icon(Icons.error), + ); + } else { + if(resourceDTO.url == null) { + return Center(child: Text("Error loading video")); + } else { + return VideoViewerYoutube(videoUrl: resourceDTO.url!, isAuto: isAuto, webView: webView); + } + } + } else { + // Check if exist on local storage, if no, just show it via url + print("Check local storage in cached custom resource"); + return FutureBuilder( + future: _checkIfLocalResourceExists(tabletAppContext), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + // Loader ou indicateur de chargement pendant la vérification + return CircularProgressIndicator(); + } else if (snapshot.hasError || snapshot.data == null) { + // Si la ressource locale n'existe pas ou s'il y a une erreur + + switch(resourceDTO.type) { + case ResourceType.Image : + return CachedNetworkImage( + imageUrl: resourceDTO.url!, + fit: fit, + placeholder: (context, url) => CircularProgressIndicator(), + errorWidget: (context, url, error) => Icon(Icons.error), + ); + case ResourceType.Video : + return VideoViewer(file: null, videoUrl: resourceDTO.url!); + case ResourceType.Audio : + return AudioPlayerFloatingContainer(file: null, audioBytes: null, resourceURl: resourceDTO.url!, isAuto: isAuto); + default: + return Text("Not supported type"); + } + } else { + + switch(resourceDTO.type) { + case ResourceType.Image : + return Image.file( + snapshot.data!, + fit: fit, + ); + case ResourceType.Video : + return VideoViewer(file: snapshot.data!, videoUrl: resourceDTO.url!); + case ResourceType.Audio : + return AudioPlayerFloatingContainer(file: snapshot.data!, audioBytes: null, resourceURl: resourceDTO.url!, isAuto: isAuto); + default: + return Text("Not supported type"); + } + // Utilisation de l'image locale + + } + }, + ); + } + } + + Future _checkIfLocalResourceExists(TabletAppContext tabletAppContext) async { + Directory? appDocumentsDirectory = await getDownloadsDirectory(); + String localPath = appDocumentsDirectory!.path; + Directory configurationDirectory = Directory('$localPath/${tabletAppContext.configuration!.id}'); + List fileList = configurationDirectory.listSync(); + + if(fileList.any((fileL) => fileL.uri.pathSegments.last.contains(resourceDTO.id!))) { + File file = File(fileList.firstWhere((fileL) => fileL.uri.pathSegments.last.contains(resourceDTO.id!)).path); + return file; + } + + return null; + } + + Future get localPath async { + Directory? appDocumentsDirectory = await getDownloadsDirectory(); + return appDocumentsDirectory!.path; + } +} diff --git a/lib/Components/show_element_for_resource.dart b/lib/Components/show_element_for_resource.dart index b9a146e..da2d1e6 100644 --- a/lib/Components/show_element_for_resource.dart +++ b/lib/Components/show_element_for_resource.dart @@ -11,10 +11,14 @@ import 'package:tablet_app/Components/video_viewer_youtube.dart'; import 'package:tablet_app/Models/tabletContext.dart'; import 'package:tablet_app/app_context.dart'; +import 'cached_custom_resource.dart'; + showElementForResource(ResourceDTO resourceDTO, AppContext appContext, bool isAuto, bool webView) { TabletAppContext tabletAppContext = appContext.getContext(); Color primaryColor = new Color(int.parse(tabletAppContext.configuration!.primaryColor!.split('(0x')[1].split(')')[0], radix: 16)); + return CachedCustomResource(resourceDTO: resourceDTO, isAuto: isAuto, webView: webView); + switch(resourceDTO.type) { case ResourceType.Image: case ResourceType.ImageUrl: @@ -45,7 +49,7 @@ showElementForResource(ResourceDTO resourceDTO, AppContext appContext, bool isAu }, );*/ case ResourceType.Audio: - return AudioPlayerFloatingContainer(audioBytes: null, resourceURl: resourceDTO.url!, isAuto: isAuto); + return AudioPlayerFloatingContainer(file: null, audioBytes: null, resourceURl: resourceDTO.url!, isAuto: isAuto); /*return FutureBuilder( future: getAudio(resourceDTO.url, appContext), builder: (context, AsyncSnapshot snapshot) { @@ -76,7 +80,7 @@ showElementForResource(ResourceDTO resourceDTO, AppContext appContext, bool isAu if(resourceDTO.url == null) { return Center(child: Text("Error loading video")); } else { - return VideoViewer(videoUrl: resourceDTO.url!); + return VideoViewer(file: null, videoUrl: resourceDTO.url!); } case ResourceType.VideoUrl: if(resourceDTO.url == null) { diff --git a/lib/Components/video_viewer.dart b/lib/Components/video_viewer.dart index 9aeab67..3992013 100644 --- a/lib/Components/video_viewer.dart +++ b/lib/Components/video_viewer.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:cached_video_player/cached_video_player.dart'; import 'package:flutter/material.dart'; import 'package:tablet_app/Components/loading_common.dart'; @@ -6,7 +8,8 @@ import '../../constants.dart'; class VideoViewer extends StatefulWidget { final String videoUrl; - VideoViewer({required this.videoUrl}); + final File? file; + VideoViewer({required this.videoUrl, required this.file}); @override _VideoViewer createState() => _VideoViewer(); @@ -18,11 +21,19 @@ class _VideoViewer extends State { @override void initState() { super.initState(); - _controller = CachedVideoPlayerController.network(widget.videoUrl) // Uri.parse() - ..initialize().then((_) { - // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed. - setState(() {}); - }); + if(widget.file != null) { + _controller = CachedVideoPlayerController.file(widget.file!) // Uri.parse() + ..initialize().then((_) { + // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed. + setState(() {}); + }); + } else { + _controller = CachedVideoPlayerController.network(widget.videoUrl) // Uri.parse() + ..initialize().then((_) { + // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed. + setState(() {}); + }); + } } @override diff --git a/lib/Models/tabletContext.dart b/lib/Models/tabletContext.dart index a739c5b..3cc641c 100644 --- a/lib/Models/tabletContext.dart +++ b/lib/Models/tabletContext.dart @@ -17,6 +17,7 @@ class TabletAppContext with ChangeNotifier{ String? deviceId; String? instanceId; Size? puzzleSize; + String? localPath; TabletAppContext({this.id, this.deviceId, this.host, this.configuration, this.language, this.instanceId, this.clientAPI}); diff --git a/lib/Screens/MainView/main_view.dart b/lib/Screens/MainView/main_view.dart index d4fe97c..16b2a0b 100644 --- a/lib/Screens/MainView/main_view.dart +++ b/lib/Screens/MainView/main_view.dart @@ -26,6 +26,7 @@ import 'package:tablet_app/Screens/Puzzle/puzzle_view.dart'; import 'package:tablet_app/Screens/Slider/slider_view.dart'; import 'package:tablet_app/Screens/Video/video_view.dart'; import 'package:tablet_app/Screens/Web/web_view.dart'; +import 'package:tablet_app/Services/downloadService.dart'; import 'package:tablet_app/app_context.dart'; import 'package:tablet_app/constants.dart'; import 'package:intl/intl.dart'; @@ -418,7 +419,32 @@ class _MainViewWidget extends State { ), ), ]), - ) + ), + floatingActionButton: InkWell( + onTap: () async { + var result = await showDialog( + builder: (BuildContext dialogContext) => AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20.0)) + ), + content: Container( + width: size.width *0.5, + height: size.height *0.5, + child: DownloadConfigurationWidget(), + ), + actions: [], + ), context: context + ); + + print("RESULLLLT"); + print(result); + }, + child: Container( + width: 150, + height: 150, + color: Colors.red, + ), + ), ); } } diff --git a/lib/Screens/Slider/slider_view.dart b/lib/Screens/Slider/slider_view.dart index bbe9ecf..29bdab1 100644 --- a/lib/Screens/Slider/slider_view.dart +++ b/lib/Screens/Slider/slider_view.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/foundation.dart'; @@ -6,9 +7,11 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; import 'package:manager_api/api.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; import 'package:tablet_app/Components/show_element_for_resource.dart'; import 'package:tablet_app/Components/video_viewer.dart'; +import 'package:tablet_app/Models/tabletContext.dart'; import 'package:tablet_app/app_context.dart'; import 'package:tablet_app/constants.dart'; import 'package:photo_view/photo_view.dart'; @@ -251,6 +254,29 @@ class _SliderView extends State { switch(i.resourceType) { case ResourceType.Image: + TabletAppContext tabletAppContext = appContext.getContext(); + Directory configurationDirectory = Directory('${tabletAppContext.localPath}/${tabletAppContext.configuration!.id}'); + List fileList = configurationDirectory.listSync(); + + var imageProvider = null; + if(fileList.any((fileL) => fileL.uri.pathSegments.last.contains(i.resourceId!))) { + File file = File(fileList.firstWhere((fileL) => fileL.uri.pathSegments.last.contains(i.resourceId!)).path); + imageProvider = FileImage(file); + } else { + imageProvider = new CachedNetworkImageProvider(i.resourceUrl!,); + } + + widgetToInclude = PhotoView( + imageProvider: imageProvider, + minScale: PhotoViewComputedScale.contained * 0.8, + maxScale: PhotoViewComputedScale.contained * 3.0, + backgroundDecoration: BoxDecoration( + color: kBackgroundSecondGrey, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(15.0), + ), + ); + break; case ResourceType.ImageUrl: widgetToInclude = PhotoView( imageProvider: CachedNetworkImageProvider(i.resourceUrl!),/*new NetworkImage( diff --git a/lib/Services/downloadService.dart b/lib/Services/downloadService.dart new file mode 100644 index 0000000..ebaf23b --- /dev/null +++ b/lib/Services/downloadService.dart @@ -0,0 +1,219 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:manager_api/api.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'; + +class DownloadConfigurationWidget extends StatefulWidget { + DownloadConfigurationWidget(); + + @override + State createState() => _DownloadConfigurationWidgetState(); +} + +class _DownloadConfigurationWidgetState extends State { + int? nbrResources; + ValueNotifier currentResourceIndex = ValueNotifier(0); + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + Future download(BuildContext buildContext, TabletAppContext tabletAppContext) async { + bool isAllLanguages = true; + + ExportConfigurationDTO? exportConfigurationDTO; + try{ + print(tabletAppContext.configuration!.id!); + print("essai"); + + // Retrieve all url from resource to download (get all resource from configuration en somme) + exportConfigurationDTO = await tabletAppContext.clientAPI!.configurationApi!.configurationExport(tabletAppContext.configuration!.id!); + } catch(e) { + print("Erreur lors du téléchargement de la configuration et de ses ressources !"); + print(e); + return false; + } + + print("COUCUO heeere"); + print(exportConfigurationDTO); + print(exportConfigurationDTO.resources!.length); + + exportConfigurationDTO.resources!.forEach((element) { + print(element.id); + print(element.label); + }); + + + if(exportConfigurationDTO != null && exportConfigurationDTO.resources != null && exportConfigurationDTO.resources!.isNotEmpty) { + nbrResources = exportConfigurationDTO.resources!.where((resource) => resource.type != ResourceType.ImageUrl && resource.type != ResourceType.VideoUrl && resource.url != null).length; + + print("i'm here"); + print(nbrResources); + Directory? appDocumentsDirectory = await getDownloadsDirectory(); + String localPath = appDocumentsDirectory!.path; + + print(localPath); + + Map statuses = await [ + Permission.storage, + ].request(); + + print(statuses[Permission.storage]); + print(statuses); + + if(statuses[Permission.storage] == PermissionStatus.granted) { + + try{ + Directory directory = Directory('$localPath'); + List allConfigurations = directory.listSync(); + + for (var file in allConfigurations) { + print('file.uri.pathSegments'); + print(file.uri.pathSegments); + print('file.uri.pathSegments.last'); + print(file.uri.pathSegments.last); + } + + Directory configurationDirectory = Directory('$localPath/${tabletAppContext.configuration!.id}'); + + if(!allConfigurations.any((configurationDirectory) => configurationDirectory.uri.pathSegments.any((element) => element == tabletAppContext.configuration!.id))) { + // create directory + configurationDirectory.createSync(recursive: true); + print('Répertoire créé avec succès.'); + } else { + print('EXISTE D2J0 NIGAUD.'); + } + + List fileList = configurationDirectory.listSync(); + + print("HERE LIST in directory"); + + for (var file in fileList) { + print(file.uri.pathSegments.last); + } + + // 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 + exportConfigurationDTO.resources!.forEach((resource) async { + + if(fileList.any((fileL) => fileL.uri.pathSegments.last.contains(resource.id!))) { + print("Already exist TRIPLE NIGAUD!"); + currentResourceIndex.value++; + } else { + if(resource.type != ResourceType.ImageUrl && resource.type != ResourceType.VideoUrl && resource.url != null) { + bool success = await downloadResource(tabletAppContext, resource, localPath); + + if (success) { + currentResourceIndex.value++; + } + } + } + }); + } catch(e) { + print("ERRORRRR"); + print(e); + return false; + } + } else { + return false; + } + } + + // Puis après faudra changer tous les appels à une resourceUrl avec un get local de la resource + return true; + } + + @override + Widget build(BuildContext context) { + final appContext = Provider.of(context); + TabletAppContext tabletAppContext = appContext.getContext(); + Size size = MediaQuery.of(context).size; + + return Column( + children: [ + FutureBuilder(future: download(context, tabletAppContext), builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + // Loader ou indicateur de chargement pendant la vérification + return Center(child: CircularProgressIndicator()); + } else { + return Text("Mise à jour finie"); + } + }), + ValueListenableBuilder( + valueListenable: currentResourceIndex, + builder: (context, value, _) { + return Center( + child: Text( + value.toString()+'/'+nbrResources.toString(), + style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500), + ), + ); + } + ), + ], + ); + } +} + +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"; +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index b340638..eb24461 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:manager_api/api.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; import 'package:tablet_app/client.dart'; import 'Helpers/DatabaseHelper.dart'; @@ -24,6 +25,9 @@ void main() async { localContext = await DatabaseHelper.instance.getData(); } + Directory? appDocumentsDirectory = await getDownloadsDirectory(); + String localPath = appDocumentsDirectory!.path; + if(localContext != null && localContext.host != null) { print("we've got an local db !"); print(localContext); @@ -38,6 +42,8 @@ void main() async { DeviceDetailDTO? device = await localContext.clientAPI!.deviceApi!.deviceGetDetail(localContext.deviceId!); localContext.configuration!.id = device!.configurationId; localContext.instanceId = device.instanceId; + localContext.localPath = localPath; + if (device.configurationId == null) { print("device.configurationId == null"); localContext.configuration = null; @@ -46,6 +52,8 @@ void main() async { } else { print("NO LOCAL DB !"); localContext = TabletAppContext(host: "https://api.myinfomate.be"); + + localContext.localPath = localPath; } if(kIsWeb) { diff --git a/macos/Flutter/ephemeral/Flutter-Generated.xcconfig b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig index 823be3e..33e3f7f 100644 --- a/macos/Flutter/ephemeral/Flutter-Generated.xcconfig +++ b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig @@ -1,6 +1,6 @@ // This is a generated file; do not edit or check into version control. -FLUTTER_ROOT=/Users/thomasfransolet/Documents/flutter -FLUTTER_APPLICATION_PATH=/Users/thomasfransolet/Documents/Proj/MyMuseum/tablet_app +FLUTTER_ROOT=C:\PROJ\flutter +FLUTTER_APPLICATION_PATH=C:\Users\ThomasFransolet\Documents\Documents\Perso\MuseeDeLaFraise\tablet-app COCOAPODS_PARALLEL_CODE_SIGN=true FLUTTER_BUILD_DIR=build FLUTTER_BUILD_NAME=2.0.0 diff --git a/macos/Flutter/ephemeral/flutter_export_environment.sh b/macos/Flutter/ephemeral/flutter_export_environment.sh index 89ec1db..77439b7 100755 --- a/macos/Flutter/ephemeral/flutter_export_environment.sh +++ b/macos/Flutter/ephemeral/flutter_export_environment.sh @@ -1,7 +1,7 @@ #!/bin/sh # This is a generated file; do not edit or check into version control. -export "FLUTTER_ROOT=/Users/thomasfransolet/Documents/flutter" -export "FLUTTER_APPLICATION_PATH=/Users/thomasfransolet/Documents/Proj/MyMuseum/tablet_app" +export "FLUTTER_ROOT=C:\PROJ\flutter" +export "FLUTTER_APPLICATION_PATH=C:\Users\ThomasFransolet\Documents\Documents\Perso\MuseeDeLaFraise\tablet-app" export "COCOAPODS_PARALLEL_CODE_SIGN=true" export "FLUTTER_BUILD_DIR=build" export "FLUTTER_BUILD_NAME=2.0.0" diff --git a/manager_api/lib/api/configuration_api.dart b/manager_api/lib/api/configuration_api.dart index 22d6a3a..5eb92d2 100644 --- a/manager_api/lib/api/configuration_api.dart +++ b/manager_api/lib/api/configuration_api.dart @@ -152,8 +152,8 @@ class ConfigurationApi { /// * [String] id (required): /// /// * [String] language: - Future configurationExport(String id, { String? language, }) async { - final response = await configurationExportWithHttpInfo(id, language: language, ); + Future configurationExport(String id,) async { + final response = await configurationExportWithHttpInfo(id,); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -161,10 +161,10 @@ class ConfigurationApi { // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // FormatException when trying to decode an empty string. if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { - return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'MultipartFile',) as MultipartFile; - + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'ExportConfigurationDTO',) as ExportConfigurationDTO; + } - return null; + return Future.value(null); } /// Performs an HTTP 'GET /api/Configuration' operation and returns the [Response]. diff --git a/pubspec.yaml b/pubspec.yaml index a2ead93..3a71217 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,7 +36,7 @@ dependencies: flare_flutter: ^3.0.2 provider: ^6.0.5 - http: ^1.1.0 + http: ^1.2.0 auto_size_text: ^3.0.0 fluttertoast: device_info: ^2.0.2 # DISCONTINUED @@ -55,6 +55,8 @@ dependencies: cached_video_player: ^2.0.4 cached_network_image: ^3.3.1 just_audio_cache: ^0.1.2 + path_provider: ^2.1.2 + permission_handler: ^11.2.0 openapi_generator_cli: ^4.13.1 openapi_generator: ^4.13.1 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8a0ad35..f7fb082 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -8,6 +8,7 @@ #include #include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { @@ -15,6 +16,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); FirebaseStoragePluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("FirebaseStoragePluginCApi")); + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index bd4f3fb..d17f009 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -5,6 +5,7 @@ list(APPEND FLUTTER_PLUGIN_LIST firebase_core firebase_storage + permission_handler_windows url_launcher_windows )