diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies index 70731ba..69f9a1d 100644 --- a/.flutter-plugins-dependencies +++ b/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"audioplayers_darwin","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\audioplayers_darwin-1.0.3\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_ios","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_ios-2.0.11\\\\","native_build":true,"dependencies":[]},{"name":"qr_code_scanner","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\qr_code_scanner-1.0.1\\\\","native_build":true,"dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"android":[{"name":"audioplayers_android","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\audioplayers_android-1.1.1\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_android-2.0.20\\\\","native_build":true,"dependencies":[]},{"name":"qr_code_scanner","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\qr_code_scanner-1.0.1\\\\","native_build":true,"dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"macos":[{"name":"audioplayers_darwin","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\audioplayers_darwin-1.0.3\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_macos","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_macos-2.0.6\\\\","native_build":true,"dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"linux":[{"name":"audioplayers_linux","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\audioplayers_linux-1.0.1\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_linux","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_linux-2.1.7\\\\","native_build":false,"dependencies":[]}],"windows":[{"name":"audioplayers_windows","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\audioplayers_windows-1.1.0\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_windows","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_windows-2.1.3\\\\","native_build":false,"dependencies":[]}],"web":[{"name":"audioplayers_web","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\audioplayers_web-2.0.1\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"audioplayers","dependencies":["audioplayers_android","audioplayers_darwin","audioplayers_linux","audioplayers_web","audioplayers_windows","path_provider"]},{"name":"audioplayers_android","dependencies":[]},{"name":"audioplayers_darwin","dependencies":[]},{"name":"audioplayers_linux","dependencies":[]},{"name":"audioplayers_web","dependencies":[]},{"name":"audioplayers_windows","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_ios","path_provider_linux","path_provider_macos","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_ios","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"qr_code_scanner","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2022-12-07 17:56:41.470312","version":"3.0.3"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"audioplayers_darwin","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\audioplayers_darwin-1.0.3\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_ios","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_ios-2.0.11\\\\","native_build":true,"dependencies":[]},{"name":"qr_code_scanner","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\qr_code_scanner-1.0.1\\\\","native_build":true,"dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"android":[{"name":"audioplayers_android","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\audioplayers_android-1.1.1\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_android-2.0.20\\\\","native_build":true,"dependencies":[]},{"name":"qr_code_scanner","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\qr_code_scanner-1.0.1\\\\","native_build":true,"dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"macos":[{"name":"audioplayers_darwin","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\audioplayers_darwin-1.0.3\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_macos","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_macos-2.0.6\\\\","native_build":true,"dependencies":[]},{"name":"sqflite","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-2.0.3+1\\\\","native_build":true,"dependencies":[]}],"linux":[{"name":"audioplayers_linux","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\audioplayers_linux-1.0.1\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_linux","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_linux-2.1.7\\\\","native_build":false,"dependencies":[]}],"windows":[{"name":"audioplayers_windows","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\audioplayers_windows-1.1.0\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_windows","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider_windows-2.1.3\\\\","native_build":false,"dependencies":[]}],"web":[{"name":"audioplayers_web","path":"C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dartlang.org\\\\audioplayers_web-2.0.1\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"audioplayers","dependencies":["audioplayers_android","audioplayers_darwin","audioplayers_linux","audioplayers_web","audioplayers_windows","path_provider"]},{"name":"audioplayers_android","dependencies":[]},{"name":"audioplayers_darwin","dependencies":[]},{"name":"audioplayers_linux","dependencies":[]},{"name":"audioplayers_web","dependencies":[]},{"name":"audioplayers_windows","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_ios","path_provider_linux","path_provider_macos","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_ios","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"qr_code_scanner","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2022-12-07 20:58:46.572820","version":"3.0.3"} \ No newline at end of file diff --git a/lib/Components/ScannerDialog.dart b/lib/Components/ScannerDialog.dart index 93d8827..3e965ea 100644 --- a/lib/Components/ScannerDialog.dart +++ b/lib/Components/ScannerDialog.dart @@ -145,7 +145,6 @@ class _ScannerDialogState extends State { );*/ VisitAppContext visitAppContext = widget.appContext!.getContext(); - if(!visitAppContext.sectionIds!.contains(code)) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(TranslationHelper.getFromLocale('invalidQRCode', widget.appContext!)), backgroundColor: kBlue2), diff --git a/lib/Components/ShowImagePopup.dart b/lib/Components/ShowImagePopup.dart index 995ca33..c8af645 100644 --- a/lib/Components/ShowImagePopup.dart +++ b/lib/Components/ShowImagePopup.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:manager_api/api.dart'; import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; import 'package:mymuseum_visitapp/Models/resourceModel.dart'; +import 'package:mymuseum_visitapp/Models/visitContext.dart'; import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/constants.dart'; import 'package:photo_view/photo_view.dart'; @@ -17,14 +18,22 @@ void showImagePopup(ImageDTO imageDTO, ResourceModel resourceModel, AppContext a content: SingleChildScrollView( child: Column( children: [ + Padding( + padding: const EdgeInsets.only(top: 5.0, left:8.0, right: 8.0), + child: Text( + TranslationHelper.get(imageDTO.title, appContext), + style: const TextStyle(fontSize: kArticleContentSize, fontWeight: FontWeight.w400)), + ), SizedBox( height: size.height *0.3, width: size.width * 0.9, child: Center( child: Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.only(left:8.0, right: 8.0), child: PhotoView( - imageProvider: Image.memory(base64Decode(resourceModel.data!)).image, + imageProvider: (appContext.getContext() as VisitAppContext).configuration!.isOffline! ? + Image.memory(base64Decode(resourceModel.data!)).image : + Image.network(resourceModel.source!).image, minScale: PhotoViewComputedScale.contained * 1.0, maxScale: PhotoViewComputedScale.contained * 3.0, backgroundDecoration: const BoxDecoration( @@ -37,12 +46,6 @@ void showImagePopup(ImageDTO imageDTO, ResourceModel resourceModel, AppContext a ), ), ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - TranslationHelper.get(imageDTO.title, appContext), - style: const TextStyle(fontSize: kArticleContentSize, fontWeight: FontWeight.w400)), - ), ], ), ), diff --git a/lib/Components/SliderImages.dart b/lib/Components/SliderImages.dart index 04165cf..dada49c 100644 --- a/lib/Components/SliderImages.dart +++ b/lib/Components/SliderImages.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:manager_api/api.dart'; import 'package:mymuseum_visitapp/Components/ShowImagePopup.dart'; import 'package:mymuseum_visitapp/Models/resourceModel.dart'; +import 'package:mymuseum_visitapp/Models/visitContext.dart'; import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/constants.dart'; import 'package:provider/provider.dart'; @@ -39,6 +40,7 @@ class _SliderImagesWidget extends State { @override Widget build(BuildContext context) { final appContext = Provider.of(context); + VisitAppContext visitAppContext = appContext.getContext() as VisitAppContext; Size size = MediaQuery.of(context).size; return Stack( children: [ @@ -63,6 +65,7 @@ class _SliderImagesWidget extends State { items: resourcesInWidget.map((i) { return Builder( builder: (BuildContext context) { + print(widget.imagesDTO[currentIndex-1]); return InkWell( onTap: () { showImagePopup(widget.imagesDTO[currentIndex-1]!, i!, appContext, context, size); @@ -73,7 +76,26 @@ class _SliderImagesWidget extends State { //width: size.width * 0.95, child: ClipRRect( borderRadius: BorderRadius.circular(15.0), - child: Image.memory(base64Decode(i!.data!))/*PhotoView( + child: visitAppContext.configuration!.isOffline! ? + Image.memory(base64Decode(i!.data!)) : + Image.network( + i!.source!, + loadingBuilder: (BuildContext context, Widget child, + ImageChunkEvent? loadingProgress) { + if (loadingProgress == null) { + return child; + } + return Center( + child: CircularProgressIndicator( + color: kBlue1, + value: loadingProgress.expectedTotalBytes != null + ? loadingProgress.cumulativeBytesLoaded / + loadingProgress.expectedTotalBytes! + : null, + ), + ); + }, + ) /*PhotoView( imageProvider: Image.memory(base64Decode(i!.data!)).image, minScale: PhotoViewComputedScale.contained * 0.8, maxScale: PhotoViewComputedScale.contained * 3.0, diff --git a/lib/Helpers/DatabaseHelper.dart b/lib/Helpers/DatabaseHelper.dart index 5757e76..50ea9e4 100644 --- a/lib/Helpers/DatabaseHelper.dart +++ b/lib/Helpers/DatabaseHelper.dart @@ -47,6 +47,8 @@ class DatabaseHelper { static final resourcesTable = 'resources'; + static final columnSource = 'source'; + DatabaseHelper._privateConstructor(); static final DatabaseHelper instance = DatabaseHelper._privateConstructor(); @@ -110,6 +112,7 @@ class DatabaseHelper { CREATE TABLE $resourcesTable ( $columnId TEXT NOT NULL PRIMARY KEY, $columnData TEXT NOT NULL, + $columnSource TEXT NOT NULL, $columnType INT NOT NULL ) '''); @@ -291,6 +294,7 @@ class DatabaseHelper { return ResourceModel( id: element["id"], data: element["data"], + source: element["source"], type: ResourceType.values[element["type"]] ); } diff --git a/lib/Models/resourceModel.dart b/lib/Models/resourceModel.dart index b5e7d97..23a1ecd 100644 --- a/lib/Models/resourceModel.dart +++ b/lib/Models/resourceModel.dart @@ -4,14 +4,16 @@ import 'package:manager_api/api.dart'; class ResourceModel { String? id = ""; String? data = ""; + String? source = ""; ResourceType? type; - ResourceModel({this.id, this.data, this.type}); + ResourceModel({this.id, this.data, this.source, this.type}); Map toMap() { return { 'id': id, 'data': data, + 'source': source, 'type': type!.value }; } @@ -20,12 +22,13 @@ class ResourceModel { return ResourceModel( id: json['id'] as String, data: json['data'] as String, + source: json['source'] as String, type: json['type'] as ResourceType ); } @override String toString() { - return 'ResourceModel{id: $id, type: $type, data: $data}'; + return 'ResourceModel{id: $id, type: $type, source: $source, data: $data}'; } } \ No newline at end of file diff --git a/lib/Models/visitContext.dart b/lib/Models/visitContext.dart index ff6817d..5e8cd18 100644 --- a/lib/Models/visitContext.dart +++ b/lib/Models/visitContext.dart @@ -4,7 +4,7 @@ import 'package:manager_api/api.dart'; class VisitAppContext with ChangeNotifier{ String? id = ""; String? language = ""; - String? instanceId = "633ee379d9405f32f166f047"; + String? instanceId = "633ee379d9405f32f166f047"; // Hardcoded Fort de Saint-Héribert instance id List? configurations; ConfigurationDTO? configuration; List? sectionIds; // Use to valid QR code found diff --git a/lib/Screens/Article/article.dart b/lib/Screens/Article/article.dart index 1d43fd9..368175e 100644 --- a/lib/Screens/Article/article.dart +++ b/lib/Screens/Article/article.dart @@ -11,7 +11,9 @@ import 'package:mymuseum_visitapp/Helpers/DatabaseHelper.dart'; import 'package:mymuseum_visitapp/Helpers/networkCheck.dart'; import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; import 'package:mymuseum_visitapp/Models/resourceModel.dart'; +import 'package:mymuseum_visitapp/Models/visitContext.dart'; import 'package:mymuseum_visitapp/Screens/Article/audio_player.dart'; +import 'package:mymuseum_visitapp/Services/apiService.dart'; import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/client.dart'; import 'package:mymuseum_visitapp/constants.dart'; @@ -47,7 +49,7 @@ class _ArticlePageState extends State { isHomeButton: false, ), body: FutureBuilder( - future: getArticle(appContext.clientAPI, widget.articleId), + future: getArticle(appContext, appContext.clientAPI, widget.articleId), builder: (context, AsyncSnapshot snapshot) { if(articleDTO != null && sectionDTO != null) { if(size.height > size.width) { @@ -226,18 +228,31 @@ class _ArticlePageState extends State { } } - Future getArticle(Client client, String articleId) async { + Future getArticle(AppContext appContext, Client client, String articleId) async { try { - bool isOnline = await hasNetwork(); if(sectionDTO == null || articleDTO == null) { print("ARTCILE DTO GET ARTICLE"); + bool isConfigOffline = (appContext.getContext() as VisitAppContext).configuration!.isOffline!; - List> sectionTest = await DatabaseHelper.instance.queryWithColumnId(DatabaseTableType.sections, articleId); - if(sectionTest.isNotEmpty) { - sectionDTO = DatabaseHelper.instance.getSectionFromDB(sectionTest.first); - print("sectionDTO!.datasectionDTO!.datasectionDTO!.datasectionDTO!.data"); - } else { - print("EMPTY SECTION"); + if(isConfigOffline) + { + // OFFLINE + List> sectionTest = await DatabaseHelper.instance.queryWithColumnId(DatabaseTableType.sections, articleId); + if(sectionTest.isNotEmpty) { + sectionDTO = DatabaseHelper.instance.getSectionFromDB(sectionTest.first); + print("sectionDTO!.datasectionDTO!.datasectionDTO!.datasectionDTO!.data"); + } else { + print("EMPTY SECTION"); + } + } else + { + // ONLINE + SectionDTO? sectionOnline = await client.sectionApi!.sectionGetDetail(articleId); + if(sectionOnline != null) { + sectionDTO = sectionOnline; + } else { + print("EMPTY SECTION"); + } } if(sectionDTO!.type == SectionType.Article) { @@ -248,40 +263,59 @@ class _ArticlePageState extends State { if(articleDTO != null) { if(articleDTO!.audioId != null) { - List> ressourceTest = await DatabaseHelper - .instance.queryWithColumnId( - DatabaseTableType.resources, articleDTO!.audioId!); - if (ressourceTest.isNotEmpty) { - audioResourceModel = DatabaseHelper.instance.getResourceFromDB(ressourceTest.first); - Uint8List base64String = base64Decode(audioResourceModel!.data!); - audiobytes = base64String; - } else { - print("EMPTY resourcesModel - first"); + if(isConfigOffline) + { + // OFFLINE + List> ressourceTest = await DatabaseHelper + .instance.queryWithColumnId( + DatabaseTableType.resources, articleDTO!.audioId!); + if (ressourceTest.isNotEmpty) { + audioResourceModel = DatabaseHelper.instance.getResourceFromDB(ressourceTest.first); + Uint8List base64String = base64Decode(audioResourceModel!.data!); + audiobytes = base64String; + } else { + print("EMPTY resourcesModel - first"); + } + } + else + { + // ONLINE + ResourceModel? resourceAudioOnline = await ApiService.downloadAudio(client, articleDTO!.audioId!); + if(resourceAudioOnline != null) { + audioResourceModel = resourceAudioOnline; + Uint8List base64String = base64Decode(resourceAudioOnline.data!); + audiobytes = base64String; + } else { + print("EMPTY resourcesModel online - audio"); + } } } } - /*if(sectionDTO!.imageId != null) { - List> ressourceTest = await DatabaseHelper - .instance.queryWithColumnId( - DatabaseTableType.resources, sectionDTO!.imageId!); - if (ressourceTest.isNotEmpty) { - resourcesModel.add(DatabaseHelper.instance.getResourceFromDB(ressourceTest.first)); - } else { - print("EMPTY resourcesModel - first"); - } - }*/ - if(articleDTO!.images!.isNotEmpty) { for (var image in articleDTO!.images!) { if(image.resourceId != null) { - List> ressourceArticle = await DatabaseHelper.instance.queryWithColumnId(DatabaseTableType.resources, image.resourceId!); - if(ressourceArticle.isNotEmpty) { - resourcesModel.add(DatabaseHelper.instance.getResourceFromDB(ressourceArticle.first)); - } else { - print("EMPTY resourcesModel - second"); + if(isConfigOffline) + { + // OFFLINE + List> ressourceArticle = await DatabaseHelper.instance.queryWithColumnId(DatabaseTableType.resources, image.resourceId!); + if(ressourceArticle.isNotEmpty) { + resourcesModel.add(DatabaseHelper.instance.getResourceFromDB(ressourceArticle.first)); + } else { + print("EMPTY resourcesModel - second"); + } + } + else + { + // ONLINE + // Not needed as it's in display logic + //ResourceModel? resourceImageOnline = await ApiService.downloadImage(client, image); + //if(resourceImageOnline != null) { + resourcesModel.add(ResourceModel(id: image.resourceId, source: image.source_, type: ResourceType.Image)); + /*} else { + print("EMPTY resourcesModel online - audio"); + }*/ } - } } } @@ -290,7 +324,6 @@ class _ArticlePageState extends State { print(sectionDTO!.title); }); } else { - // TODO ONLINE return null; // TODO return local list.. } } catch (e) { diff --git a/lib/Screens/Home/configurations_list.dart b/lib/Screens/Home/configurations_list.dart index 1f42c43..dbc1d6c 100644 --- a/lib/Screens/Home/configurations_list.dart +++ b/lib/Screens/Home/configurations_list.dart @@ -53,9 +53,8 @@ class _ConfigurationsListState extends State { itemBuilder: (BuildContext context, int index) { return InkWell( onTap: () async { + VisitAppContext visitAppContext = appContext.getContext(); if(configurations[index].isOffline! && alreadyDownloaded.any((c) => c == configurations[index].id)) { - VisitAppContext visitAppContext = appContext.getContext(); - if(!configurations[index].languages!.contains(visitAppContext.language)) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(TranslationHelper.getFromLocale("languageNotSupported", appContext)), backgroundColor: kBlue2), @@ -71,9 +70,25 @@ class _ConfigurationsListState extends State { )); } } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(TranslationHelper.getFromLocale("visitDownloadWarning", appContext)), backgroundColor: kBlue2), - ); + if(configurations[index].isOffline!) { + // Not already downloaded + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(TranslationHelper.getFromLocale("visitDownloadWarning", appContext)), backgroundColor: kBlue2), + ); + } else { + // Online mode + // Update context + visitAppContext.configuration = configurations[index]; + List? sections = await ApiService.getAllSections(appContext.clientAPI, visitAppContext.configuration!.id!); + + if(sections != null) { + visitAppContext.sectionIds = sections.map((e) => e.id).toList(); + } + + Navigator.of(context).pushReplacement(MaterialPageRoute( + builder: (context) => VisitPage(configurationId: configurations[index].id!), + )); + } } }, child: Container( @@ -91,7 +106,7 @@ class _ConfigurationsListState extends State { // image is square but we add extra 20 + 20 padding thats why width is 200 width: size.width*0.3, child: FutureBuilder( - future: ApiService.getResource(configurations[index].imageId!), + future: ApiService.getResource(appContext, configurations[index].imageId!), builder: (context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done) { return snapshot.data != null ? Container( diff --git a/lib/Screens/Visit/components/body.dart b/lib/Screens/Visit/components/body.dart index caa1c6a..7c383ad 100644 --- a/lib/Screens/Visit/components/body.dart +++ b/lib/Screens/Visit/components/body.dart @@ -5,7 +5,9 @@ import 'package:mymuseum_visitapp/Components/SearchBox.dart'; import 'package:mymuseum_visitapp/Components/SearchNumberBox.dart'; import 'package:mymuseum_visitapp/Helpers/DatabaseHelper.dart'; import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; +import 'package:mymuseum_visitapp/Models/visitContext.dart'; import 'package:mymuseum_visitapp/Screens/Article/article.dart'; +import 'package:mymuseum_visitapp/Services/apiService.dart'; import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/constants.dart'; import 'package:provider/provider.dart'; @@ -84,22 +86,29 @@ class _BodyState extends State { print(sectionsToDisplay);*/ return Padding( padding: const EdgeInsets.only(bottom: 0), - child: ListView.builder( - itemCount: sectionsToDisplay.length, - itemBuilder: (context, index) => SectionCard( + child: RefreshIndicator( + onRefresh: () async { + if(!(appContext.getContext() as VisitAppContext).configuration!.isOffline!) { + // Force refresh if online + setState(() {}); + } }, + child: ListView.builder( itemCount: sectionsToDisplay.length, - itemIndex: index, - sectionDTO: sectionsToDisplay[index], - press: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ArticlePage( - articleId: sectionsToDisplay[index].id!, + itemBuilder: (context, index) => SectionCard( + itemCount: sectionsToDisplay.length, + itemIndex: index, + sectionDTO: sectionsToDisplay[index], + press: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ArticlePage( + articleId: sectionsToDisplay[index].id!, + ), ), - ), - ); - }, + ); + }, + ), ), ), ); @@ -124,7 +133,22 @@ class _BodyState extends State { } getSections(AppContext appContext) async { - sections = List.from(await DatabaseHelper.instance.getData(DatabaseTableType.sections)); + if((appContext.getContext() as VisitAppContext).configuration!.isOffline!) + { + // OFFLINE + sections = List.from(await DatabaseHelper.instance.getData(DatabaseTableType.sections)); + } + else + { + // ONLINE + List? sectionsDownloaded = await ApiService.getAllSections(appContext.clientAPI, (appContext.getContext() as VisitAppContext).configuration!.id!); + print(sectionsDownloaded); + if(sectionsDownloaded!.isNotEmpty) { + sections = sectionsDownloaded.where((s) => s.type == SectionType.Article).toList(); // HERE TODO IF support more than article type + print(sections); + } + } + sections = sections.where((s) => s.configurationId == widget.configurationId).toList(); sections.sort((a,b) => a.order!.compareTo(b.order!)); sectionsToDisplay = sections; diff --git a/lib/Screens/Visit/components/section_card.dart b/lib/Screens/Visit/components/section_card.dart index 8022703..8e6308f 100644 --- a/lib/Screens/Visit/components/section_card.dart +++ b/lib/Screens/Visit/components/section_card.dart @@ -3,9 +3,8 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:manager_api/api.dart'; import 'package:mymuseum_visitapp/Components/Loading.dart'; -import 'package:mymuseum_visitapp/Helpers/DatabaseHelper.dart'; import 'package:mymuseum_visitapp/Helpers/translationHelper.dart'; -import 'package:mymuseum_visitapp/Models/resourceModel.dart'; +import 'package:mymuseum_visitapp/Models/visitContext.dart'; import 'package:mymuseum_visitapp/Services/apiService.dart'; import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/constants.dart'; @@ -30,7 +29,7 @@ class SectionCard extends StatelessWidget { // It will provide us total height and width of our screen Size size = MediaQuery.of(context).size; final appContext = Provider.of(context); - ResourceModel? resourceModel; + bool isOffline = (appContext.getContext() as VisitAppContext).configuration!.isOffline!; return Container( margin: EdgeInsets.symmetric( @@ -78,15 +77,35 @@ class SectionCard extends StatelessWidget { // image is square but we add extra 20 + 20 padding thats why width is 200 width: size.width*0.5, child: FutureBuilder( - future: ApiService.getResource(sectionDTO.imageId!), + future: ApiService.getResource(appContext, sectionDTO.imageId!), builder: (context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done) { return snapshot.data != null ? Container( child: ClipRRect( borderRadius: const BorderRadius.only(topRight: Radius.circular(20), bottomRight: Radius.circular(20)), - child: Image.memory( + child: isOffline ? + Image.memory( base64Decode(snapshot.data!.data!), fit: BoxFit.cover + ) : + Image.network( + sectionDTO.imageSource!, + fit: BoxFit.cover, + loadingBuilder: (BuildContext context, Widget child, + ImageChunkEvent? loadingProgress) { + if (loadingProgress == null) { + return child; + } + return Center( + child: CircularProgressIndicator( + color: kBlue1, + value: loadingProgress.expectedTotalBytes != null + ? loadingProgress.cumulativeBytesLoaded / + loadingProgress.expectedTotalBytes! + : null, + ), + ); + }, ), ), ) : const Text(""); @@ -139,7 +158,7 @@ class SectionCard extends StatelessWidget { ), ), child: Text( - "${sectionDTO.order!+1}", + "${itemIndex+1}", //style: Theme.of(context).textTheme.button, style: const TextStyle(color: Colors.white) ), diff --git a/lib/Services/apiService.dart b/lib/Services/apiService.dart index ee15de4..3560370 100644 --- a/lib/Services/apiService.dart +++ b/lib/Services/apiService.dart @@ -6,6 +6,7 @@ import 'package:mymuseum_visitapp/Helpers/DatabaseHelper.dart'; import 'package:mymuseum_visitapp/Helpers/networkCheck.dart'; import 'package:mymuseum_visitapp/Models/resourceModel.dart'; import 'package:mymuseum_visitapp/Models/visitContext.dart'; +import 'package:mymuseum_visitapp/app_context.dart'; import 'package:mymuseum_visitapp/client.dart'; class ApiService { @@ -20,7 +21,7 @@ class ApiService { if(configurations.isNotEmpty) { for(var configuration in configurations) { if(configuration.imageId != null) { - await getAndDownloadImage(client, ImageDTO(source_: configuration.imageSource, resourceId: configuration.imageId)); + await downloadAndPushLocalImage(client, ImageDTO(source_: configuration.imageSource, resourceId: configuration.imageId)); } } } @@ -33,7 +34,7 @@ class ApiService { } catch (e) { print(e); - print("IN CATCH"); + print("getConfigurations IN CATCH"); return []; } } @@ -49,12 +50,12 @@ class ApiService { } } catch (e) { print(e); - print("IN CATCH"); + print("getAllSections IN CATCH"); return []; } } - static Future getAndDownloadImage(Client client, ImageDTO imageDTO) async { + static Future downloadAndPushLocalImage(Client client, ImageDTO imageDTO) async { try { // Test if already here or not.. @@ -64,23 +65,10 @@ class ApiService { } else { bool isOnline = await hasNetwork(); if(isOnline) { - var source = imageDTO.source_; - //print("SOURCE getAndDownloadImage"); - //print(source); - if(imageDTO.source_!.contains("localhost:5000")) { - print("Contains localhost:5000"); - source = imageDTO.source_!.replaceAll("http://localhost:5000", client.apiApi!.basePath); + ResourceModel? resourceModel = await downloadImage(client, imageDTO); + if(resourceModel != null) { + await DatabaseHelper.instance.insert(DatabaseTableType.resources, resourceModel.toMap()); } - HttpClient client2 = HttpClient(); - var _downloadData = []; - final HttpClientRequest request = await client2.getUrl(Uri.parse(source!)); - //print("RESPONSE"); - HttpClientResponse response = await request.close(); - await for(dynamic d in response) { _downloadData.addAll(d); } - //print("AFTER"); - final base64Str = base64.encode(_downloadData); - ResourceModel resourceModel = ResourceModel(id: imageDTO.resourceId, data: base64Str, type: ResourceType.Image); - await DatabaseHelper.instance.insert(DatabaseTableType.resources, resourceModel.toMap()); return true; } else { return false; @@ -88,13 +76,33 @@ class ApiService { } } catch (e) { print(e); - print("IN CATCH"); + print("getAndDownloadImage IN CATCH"); return false; } } + static Future downloadImage(Client client, ImageDTO imageDTO) async { + var source = imageDTO.source_; + //print("SOURCE getAndDownloadImage"); + //print(source); + if(imageDTO.source_!.contains("localhost:5000")) { + print("Contains localhost:5000"); + source = imageDTO.source_!.replaceAll("http://localhost:5000", client.apiApi!.basePath); + } + HttpClient client2 = HttpClient(); + var _downloadData = []; + final HttpClientRequest request = await client2.getUrl(Uri.parse(source!)); + //print("RESPONSE"); + HttpClientResponse response = await request.close(); + await for(dynamic d in response) { _downloadData.addAll(d); } + //print("AFTER"); + final base64Str = base64.encode(_downloadData); + ResourceModel resourceModel = ResourceModel(id: imageDTO.resourceId, source: imageDTO.source_, data: base64Str, type: ResourceType.Image); + return resourceModel; + } - static Future getAndDownloadAudio(Client client, String audioId) async { + + static Future downloadAndPushLocalAudio(Client client, String audioId) async { try { // Test if already here or not.. @@ -104,15 +112,10 @@ class ApiService { } else { bool isOnline = await hasNetwork(); if(isOnline) { - var url = "https://api.mymuseum.be/api/Resource/"+audioId; // TO TEST TODO UPDATE ROUTE - HttpClient client2 = HttpClient(); - var _downloadData = []; - final HttpClientRequest request = await client2.getUrl(Uri.parse(url)); - HttpClientResponse response = await request.close(); - await for(dynamic d in response) { _downloadData.addAll(d); } - final base64Str = base64.encode(_downloadData); - ResourceModel resourceModel = ResourceModel(id: audioId, data: base64Str, type: ResourceType.Audio); - await DatabaseHelper.instance.insert(DatabaseTableType.resources, resourceModel.toMap()); + ResourceModel? resourceModel = await downloadAudio(client, audioId); + if(resourceModel != null) { + await DatabaseHelper.instance.insert(DatabaseTableType.resources, resourceModel.toMap()); + } return true; } else { return false; @@ -120,18 +123,38 @@ class ApiService { } } catch (e) { print(e); - print("IN CATCH"); + print("getAndDownloadAudio IN CATCH"); return false; } } - static Future getResource(String imageId) async { - List> resourceTest = await DatabaseHelper.instance.queryWithColumnId(DatabaseTableType.resources, imageId); - if(resourceTest.isNotEmpty) { - return DatabaseHelper.instance.getResourceFromDB(resourceTest.first); - } else { - return null; - } + static Future downloadAudio(Client client, String audioId) async { + var url = "https://api.mymuseum.be/api/Resource/"+audioId; // TO TEST TODO UPDATE ROUTE + HttpClient client2 = HttpClient(); + var _downloadData = []; + final HttpClientRequest request = await client2.getUrl(Uri.parse(url)); + HttpClientResponse response = await request.close(); + await for(dynamic d in response) { _downloadData.addAll(d); } + final base64Str = base64.encode(_downloadData); + ResourceModel resourceModel = ResourceModel(id: audioId, data: base64Str, type: ResourceType.Audio); + return resourceModel; + } + + static Future getResource(AppContext appContext, String imageId) async { + if((appContext.getContext() as VisitAppContext).configuration == null || (appContext.getContext() as VisitAppContext).configuration!.isOffline!) + { + // OFFLINE + List> resourceTest = await DatabaseHelper.instance.queryWithColumnId(DatabaseTableType.resources, imageId); + if(resourceTest.isNotEmpty) { + return DatabaseHelper.instance.getResourceFromDB(resourceTest.first); + } else { + return null; + } + } else + { + // ONLINE + return ResourceModel(); // To mock + } } } diff --git a/lib/Services/downloadConfiguration.dart b/lib/Services/downloadConfiguration.dart index 14d4047..1249315 100644 --- a/lib/Services/downloadConfiguration.dart +++ b/lib/Services/downloadConfiguration.dart @@ -17,7 +17,7 @@ class DownloadConfiguration { if(configuration.imageId != null) { ImageDTO image = ImageDTO(resourceId: configuration.imageId, source_: configuration.imageSource); usedImageOrAudioIds.add(configuration.imageId!); - await ApiService.getAndDownloadImage(appContext.clientAPI, image); + await ApiService.downloadAndPushLocalImage(appContext.clientAPI, image); } List? sections = await ApiService.getAllSections(appContext.clientAPI, configuration.id!); @@ -38,7 +38,7 @@ class DownloadConfiguration { // Download section image if(section.imageId != null) { usedImageOrAudioIds.add(section.imageId!); - await ApiService.getAndDownloadImage(appContext.clientAPI, ImageDTO(source_: section.imageSource, resourceId: section.imageId)); + await ApiService.downloadAndPushLocalImage(appContext.clientAPI, ImageDTO(source_: section.imageSource, resourceId: section.imageId)); } // Download all images.. @@ -47,11 +47,11 @@ class DownloadConfiguration { if(articleDTO != null) { for(var image in articleDTO.images!) { usedImageOrAudioIds.add(image.resourceId!); - await ApiService.getAndDownloadImage(appContext.clientAPI, image); + await ApiService.downloadAndPushLocalImage(appContext.clientAPI, image); } if(articleDTO.audioId != null) { usedImageOrAudioIds.add(articleDTO.audioId!); - await ApiService.getAndDownloadAudio(appContext.clientAPI, articleDTO.audioId!); + await ApiService.downloadAndPushLocalAudio(appContext.clientAPI, articleDTO.audioId!); } } newOrder = newOrder + 1;