diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d2a0085..2a71919 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -94,7 +94,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: camera_avfoundation: 3125e8cd1a4387f6f31c6c63abb8a55892a9eeeb - connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e + connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0 diff --git a/lib/core/routes/app_routes.dart b/lib/core/routes/app_routes.dart deleted file mode 100644 index f4d3231..0000000 --- a/lib/core/routes/app_routes.dart +++ /dev/null @@ -1,6 +0,0 @@ -abstract class Routes { - static const INITIAL = '/'; - static const LOGIN = '/login'; - static const CAMERA = '/camera'; - static const LIST = '/list'; -} diff --git a/lib/core/routes/plausible_tracker.dart b/lib/core/routes/plausible_tracker.dart new file mode 100644 index 0000000..716d04f --- /dev/null +++ b/lib/core/routes/plausible_tracker.dart @@ -0,0 +1,103 @@ +import 'package:flutter/foundation.dart'; +import 'package:universal_io/io.dart'; // instead of 'dart:io'; +import 'dart:convert'; + +/// Plausible class. Use the constructor to set the parameters. +class PlausibleTracker { + /// The url of your plausible server e.g. https://plausible.io + String serverUrl; + String userAgent; + String domain; + String screenWidth; + String xForwardedFor; + bool enabled = true; + + /// Constructor + PlausibleTracker(this.serverUrl, this.domain, + {this.userAgent = "", + this.screenWidth = "", + this.xForwardedFor = "127.0.0.1"}); + + /// Post event to plausible + Future event( + {String name = "pageview", + String referrer = "", + String page = "", + Map props = const {}}) async { + if (!enabled) { + return 0; + } + + // Post-edit parameters + int lastCharIndex = serverUrl.length - 1; + if (serverUrl.toString()[lastCharIndex] == '/') { + // Remove trailing slash '/' + serverUrl = serverUrl.substring(0, lastCharIndex); + } + page = "app://localhost/" + page; + referrer = "app://localhost/" + referrer; + + // Get and set device infos + String version = Platform.operatingSystemVersion.replaceAll('"', ''); + + if (userAgent == "") { + userAgent = "Mozilla/5.0 ($version; rv:53.0) Gecko/20100101 Chrome/53.0"; + } + + // Http Post request see https://plausible.io/docs/events-api + try { + HttpClient client = HttpClient(); + HttpClientRequest request = + await client.postUrl(Uri.parse(serverUrl + '/api/event')); + request.headers.set('User-Agent', userAgent); + request.headers.set('Content-Type', 'application/json; charset=utf-8'); + request.headers.set('X-Forwarded-For', xForwardedFor); + Object body = { + "domain": domain, + "name": name, + "url": page, + "referrer": referrer, + "screen_width": screenWidth, + "props": props, + }; + request.write(json.encode(body)); + final HttpClientResponse response = await request.close(); + client.close(); + return response.statusCode; + } catch (e) { + if (kDebugMode) { + print(e); + } + } + + return 1; + } + + /// check if plausible is UP + Future hello() async { + try { + final client = HttpClient(); + final request = await client.getUrl(Uri.parse(serverUrl + '/api/health')); + final response = await request.close(); + + if (response.statusCode == 200) { + final responseBody = await response.transform(utf8.decoder).join(); + final json = jsonDecode(responseBody); + + final clickhouseStatus = json['clickhouse']; + final postgresStatus = json['postgres']; + final sitesCacheStatus = json['sites_cache']; + + return clickhouseStatus == 'ok' && + postgresStatus == 'ok' && + sitesCacheStatus == 'ok'; + } + } catch (e) { + if (kDebugMode) { + print(e); + } + } + + return false; + } +} diff --git a/lib/db/db_log.dart b/lib/db/db_log.dart new file mode 100644 index 0000000..b725b7c --- /dev/null +++ b/lib/db/db_log.dart @@ -0,0 +1,8 @@ +import "package:mobdr/main.dart"; + +class dbLog { + static void addLog( + String type, String module, String libelle, int duree) async { + objectbox.addLog(type, module, libelle, duree); + } +} diff --git a/lib/main.dart b/lib/main.dart index 340183c..c2570c4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,36 +1,32 @@ // ignore_for_file: prefer_const_constructors import 'dart:ui'; -//import 'dart:developer' as developer; -import 'dart:io'; - -import 'package:device_info_plus/device_info_plus.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; -import 'package:path_provider/path_provider.dart'; - import 'objectbox.dart'; import 'package:wakelock/wakelock.dart'; import 'package:event_bus_plus/event_bus_plus.dart'; +import 'package:mobdr/service/logger_util.dart'; import 'package:mobdr/config/constant.dart'; - +import 'package:mobdr/core/routes/plausible_tracker.dart'; import 'package:mobdr/cubit/language/language_cubit.dart'; import 'package:mobdr/cubit/language/app_localizations.dart'; import 'package:mobdr/cubit/language/initial_language.dart'; import 'package:mobdr/service/shared_prefs.dart'; import 'package:mobdr/ui/splash_screen.dart'; +import 'package:mobdr/service/device_info.dart'; +import 'package:mobdr/service/directories.dart'; +import 'package:mobdr/service/plausible.dart'; +import 'package:mobdr/db/db_log.dart'; /// Provides access to the ObjectBox Store throughout the app. late ObjectBox objectbox; - -final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); +late PlausibleTracker plausible; final EventBus eventBus = EventBus(); @@ -39,12 +35,39 @@ Future main() async { // to store the database in. WidgetsFlutterBinding.ensureInitialized(); - await SharedPrefs().init(); + // registration of the observer + WidgetsBinding.instance.addObserver(MyApp()); + LoggerUtil.logNStackInfo('Application MobDR started'); + + // create database object objectbox = await ObjectBox.create(); - /// Log - objectbox.addLog('LOG', 'MOBDR', 'Ouverture application ', 0); + // initialize shared preferences + await SharedPrefs().init(); + + // get/set device informations + await device_info_plus().initPlatformState(); + + // initialize directories + await directories().initDirectories(); + + /// tracker analytics + await PlausibleUtil.initializePlausible(window.physicalSize.width); + + // url MP4 + SharedPrefs().urlMP4 = + 'https://mp4.ikksgroup.com/MobilePortal4/index.html#ajax/dashboard.html'; + + PlausibleUtil.addEvent( + name: 'access', + page: 'access', + referrer: 'referrerPage', + props: { + 'name': SharedPrefs().login, + }); + + dbLog.addLog('LOG', 'MOBDR', 'Ouverture application ', 0); SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]) .then((_) { @@ -53,7 +76,7 @@ Future main() async { Wakelock.enable(); eventBus.on().listen((event) { - print('${DateTime.now()} Event: $event'); + LoggerUtil.logVerbose('${DateTime.now()} Event: $event'); }); runApp(MyApp()); @@ -70,17 +93,30 @@ class MyCustomScrollBehavior extends MaterialScrollBehavior { }; } -class MyApp extends StatelessWidget { +class MyApp extends StatelessWidget with WidgetsBindingObserver { + @override + void didChangeAppLifecycleState(AppLifecycleState state) async { + if (state == AppLifecycleState.resumed) { + LoggerUtil.logNStackInfo("The application is in foreground"); + + // check if plausible is UP + plausible.enabled = await plausible.hello(); + + plausible.event( + name: 'access', + page: 'access', + referrer: 'referrerPage', + props: { + 'name': SharedPrefs().login, + }); + } else if (state == AppLifecycleState.inactive) { + LoggerUtil.logNStackInfo("The application runs in the background"); + } + } + // This widget is the root of your application. @override Widget build(BuildContext context) { - initDirectories(); - initPlatformState(); - - // url MP4 - SharedPrefs().urlMP4 = - 'https://mp4.ikksgroup.com/MobilePortal4/index.html#ajax/dashboard.html'; - // Initialize all bloc provider used on this entire application here return MultiBlocProvider( providers: [ @@ -139,211 +175,4 @@ class MyApp extends StatelessWidget { ), ); } - - void initDirectories() async { - // Get the application's document directory - final Directory documentsDir = await getApplicationDocumentsDirectory(); - - // Create a Directory object for the "photos" directory in the documents directory - final Directory photosDir = Directory('${documentsDir.path}/photos'); - - // Check if the "photos" directory exists - if (await photosDir.exists()) { - print( - 'The "photos" directory already exists in the documents directory.'); - } else { - // Create the "photos" directory if it does not exist - await photosDir.create(); - print( - 'The "photos" directory has been created in the documents directory.'); - } - - // save directories into shared Prefs - SharedPrefs().documentsDir = documentsDir.path; - SharedPrefs().photosDir = photosDir.path; - - // Get a list of all files in the "photos" directory - final List files = await photosDir.list().toList(); - - // Print out the names of the files in the directory - for (FileSystemEntity file in files) { - print('File name: ${file.path.split('/').last}'); - } - } - - Future initPlatformState() async { - var deviceData = {}; - - try { - if (kIsWeb) { - deviceData = _readWebBrowserInfo(await deviceInfoPlugin.webBrowserInfo); - } else { - if (Platform.isAndroid) { - deviceData = - _readAndroidBuildData(await deviceInfoPlugin.androidInfo); - } else if (Platform.isIOS) { - deviceData = _readIosDeviceInfo(await deviceInfoPlugin.iosInfo); - } else if (Platform.isLinux) { - deviceData = _readLinuxDeviceInfo(await deviceInfoPlugin.linuxInfo); - } else if (Platform.isMacOS) { - deviceData = _readMacOsDeviceInfo(await deviceInfoPlugin.macOsInfo); - } else if (Platform.isWindows) { - deviceData = - _readWindowsDeviceInfo(await deviceInfoPlugin.windowsInfo); - } - } - - // save if we are on a simulator - if (deviceData['isPhysicalDevice'] == false) { - SharedPrefs().isSimulator = true; - } - - print(deviceData); - } on PlatformException { - deviceData = { - 'Error:': 'Failed to get platform version.' - }; - } - } - - Map _readAndroidBuildData(AndroidDeviceInfo build) { - return { - 'version.securityPatch': build.version.securityPatch, - 'version.sdkInt': build.version.sdkInt, - 'version.release': build.version.release, - 'version.previewSdkInt': build.version.previewSdkInt, - 'version.incremental': build.version.incremental, - 'version.codename': build.version.codename, - 'version.baseOS': build.version.baseOS, - 'board': build.board, - 'bootloader': build.bootloader, - 'brand': build.brand, - 'device': build.device, - 'display': build.display, - 'fingerprint': build.fingerprint, - 'hardware': build.hardware, - 'host': build.host, - 'id': build.id, - 'manufacturer': build.manufacturer, - 'model': build.model, - 'product': build.product, - 'supported32BitAbis': build.supported32BitAbis, - 'supported64BitAbis': build.supported64BitAbis, - 'supportedAbis': build.supportedAbis, - 'tags': build.tags, - 'type': build.type, - 'isPhysicalDevice': build.isPhysicalDevice, - 'systemFeatures': build.systemFeatures, - 'displaySizeInches': - ((build.displayMetrics.sizeInches * 10).roundToDouble() / 10), - 'displayWidthPixels': build.displayMetrics.widthPx, - 'displayWidthInches': build.displayMetrics.widthInches, - 'displayHeightPixels': build.displayMetrics.heightPx, - 'displayHeightInches': build.displayMetrics.heightInches, - 'displayXDpi': build.displayMetrics.xDpi, - 'displayYDpi': build.displayMetrics.yDpi, - 'serialNumber': build.serialNumber, - }; - } - - Map _readIosDeviceInfo(IosDeviceInfo data) { - return { - 'name': data.name, - 'systemName': data.systemName, - 'systemVersion': data.systemVersion, - 'model': data.model, - 'localizedModel': data.localizedModel, - 'identifierForVendor': data.identifierForVendor, - 'isPhysicalDevice': data.isPhysicalDevice, - 'utsname.sysname:': data.utsname.sysname, - 'utsname.nodename:': data.utsname.nodename, - 'utsname.release:': data.utsname.release, - 'utsname.version:': data.utsname.version, - 'utsname.machine:': data.utsname.machine, - }; - } - - Map _readLinuxDeviceInfo(LinuxDeviceInfo data) { - return { - 'name': data.name, - 'version': data.version, - 'id': data.id, - 'idLike': data.idLike, - 'versionCodename': data.versionCodename, - 'versionId': data.versionId, - 'prettyName': data.prettyName, - 'buildId': data.buildId, - 'variant': data.variant, - 'variantId': data.variantId, - 'machineId': data.machineId, - }; - } - - Map _readWebBrowserInfo(WebBrowserInfo data) { - return { - 'browserName': describeEnum(data.browserName), - 'appCodeName': data.appCodeName, - 'appName': data.appName, - 'appVersion': data.appVersion, - 'deviceMemory': data.deviceMemory, - 'language': data.language, - 'languages': data.languages, - 'platform': data.platform, - 'product': data.product, - 'productSub': data.productSub, - 'userAgent': data.userAgent, - 'vendor': data.vendor, - 'vendorSub': data.vendorSub, - 'hardwareConcurrency': data.hardwareConcurrency, - 'maxTouchPoints': data.maxTouchPoints, - }; - } - - Map _readMacOsDeviceInfo(MacOsDeviceInfo data) { - return { - 'computerName': data.computerName, - 'hostName': data.hostName, - 'arch': data.arch, - 'model': data.model, - 'kernelVersion': data.kernelVersion, - 'majorVersion': data.majorVersion, - 'minorVersion': data.minorVersion, - 'patchVersion': data.patchVersion, - 'osRelease': data.osRelease, - 'activeCPUs': data.activeCPUs, - 'memorySize': data.memorySize, - 'cpuFrequency': data.cpuFrequency, - 'systemGUID': data.systemGUID, - }; - } - - Map _readWindowsDeviceInfo(WindowsDeviceInfo data) { - return { - 'numberOfCores': data.numberOfCores, - 'computerName': data.computerName, - 'systemMemoryInMegabytes': data.systemMemoryInMegabytes, - 'userName': data.userName, - 'majorVersion': data.majorVersion, - 'minorVersion': data.minorVersion, - 'buildNumber': data.buildNumber, - 'platformId': data.platformId, - 'csdVersion': data.csdVersion, - 'servicePackMajor': data.servicePackMajor, - 'servicePackMinor': data.servicePackMinor, - 'suitMask': data.suitMask, - 'productType': data.productType, - 'reserved': data.reserved, - 'buildLab': data.buildLab, - 'buildLabEx': data.buildLabEx, - 'digitalProductId': data.digitalProductId, - 'displayVersion': data.displayVersion, - 'editionId': data.editionId, - 'installDate': data.installDate, - 'productId': data.productId, - 'productName': data.productName, - 'registeredOwner': data.registeredOwner, - 'releaseId': data.releaseId, - 'deviceId': data.deviceId, - }; - } } diff --git a/lib/network/get_ip_address.dart b/lib/network/get_ip_address.dart new file mode 100644 index 0000000..c9bbf9a --- /dev/null +++ b/lib/network/get_ip_address.dart @@ -0,0 +1,17 @@ + +import 'package:http/http.dart' as http; +import 'dart:convert'; + +Future getPublicIPAddress() async { + try { + var response = + await http.get(Uri.parse('https://api.ipify.org/?format=json')); + if (response.statusCode == 200) { + var data = json.decode(response.body); + return data['ip']; + } + } catch (e) { + print(e.toString()); + } + return '127.0.0.1'; +} diff --git a/lib/service/device_info.dart b/lib/service/device_info.dart new file mode 100644 index 0000000..60ba41b --- /dev/null +++ b/lib/service/device_info.dart @@ -0,0 +1,201 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'dart:io'; + +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:mobdr/service/logger_util.dart'; +import 'package:mobdr/service/shared_prefs.dart'; + +class device_info_plus { + static final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); + + Future initPlatformState() async { + var deviceData = {}; + + try { + if (kIsWeb) { + deviceData = _readWebBrowserInfo(await deviceInfoPlugin.webBrowserInfo); + } else { + if (Platform.isAndroid) { + deviceData = + _readAndroidBuildData(await deviceInfoPlugin.androidInfo); + } else if (Platform.isIOS) { + deviceData = _readIosDeviceInfo(await deviceInfoPlugin.iosInfo); + } else if (Platform.isLinux) { + deviceData = _readLinuxDeviceInfo(await deviceInfoPlugin.linuxInfo); + } else if (Platform.isMacOS) { + deviceData = _readMacOsDeviceInfo(await deviceInfoPlugin.macOsInfo); + } else if (Platform.isWindows) { + deviceData = + _readWindowsDeviceInfo(await deviceInfoPlugin.windowsInfo); + } + } + + // save if we are on a simulator + if (deviceData['isPhysicalDevice'] == false) { + SharedPrefs().isSimulator = true; + } + + if (deviceData['userAgent'] == null) { + SharedPrefs().mobileUserAgent = buildUserAgent(deviceData); + } else { + SharedPrefs().mobileUserAgent = deviceData['userAgent']; + } + + LoggerUtil.logNStackInfo('Devicedata:' + deviceData.toString()); + } on PlatformException { + deviceData = { + 'Error:': 'Failed to get platform version.' + }; + } + } + + Map _readAndroidBuildData(AndroidDeviceInfo build) { + return { + 'version.securityPatch': build.version.securityPatch, + 'version.sdkInt': build.version.sdkInt, + 'version.release': build.version.release, + 'version.previewSdkInt': build.version.previewSdkInt, + 'version.incremental': build.version.incremental, + 'version.codename': build.version.codename, + 'version.baseOS': build.version.baseOS, + 'board': build.board, + 'bootloader': build.bootloader, + 'brand': build.brand, + 'device': build.device, + 'display': build.display, + 'fingerprint': build.fingerprint, + 'hardware': build.hardware, + 'host': build.host, + 'id': build.id, + 'manufacturer': build.manufacturer, + 'model': build.model, + 'product': build.product, + 'supported32BitAbis': build.supported32BitAbis, + 'supported64BitAbis': build.supported64BitAbis, + 'supportedAbis': build.supportedAbis, + 'tags': build.tags, + 'type': build.type, + 'isPhysicalDevice': build.isPhysicalDevice, + 'systemFeatures': build.systemFeatures, + 'displaySizeInches': + ((build.displayMetrics.sizeInches * 10).roundToDouble() / 10), + 'displayWidthPixels': build.displayMetrics.widthPx, + 'displayWidthInches': build.displayMetrics.widthInches, + 'displayHeightPixels': build.displayMetrics.heightPx, + 'displayHeightInches': build.displayMetrics.heightInches, + 'displayXDpi': build.displayMetrics.xDpi, + 'displayYDpi': build.displayMetrics.yDpi, + 'serialNumber': build.serialNumber, + }; + } + + Map _readIosDeviceInfo(IosDeviceInfo data) { + return { + 'name': data.name, + 'systemName': data.systemName, + 'systemVersion': data.systemVersion, + 'model': data.model, + 'localizedModel': data.localizedModel, + 'identifierForVendor': data.identifierForVendor, + 'isPhysicalDevice': data.isPhysicalDevice, + 'utsname.sysname:': data.utsname.sysname, + 'utsname.nodename:': data.utsname.nodename, + 'utsname.release:': data.utsname.release, + 'utsname.version:': data.utsname.version, + 'utsname.machine:': data.utsname.machine, + }; + } + + Map _readLinuxDeviceInfo(LinuxDeviceInfo data) { + return { + 'name': data.name, + 'version': data.version, + 'id': data.id, + 'idLike': data.idLike, + 'versionCodename': data.versionCodename, + 'versionId': data.versionId, + 'prettyName': data.prettyName, + 'buildId': data.buildId, + 'variant': data.variant, + 'variantId': data.variantId, + 'machineId': data.machineId, + }; + } + + Map _readWebBrowserInfo(WebBrowserInfo data) { + return { + 'browserName': describeEnum(data.browserName), + 'appCodeName': data.appCodeName, + 'appName': data.appName, + 'appVersion': data.appVersion, + 'deviceMemory': data.deviceMemory, + 'language': data.language, + 'languages': data.languages, + 'platform': data.platform, + 'product': data.product, + 'productSub': data.productSub, + 'userAgent': data.userAgent, + 'vendor': data.vendor, + 'vendorSub': data.vendorSub, + 'hardwareConcurrency': data.hardwareConcurrency, + 'maxTouchPoints': data.maxTouchPoints, + }; + } + + Map _readMacOsDeviceInfo(MacOsDeviceInfo data) { + return { + 'computerName': data.computerName, + 'hostName': data.hostName, + 'arch': data.arch, + 'model': data.model, + 'kernelVersion': data.kernelVersion, + 'majorVersion': data.majorVersion, + 'minorVersion': data.minorVersion, + 'patchVersion': data.patchVersion, + 'osRelease': data.osRelease, + 'activeCPUs': data.activeCPUs, + 'memorySize': data.memorySize, + 'cpuFrequency': data.cpuFrequency, + 'systemGUID': data.systemGUID, + }; + } + + Map _readWindowsDeviceInfo(WindowsDeviceInfo data) { + return { + 'numberOfCores': data.numberOfCores, + 'computerName': data.computerName, + 'systemMemoryInMegabytes': data.systemMemoryInMegabytes, + 'userName': data.userName, + 'majorVersion': data.majorVersion, + 'minorVersion': data.minorVersion, + 'buildNumber': data.buildNumber, + 'platformId': data.platformId, + 'csdVersion': data.csdVersion, + 'servicePackMajor': data.servicePackMajor, + 'servicePackMinor': data.servicePackMinor, + 'suitMask': data.suitMask, + 'productType': data.productType, + 'reserved': data.reserved, + 'buildLab': data.buildLab, + 'buildLabEx': data.buildLabEx, + 'digitalProductId': data.digitalProductId, + 'displayVersion': data.displayVersion, + 'editionId': data.editionId, + 'installDate': data.installDate, + 'productId': data.productId, + 'productName': data.productName, + 'registeredOwner': data.registeredOwner, + 'releaseId': data.releaseId, + 'deviceId': data.deviceId, + }; + } + + String buildUserAgent(Map deviceData) { + String systemName = deviceData['systemName']; + String systemVersion = deviceData['systemVersion']; + String model = deviceData['model']; + + return '$systemName $systemVersion; $model'; + } +} diff --git a/lib/service/directories.dart b/lib/service/directories.dart new file mode 100644 index 0000000..1208a35 --- /dev/null +++ b/lib/service/directories.dart @@ -0,0 +1,49 @@ +import 'dart:io'; +import 'package:path_provider/path_provider.dart'; + +import 'package:mobdr/service/shared_prefs.dart'; + +class directories { + Future initDirectories() async { + // Get the application's document directory + final Directory documentsDir = await getApplicationDocumentsDirectory(); + + // Directory object for the "photos" directory in the documents directory + final Directory photosDir = Directory('${documentsDir.path}/photos'); + + // Directory object for the "cache" directory in the documents directory + final Directory cacheDir = await getTemporaryDirectory(); + + // Check if the "photos" directory exists + if (await photosDir.exists()) { + print( + 'The "photos" directory already exists in the documents directory.'); + } else { + // Create the "photos" directory if it does not exist + await photosDir.create(); + print( + 'The "photos" directory has been created in the documents directory.'); + } + + // save directories into shared Prefs + SharedPrefs().documentsDir = documentsDir.path; + SharedPrefs().photosDir = photosDir.path; + SharedPrefs().cacheDir = cacheDir.path; + + // Get a list of all files in the "photos" directory + List files = await photosDir.list().toList(); + + // Print out the names of the files in the directory + for (FileSystemEntity file in files) { + print('File name in photo directory : ${file.path.split('/').last}'); + } + + // Get a list of all files in the "photos" directory + files = await cacheDir.list().toList(); + + // Print out the names of the files in the directory + for (FileSystemEntity file in files) { + print('File name in cache directory : ${file.path.split('/').last}'); + } + } +} diff --git a/lib/service/logger_util.dart b/lib/service/logger_util.dart new file mode 100644 index 0000000..a951fab --- /dev/null +++ b/lib/service/logger_util.dart @@ -0,0 +1,49 @@ +import 'package:logger/logger.dart'; + +class LoggerUtil { + static final Logger _logger = Logger(printer: PrettyPrinter()); + + static final Logger _loggerNoStack = Logger( + printer: PrettyPrinter(methodCount: 0), + ); + + static void logVerbose(String message) { + _logger.v(message); + } + + static void logDebug(String message) { + _logger.d(message); + } + + static void logInfo(String message) { + _logger.i(message); + } + + static void logWarning(String message) { + _logger.w(message); + } + + static void logError(String message) { + _logger.e(message); + } + + static void logNStacktackVerbose(String message) { + _loggerNoStack.v(message); + } + + static void logNStacktackDebug(String message) { + _loggerNoStack.d(message); + } + + static void logNStackInfo(String message) { + _loggerNoStack.i(message); + } + + static void logNStackWarning(String message) { + _loggerNoStack.w(message); + } + + static void logNStackError(String message) { + _loggerNoStack.e(message); + } +} diff --git a/lib/service/plausible.dart b/lib/service/plausible.dart new file mode 100644 index 0000000..d48a740 --- /dev/null +++ b/lib/service/plausible.dart @@ -0,0 +1,35 @@ +import 'package:mobdr/core/routes/plausible_tracker.dart'; +import 'package:mobdr/service/shared_prefs.dart'; +import 'package:mobdr/network/get_ip_address.dart'; + +class PlausibleUtil { + static PlausibleTracker? plausible; + + static Future initializePlausible(double screenWidth) async { + plausible = PlausibleTracker( + "https://plausible.q2ii.fr", + "mobdr.ikksgroup.com", + ); + + await plausible!.hello(); + + plausible!.userAgent = SharedPrefs().mobileUserAgent; + plausible!.xForwardedFor = await getPublicIPAddress(); + plausible!.screenWidth = screenWidth.toString(); + plausible!.enabled = await plausible!.hello(); + } + + static void addEvent({ + required String name, + required String page, + String? referrer, + Map? props, + }) { + PlausibleUtil.plausible?.event( + name: name, + page: page, + referrer: referrer ?? '', + props: props ?? {}, + ); + } +} diff --git a/lib/service/shared_prefs.dart b/lib/service/shared_prefs.dart index 9a916ba..01dba09 100644 --- a/lib/service/shared_prefs.dart +++ b/lib/service/shared_prefs.dart @@ -117,6 +117,13 @@ class SharedPrefs { _sharedPrefs.setString('photosDir', value); } + /// get/set application's cache directory + String get cacheDir => _sharedPrefs.getString('cacheDir') ?? ""; + + set cacheDir(String value) { + _sharedPrefs.setString('cacheDir', value); + } + /// get/set url MP4 String get urlMP4 => _sharedPrefs.getString('urlMP4') ?? ""; @@ -138,4 +145,12 @@ class SharedPrefs { set lastCalendarRefresh(String value) { _sharedPrefs.setString('key_lastCalendarRefresh', value); } + + /// Get/set userAgent + String get mobileUserAgent => + _sharedPrefs.getString('key_mobileUserAgent') ?? ""; + + set mobileUserAgent(String value) { + _sharedPrefs.setString('key_mobileUserAgent', value); + } } diff --git a/lib/ui/home.dart b/lib/ui/home.dart index 580550f..1cb8e37 100644 --- a/lib/ui/home.dart +++ b/lib/ui/home.dart @@ -5,10 +5,11 @@ import 'package:mobdr/main.dart'; import 'package:mobdr/ui/account/tab_account.dart'; import 'package:mobdr/ui/home/tab_home.dart'; import 'package:mobdr/ui/mp4/tab_mp4.dart'; -import 'package:mobdr/ui/sync/synchronization.dart'; +import 'package:mobdr/ui/sync/tab_synchro.dart'; import 'package:mobdr/config/constant.dart'; import 'package:mobdr/events.dart'; +import 'package:mobdr/service/plausible.dart'; class HomePage extends StatefulWidget { @override @@ -100,6 +101,8 @@ class _HomePageState extends State _pageController.jumpToPage(value); FocusScope.of(context).unfocus(); }); + // analytics + _trackPageView(value); } }, selectedFontSize: 8, @@ -138,4 +141,24 @@ class _HomePageState extends State ), ); } + + void _trackPageView(int index) { + String page = ''; + switch (index) { + case 0: + page = 'tab_home'; + break; + case 1: + page = 'tab_synchro'; + break; + case 2: + page = 'tab_mp4'; + break; + case 3: + page = 'tab_account'; + break; + } + + PlausibleUtil.addEvent(name: 'pageview', page: page); + } } diff --git a/lib/ui/home/photo_pick.dart b/lib/ui/home/photo_pick.dart deleted file mode 100644 index 53ff8fd..0000000 --- a/lib/ui/home/photo_pick.dart +++ /dev/null @@ -1,369 +0,0 @@ -/* -install plugin in pubspec.yaml -- image_picker => to pick image from storage or camera (https://pub.dev/packages/image_picker) - add this to ios Info.plist - NSPhotoLibraryUsageDescription - I need this permission to test upload photo - NSCameraUsageDescription - I need this permission to test upload photo - NSMicrophoneUsageDescription - I need this permission to test upload photo - - When using package image_picker: ^0.8.0+4, we should add this permission at AndroidManifest - android/app/src/debug/AndroidManifest.xml | android/app/src/main/AndroidManifest.xml | android/app/src/profile/AndroidManifest.xml - - -- permission_handler => to handle permission such as storage, camera (https://pub.dev/packages/permission_handler) -From above link, you should add this to the podfile if you are running on iOS: -post_install do |installer| - installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - flutter_additional_ios_build_settings(target) - - # You can enable the permissions needed here. For example to enable camera - # permission, just remove the `#` character in front so it looks like this: - # - # ## dart: PermissionGroup.camera - # 'PERMISSION_CAMERA=1' - # - # Preprocessor definitions can be found in: https://github.com/Baseflow/flutter-permission-handler/blob/master/permission_handler/ios/Classes/PermissionHandlerEnums.h - config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ - '$(inherited)', - - ## dart: PermissionGroup.calendar - # 'PERMISSION_EVENTS=1', - - ## dart: PermissionGroup.reminders - # 'PERMISSION_REMINDERS=1', - - ## dart: PermissionGroup.contacts - # 'PERMISSION_CONTACTS=1', - - ## dart: PermissionGroup.camera - 'PERMISSION_CAMERA=1', - - ## dart: PermissionGroup.microphone - # 'PERMISSION_MICROPHONE=1', - - ## dart: PermissionGroup.speech - # 'PERMISSION_SPEECH_RECOGNIZER=1', - - ## dart: PermissionGroup.photos - 'PERMISSION_PHOTOS=1', - - ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse] - # 'PERMISSION_LOCATION=1', - - ## dart: PermissionGroup.notification - # 'PERMISSION_NOTIFICATIONS=1', - - ## dart: PermissionGroup.mediaLibrary - # 'PERMISSION_MEDIA_LIBRARY=1', - - ## dart: PermissionGroup.sensors - # 'PERMISSION_SENSORS=1', - - ## dart: PermissionGroup.bluetooth - # 'PERMISSION_BLUETOOTH=1', - - ## dart: PermissionGroup.appTrackingTransparency - # 'PERMISSION_APP_TRACKING_TRANSPARENCY=1', - - ## dart: PermissionGroup.criticalAlerts - # 'PERMISSION_CRITICAL_ALERTS=1' - ] - - end - end -end - -we add some logic function so if the user press back or done with this pages, cache images will be deleted and not makes the storage full - -Don't forget to add all images and sound used in this pages at the pubspec.yaml - -*** IMPORTANT NOTES FOR IOS *** -Image Picker will crash if you pick image for a second times, this error only exist on iOS Simulator 14 globaly around the world but not error on the real device -If you want to use iOS Simulator, you need to downgrade and using iOS Simulator 13 -Follow this step to downgrade : -1. Xcode > Preferences -2. Select the "Components" tab. -3. Download and select Simulator 13 after the download is finish -4. Press "Check and Install Now". -5. After that, use Simulator 13 instead of simulator 14 - */ - -import 'dart:io'; -import 'package:flutter/foundation.dart'; -import 'package:universal_io/io.dart' as IO; - -import 'package:mobdr/ui/reusable/global_widget.dart'; -import 'package:flutter/material.dart'; -import 'package:fluttertoast/fluttertoast.dart'; -import 'package:permission_handler/permission_handler.dart'; - -import 'package:image_picker/image_picker.dart'; -import 'package:image_picker_android/image_picker_android.dart'; -import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; - -class PhotoPickPage extends StatefulWidget { - @override - _PhotoPickPageState createState() => _PhotoPickPageState(); -} - -class _PhotoPickPageState extends State { - // initialize global widget - final _globalWidget = GlobalWidget(); - - Color _color1 = Color(0xFF0181cc); - Color _color2 = Color(0xff777777); - - File? _image; - final _picker = ImagePicker(); - - dynamic _selectedFile; - - @override - void initState() { - super.initState(); - } - - @override - void dispose() { - if (!kIsWeb) { - if (_selectedFile != null && _selectedFile!.existsSync()) { - _selectedFile!.deleteSync(); - } - } - _selectedFile = null; - super.dispose(); - } - - Future requestPermission(Permission permission) async { - final result = await permission.request(); - return result; - } - - void _askPermissionCamera() { - requestPermission(Permission.camera).then(_onStatusRequestedCamera); - } - - void _askPermissionStorage() { - requestPermission(Permission.storage).then(_onStatusRequested); - } - - void _askPermissionPhotos() { - requestPermission(Permission.photos).then(_onStatusRequested); - } - - void _onStatusRequested(status) { - if (status != PermissionStatus.granted) { - if (IO.Platform.isIOS) { - openAppSettings(); - } else { - if (status == PermissionStatus.permanentlyDenied) { - openAppSettings(); - } - } - } else { - _getImage(ImageSource.gallery); - } - } - - void _onStatusRequestedCamera(status) { - if (status != PermissionStatus.granted) { - if (IO.Platform.isIOS) { - openAppSettings(); - } else { - if (status == PermissionStatus.permanentlyDenied) { - openAppSettings(); - } - } - } else { - _getImage(ImageSource.camera); - } - } - - void _getImage(ImageSource source) async { - final ImagePickerPlatform imagePickerImplementation = - ImagePickerPlatform.instance; - if (imagePickerImplementation is ImagePickerAndroid) { - imagePickerImplementation.useAndroidPhotoPicker = true; - } - final pickedFile = await _picker.pickImage( - source: source, maxWidth: 640, imageQuality: 100); - setState(() { - if (pickedFile != null) { - _image = File(pickedFile.path); - } - }); - - if (_image != null) { - this.setState(() { - if (!kIsWeb) { - if (_selectedFile != null && _selectedFile!.existsSync()) { - _selectedFile!.deleteSync(); - } - } - if (kIsWeb) { - _selectedFile = pickedFile; - } else { - _selectedFile = _image; - } - - _image = null; - }); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.white, - appBar: _globalWidget.globalAppBar(), - body: Stack( - children: [ - SingleChildScrollView( - padding: EdgeInsets.all(20), - child: Column( - children: [ - _getImageWidget(), - SizedBox(height: 30), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - (!kIsWeb) - ? GestureDetector( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.camera_alt, - color: _color2, - size: 40, - ), - SizedBox(width: 10), - Text('Camera'), - ], - ), - onTap: () { - _askPermissionCamera(); - }, - ) - : SizedBox.shrink(), - Container( - width: (!kIsWeb) ? 20 : 0, - ), - GestureDetector( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.photo, - color: _color2, - size: 40, - ), - SizedBox(width: 10), - Text('Gallery'), - ], - ), - onTap: () { - if (kIsWeb) { - _getImage(ImageSource.gallery); - } else { - if (IO.Platform.isIOS) { - _askPermissionPhotos(); - } else { - _askPermissionStorage(); - } - } - }, - ), - ], - ), - _buttonSave() - ], - ), - ) - ], - )); - } - - Widget _getImageWidget() { - if (_selectedFile != null) { - return (kIsWeb) - ? Image.network( - _selectedFile!.path, - width: (kIsWeb) ? 640 : MediaQuery.of(context).size.width - 16, - fit: BoxFit.fill, - ) - : Image.file( - _selectedFile!, - width: (kIsWeb) ? 640 : MediaQuery.of(context).size.width - 16, - fit: BoxFit.fill, - ); - } else { - return Image.asset( - 'assets/images/placeholder.jpg', - width: (kIsWeb) ? 250 : MediaQuery.of(context).size.width - 16, - fit: BoxFit.fill, - ); - } - } - - Widget _buttonSave() { - if (_selectedFile != null) { - return Container( - margin: EdgeInsets.fromLTRB(0, 50, 0, 0), - child: SizedBox( - child: TextButton( - style: ButtonStyle( - backgroundColor: MaterialStateProperty.resolveWith( - (Set states) => _color1, - ), - overlayColor: MaterialStateProperty.all(Colors.transparent), - shape: MaterialStateProperty.all(RoundedRectangleBorder( - borderRadius: BorderRadius.circular(3.0), - )), - ), - onPressed: () { - if (_selectedFile != null) { - if ((!kIsWeb)) { - // if run on android or ios, should check the file exist or not - if (_selectedFile!.existsSync()) { - Fluttertoast.showToast( - msg: 'Click save success', - toastLength: Toast.LENGTH_SHORT); - } - } else { - Fluttertoast.showToast( - msg: 'Click save success', - toastLength: Toast.LENGTH_SHORT); - } - } else { - Fluttertoast.showToast( - backgroundColor: Colors.red, - textColor: Colors.white, - msg: 'File not found', - fontSize: 13, - toastLength: Toast.LENGTH_SHORT); - } - }, - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 30, vertical: 5), - child: Text( - 'Save', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.white), - textAlign: TextAlign.center, - ), - )), - ), - ); - } else { - return Container(); - } - } -} diff --git a/lib/ui/mp4/tab_mp4.dart b/lib/ui/mp4/tab_mp4.dart index 925b609..48bc771 100644 --- a/lib/ui/mp4/tab_mp4.dart +++ b/lib/ui/mp4/tab_mp4.dart @@ -1,6 +1,12 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; +// #docregion platform_imports +// Import for Android features. +import 'package:webview_flutter_android/webview_flutter_android.dart'; +// Import for iOS features. +import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart'; +// #enddocregion platform_imports import 'package:mobdr/service/shared_prefs.dart'; import 'package:mobdr/main.dart'; @@ -13,7 +19,7 @@ class TabMP4Page extends StatefulWidget { class _TabMP4PageState extends State with AutomaticKeepAliveClientMixin { - late final WebViewController controller; + late final WebViewController _controller; late StreamSubscription sub; int loadingPercentage = 0; @@ -23,27 +29,76 @@ class _TabMP4PageState extends State void initState() { super.initState(); - controller = WebViewController() - ..setNavigationDelegate(NavigationDelegate( - onPageStarted: (url) { - setState(() { - loadingPercentage = 0; - }); - }, - onProgress: (progress) { - setState(() { - loadingPercentage = progress; - }); - }, - onPageFinished: (url) { - setState(() { - loadingPercentage = 100; - }); - }, - )) - ..loadRequest( - Uri.parse(url), + // #docregion platform_features + late final PlatformWebViewControllerCreationParams params; + if (WebViewPlatform.instance is WebKitWebViewPlatform) { + params = WebKitWebViewControllerCreationParams( + allowsInlineMediaPlayback: true, + mediaTypesRequiringUserAction: const {}, ); + } else { + params = const PlatformWebViewControllerCreationParams(); + } + + final WebViewController controller = + WebViewController.fromPlatformCreationParams(params); + // #enddocregion platform_features + + controller + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..setBackgroundColor(const Color(0x00000000)) + ..setNavigationDelegate( + NavigationDelegate( + onProgress: (int progress) { + debugPrint('WebView is loading (progress : $progress%)'); + }, + onPageStarted: (String url) { + debugPrint('Page started loading: $url'); + }, + onPageFinished: (String url) { + debugPrint('Page finished loading: $url'); + }, + onWebResourceError: (WebResourceError error) { + debugPrint(''' +Page resource error: + code: ${error.errorCode} + description: ${error.description} + errorType: ${error.errorType} + isForMainFrame: ${error.isForMainFrame} + '''); + }, + onNavigationRequest: (NavigationRequest request) { + if (request.url.startsWith('https://www.youtube.com/')) { + debugPrint('blocking navigation to ${request.url}'); + return NavigationDecision.prevent; + } + debugPrint('allowing navigation to ${request.url}'); + return NavigationDecision.navigate; + }, + onUrlChange: (UrlChange change) { + debugPrint('url change to ${change.url}'); + }, + ), + ) + ..addJavaScriptChannel( + 'Toaster', + onMessageReceived: (JavaScriptMessage message) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(message.message)), + ); + }, + ) + ..loadRequest(Uri.parse(url)); + + // #docregion platform_features + if (controller.platform is AndroidWebViewController) { + AndroidWebViewController.enableDebugging(true); + (controller.platform as AndroidWebViewController) + .setMediaPlaybackRequiresUserGesture(false); + } + // #enddocregion platform_features + + _controller = controller; sub = eventBus.on().listen((e) { setState(() { @@ -67,7 +122,7 @@ class _TabMP4PageState extends State child: Stack( children: [ WebViewWidget( - controller: controller, + controller: _controller, ), if (loadingPercentage < 100) LinearProgressIndicator( diff --git a/lib/ui/splash_screen.dart b/lib/ui/splash_screen.dart index 32932a2..5c771fd 100644 --- a/lib/ui/splash_screen.dart +++ b/lib/ui/splash_screen.dart @@ -4,6 +4,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:mobdr/service/shared_prefs.dart'; +import 'package:mobdr/service/plausible.dart'; + import 'package:mobdr/config/constant.dart'; import 'package:mobdr/ui/onboarding.dart'; import 'package:mobdr/ui/home.dart'; @@ -28,9 +30,11 @@ class _SplashScreenPageState extends State { if (SharedPrefs().onboarding == 0) { SharedPrefs().onboarding = 1; + PlausibleUtil.addEvent(name: 'pageview', page: 'onboarding'); Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => OnBoardingPage())); } else { + PlausibleUtil.addEvent(name: 'pageview', page: 'tab_home'); Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => HomePage())); } diff --git a/lib/ui/sync/synchronization.dart b/lib/ui/sync/tab_synchro.dart similarity index 68% rename from lib/ui/sync/synchronization.dart rename to lib/ui/sync/tab_synchro.dart index 2df5a6a..18ebec5 100644 --- a/lib/ui/sync/synchronization.dart +++ b/lib/ui/sync/tab_synchro.dart @@ -146,7 +146,7 @@ class _SynchronizationPageState extends State } } - Future _clearVisitPhotoCache() async { + Future _cleanVisitPhotoDir() async { List _visitPhotoListTokeep; Directory photosDir = Directory(SharedPrefs().photosDir); @@ -174,6 +174,26 @@ class _SynchronizationPageState extends State } } + Future _clearPhotoCache() async { + Directory cacheDir = Directory(SharedPrefs().cacheDir); + + // Get a list of all files in the "cache" directory + final List files = await cacheDir.list().toList(); + + // Check each file in the directory + for (FileSystemEntity file in files) { + // Get the file name + String fileName = file.path.split('/').last; + + // Check if the file name starts with "CAP" + if (fileName.startsWith("CAP")) { + // Delete the file + await file.delete(); + print('Deleted file: $fileName'); + } + } + } + void startSync() async { // disable navigation bar buttons eventBus.fire(SynchronizationEvent(true)); @@ -205,7 +225,10 @@ class _SynchronizationPageState extends State // supprimer les visites "ancienne" sans photo !! // deletes photos that are no longer in any visits - await _clearVisitPhotoCache(); + await _cleanVisitPhotoDir(); + + // deleting photos in the cache directory + await _clearPhotoCache(); // last synchronization date SharedPrefs().lastCalendarRefresh = @@ -234,89 +257,90 @@ class _SynchronizationPageState extends State appBar: AppBar( title: Text('Data synchronization'), ), - body: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - SizedBox(height: 20), - Center( - child: Container( - width: 100, - height: 100, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: _isInternetConnexion - ? Colors.blue - : Colors - .red, // Modifier la couleur en fonction de la connexion Internet - ), - child: IconButton( - icon: AnimatedBuilder( - animation: _animationController, - builder: (BuildContext context, Widget? child) { - return Transform.rotate( - angle: _rotationAnimation.value * 2.0 * 3.14, - child: Icon( - _isInternetConnexion - ? Icons.refresh - : Icons - .signal_wifi_off, // Modifier l'icône en fonction de la connexion Internet - color: Colors.white, - size: 70, - ), - ); - }, + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: Container( + width: 100, + height: 100, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: _isInternetConnexion + ? Colors.blue + : Colors + .red, // Modifier la couleur en fonction de la connexion Internet + ), + child: IconButton( + icon: AnimatedBuilder( + animation: _animationController, + builder: (BuildContext context, Widget? child) { + return Transform.rotate( + angle: _rotationAnimation.value * 2.0 * 3.14, + child: Icon( + _isInternetConnexion + ? Icons.refresh + : Icons + .signal_wifi_off, // Modifier l'icône en fonction de la connexion Internet + color: Colors.white, + size: 70, + ), + ); + }, + ), + onPressed: _isSyncing ? null : doSync, ), - onPressed: _isSyncing ? null : doSync, ), ), - ), - SizedBox(height: 20), - Text( - !_isInternetConnexion - ? "No internet connection ..." - : (SharedPrefs().lastCalendarRefresh.isNotEmpty - ? SharedPrefs().lastCalendarRefresh - : "Never"), - style: TextStyle( - fontSize: 16, - color: !_isInternetConnexion - ? Colors.red - : Colors - .black, // Modifier la couleur du texte en fonction de votre préférence - fontWeight: FontWeight.bold, + SizedBox(height: 10), + Text( + !_isInternetConnexion + ? "No internet connection ..." + : (SharedPrefs().lastCalendarRefresh.isNotEmpty + ? SharedPrefs().lastCalendarRefresh + : "Never"), + style: TextStyle( + fontSize: 16, + color: !_isInternetConnexion + ? Colors.red + : Colors + .black, // Modifier la couleur du texte en fonction de votre préférence + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, ), - textAlign: TextAlign.center, - ), - SizedBox(height: 40), - SyncItem( - icon: Icons.business, - title: 'Backoffice', - description: 'Synchronisation des données du backoffice', - isSyncing: _isSyncing, - syncCompleted: _syncCompleted, - isError: _syncCompleted && !_backofficeSyncCompleted, - ), - SizedBox(height: 20), - SyncItem( - icon: Icons.photo, - title: 'Photos', - description: 'Synchronisation des photos', - isSyncing: _isSyncing, - syncCompleted: _syncCompleted, - isError: _syncCompleted && !_photosSyncCompleted, - ), - SizedBox(height: 20), - SyncItem( - icon: Icons.warning, - title: 'Log', - description: 'Synchronisation des journaux d\'activité', - isSyncing: _isSyncing, - syncCompleted: _syncCompleted, - isError: _syncCompleted && !_logSyncCompleted, - ), - ], + SizedBox(height: 30), + SyncItem( + icon: Icons.business, + title: 'Backoffice', + description: 'Synchronisation des données du backoffice', + isSyncing: _isSyncing, + syncCompleted: _syncCompleted, + isError: _syncCompleted && !_backofficeSyncCompleted, + ), + SizedBox(height: 20), + SyncItem( + icon: Icons.photo, + title: 'Photos', + description: 'Synchronisation des photos', + isSyncing: _isSyncing, + syncCompleted: _syncCompleted, + isError: _syncCompleted && !_photosSyncCompleted, + ), + SizedBox(height: 20), + SyncItem( + icon: Icons.warning, + title: 'Log', + description: 'Synchronisation des journaux d\'activité', + isSyncing: _isSyncing, + syncCompleted: _syncCompleted, + isError: _syncCompleted && !_logSyncCompleted, + ), + ], + ), ), ), ); diff --git a/lib/ui/home/photo_camera.dart b/lib/ui/visit/photo_camera.dart similarity index 100% rename from lib/ui/home/photo_camera.dart rename to lib/ui/visit/photo_camera.dart diff --git a/lib/ui/visit/visit_photo_typology.dart b/lib/ui/visit/visit_photo_typology.dart index aa127a2..c872124 100644 --- a/lib/ui/visit/visit_photo_typology.dart +++ b/lib/ui/visit/visit_photo_typology.dart @@ -34,8 +34,6 @@ class _VisitPhotoTypologyPageState extends State { )); Navigator.push(context, route).then(onGoBack); }, - - /// TODO objectbox.noteBox.remove(PhotoTypology[index].id), child: Container( decoration: const BoxDecoration( border: Border(bottom: BorderSide(color: Colors.black12)), diff --git a/lib/ui/visit/visit_photo_typology_list.dart b/lib/ui/visit/visit_photo_typology_list.dart index 51d154f..5fac990 100644 --- a/lib/ui/visit/visit_photo_typology_list.dart +++ b/lib/ui/visit/visit_photo_typology_list.dart @@ -19,12 +19,10 @@ import 'package:mobdr/main.dart'; import 'package:mobdr/db/box_visit_photo.dart'; import 'package:mobdr/model/visit_model.dart'; import 'package:mobdr/ui/reusable/global_widget.dart'; -import 'package:mobdr/ui/home/photo_camera.dart'; +import 'package:mobdr/ui/visit/photo_camera.dart'; import 'package:mobdr/ui/visit/visit_photo_typology_detail.dart'; import 'package:mobdr/events.dart'; -// TODO Il faut supprimer les possibles photos du répertoire cache ! - extension FileNameExtension on File { String getFileName() { String fileName = path.split('/').last; diff --git a/pubspec.lock b/pubspec.lock index a4c58d5..7208f01 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -157,18 +157,18 @@ packages: dependency: "direct main" description: name: camera - sha256: "7afc256902062cab191540c09908b98bc71e93d5e20b6486dbee51aa7731e9b2" + sha256: ebebead3d5ec3d148249331d751d462d7e8c98102b8830a9b45ec96a2bd4333f url: "https://pub.dev" source: hosted - version: "0.10.4" + version: "0.10.5+2" camera_android: dependency: transitive description: name: camera_android - sha256: "772c111c78f31f868b98dbf6dbeda8d6ff77acea773a92ea5705ee2f7949ebfb" + sha256: f83e406d34f5faa80bf0f5c3beee4b4c11da94a94e9621c1bb8e312988621b4b url: "https://pub.dev" source: hosted - version: "0.10.5" + version: "0.10.8+2" camera_avfoundation: dependency: transitive description: @@ -181,10 +181,10 @@ packages: dependency: transitive description: name: camera_platform_interface - sha256: "00d972adee2e8a282b4d7445e8e694aa1dc0c36b70455b99afa96fbf5e814119" + sha256: "60fa0bb62a4f3bf3a7c413e31e4cd01b69c779ccc8e4668904a24581b86c316b" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.5.1" camera_web: dependency: transitive description: @@ -245,10 +245,10 @@ packages: dependency: "direct main" description: name: connectivity_plus - sha256: b74247fad72c171381dbe700ca17da24deac637ab6d43c343b42867acb95c991 + sha256: "8599ae9edca5ff96163fca3e36f8e481ea917d1e71cdad912c084b5579913f34" url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "4.0.1" connectivity_plus_platform_interface: dependency: transitive description: @@ -622,7 +622,7 @@ packages: source: hosted version: "4.8.0" logger: - dependency: transitive + dependency: "direct main" description: name: logger sha256: db2ff852ed77090ba9f62d3611e4208a3d11dfa35991a81ae724c113fcb3e3f7 @@ -1254,10 +1254,10 @@ packages: dependency: "direct main" description: name: webview_flutter - sha256: "1a37bdbaaf5fbe09ad8579ab09ecfd473284ce482f900b5aea27cf834386a567" + sha256: "5604dac1178680a34fbe4a08c7b69ec42cca6601dc300009ec9ff69bef284cc2" url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "4.2.1" webview_flutter_android: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d225602..ef9bda2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,20 +44,27 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: 1.0.5 fluttertoast: 8.1.3 + package_info_plus: ^3.0.3 + flutter_bloc: 8.1.2 + flutter_html: 3.0.0-alpha.6 + # https://pub.dev/packages/wakelock wakelock: ^0.6.2 + + # https://pub.dev/packages/ shimmer: 2.0.0 # https://pub.dev/packages/image_picker image_picker: ^0.8.7+4 # https://pub.dev/packages/camera - camera: ^0.10.4 + camera: ^0.10.5+2 permission_handler: 10.2.0 + image: ^4.0.15 # https://pub.dev/packages/super_tag_editor @@ -69,21 +76,25 @@ dependencies: intl: 0.17.0 carousel_slider: 4.2.1 + cached_network_image: 3.2.3 + get_it: ^7.2.0 # https://pub.dev/packages/shared_preferences shared_preferences: ^2.1.0 timelines: ^0.1.0 + universal_io: 2.2.0 + xml: ^6.2.2 # https://pub.dev/packages/device_info_plus/ device_info_plus: ^8.2.0 # https://pub.dev/packages/webview_flutter/ - webview_flutter: ^4.2.0 + webview_flutter: ^4.2.1 # https://pub.dev/packages/event_bus_plus/ event_bus_plus: ^0.6.1 @@ -92,8 +103,11 @@ dependencies: badges: ^3.1.1 # https://pub.dev/packages/connectivity_plus - connectivity_plus: ^3.0.6 - + connectivity_plus: ^4.0.1 + + # https://pub.dev/packages/logger + logger: ^1.3.0 + dev_dependencies: flutter_test: sdk: flutter