refactor:log, analytics, main
parent
d01f8e84b4
commit
37cd9272e7
|
|
@ -94,7 +94,7 @@ EXTERNAL SOURCES:
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
camera_avfoundation: 3125e8cd1a4387f6f31c6c63abb8a55892a9eeeb
|
camera_avfoundation: 3125e8cd1a4387f6f31c6c63abb8a55892a9eeeb
|
||||||
connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
|
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
|
||||||
device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed
|
device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed
|
||||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||||
fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0
|
fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
abstract class Routes {
|
|
||||||
static const INITIAL = '/';
|
|
||||||
static const LOGIN = '/login';
|
|
||||||
static const CAMERA = '/camera';
|
|
||||||
static const LIST = '/list';
|
|
||||||
}
|
|
||||||
|
|
@ -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<int> event(
|
||||||
|
{String name = "pageview",
|
||||||
|
String referrer = "",
|
||||||
|
String page = "",
|
||||||
|
Map<String, String> 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<bool> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
289
lib/main.dart
289
lib/main.dart
|
|
@ -1,36 +1,32 @@
|
||||||
// ignore_for_file: prefer_const_constructors
|
// ignore_for_file: prefer_const_constructors
|
||||||
|
|
||||||
import 'dart:ui';
|
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/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
|
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
|
|
||||||
import 'objectbox.dart';
|
import 'objectbox.dart';
|
||||||
import 'package:wakelock/wakelock.dart';
|
import 'package:wakelock/wakelock.dart';
|
||||||
|
|
||||||
import 'package:event_bus_plus/event_bus_plus.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/config/constant.dart';
|
||||||
|
import 'package:mobdr/core/routes/plausible_tracker.dart';
|
||||||
import 'package:mobdr/cubit/language/language_cubit.dart';
|
import 'package:mobdr/cubit/language/language_cubit.dart';
|
||||||
import 'package:mobdr/cubit/language/app_localizations.dart';
|
import 'package:mobdr/cubit/language/app_localizations.dart';
|
||||||
import 'package:mobdr/cubit/language/initial_language.dart';
|
import 'package:mobdr/cubit/language/initial_language.dart';
|
||||||
import 'package:mobdr/service/shared_prefs.dart';
|
import 'package:mobdr/service/shared_prefs.dart';
|
||||||
import 'package:mobdr/ui/splash_screen.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.
|
/// Provides access to the ObjectBox Store throughout the app.
|
||||||
late ObjectBox objectbox;
|
late ObjectBox objectbox;
|
||||||
|
late PlausibleTracker plausible;
|
||||||
final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
|
|
||||||
|
|
||||||
final EventBus eventBus = EventBus();
|
final EventBus eventBus = EventBus();
|
||||||
|
|
||||||
|
|
@ -39,12 +35,39 @@ Future<void> main() async {
|
||||||
// to store the database in.
|
// to store the database in.
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
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();
|
objectbox = await ObjectBox.create();
|
||||||
|
|
||||||
/// Log
|
// initialize shared preferences
|
||||||
objectbox.addLog('LOG', 'MOBDR', 'Ouverture application ', 0);
|
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])
|
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
|
||||||
.then((_) {
|
.then((_) {
|
||||||
|
|
@ -53,7 +76,7 @@ Future<void> main() async {
|
||||||
Wakelock.enable();
|
Wakelock.enable();
|
||||||
|
|
||||||
eventBus.on().listen((event) {
|
eventBus.on().listen((event) {
|
||||||
print('${DateTime.now()} Event: $event');
|
LoggerUtil.logVerbose('${DateTime.now()} Event: $event');
|
||||||
});
|
});
|
||||||
|
|
||||||
runApp(MyApp());
|
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.
|
// This widget is the root of your application.
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
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
|
// Initialize all bloc provider used on this entire application here
|
||||||
return MultiBlocProvider(
|
return MultiBlocProvider(
|
||||||
providers: [
|
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<FileSystemEntity> 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<void> initPlatformState() async {
|
|
||||||
var deviceData = <String, dynamic>{};
|
|
||||||
|
|
||||||
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 = <String, dynamic>{
|
|
||||||
'Error:': 'Failed to get platform version.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _readAndroidBuildData(AndroidDeviceInfo build) {
|
|
||||||
return <String, dynamic>{
|
|
||||||
'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<String, dynamic> _readIosDeviceInfo(IosDeviceInfo data) {
|
|
||||||
return <String, dynamic>{
|
|
||||||
'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<String, dynamic> _readLinuxDeviceInfo(LinuxDeviceInfo data) {
|
|
||||||
return <String, dynamic>{
|
|
||||||
'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<String, dynamic> _readWebBrowserInfo(WebBrowserInfo data) {
|
|
||||||
return <String, dynamic>{
|
|
||||||
'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<String, dynamic> _readMacOsDeviceInfo(MacOsDeviceInfo data) {
|
|
||||||
return <String, dynamic>{
|
|
||||||
'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<String, dynamic> _readWindowsDeviceInfo(WindowsDeviceInfo data) {
|
|
||||||
return <String, dynamic>{
|
|
||||||
'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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
Future<String> 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';
|
||||||
|
}
|
||||||
|
|
@ -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<void> initPlatformState() async {
|
||||||
|
var deviceData = <String, dynamic>{};
|
||||||
|
|
||||||
|
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 = <String, dynamic>{
|
||||||
|
'Error:': 'Failed to get platform version.'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _readAndroidBuildData(AndroidDeviceInfo build) {
|
||||||
|
return <String, dynamic>{
|
||||||
|
'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<String, dynamic> _readIosDeviceInfo(IosDeviceInfo data) {
|
||||||
|
return <String, dynamic>{
|
||||||
|
'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<String, dynamic> _readLinuxDeviceInfo(LinuxDeviceInfo data) {
|
||||||
|
return <String, dynamic>{
|
||||||
|
'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<String, dynamic> _readWebBrowserInfo(WebBrowserInfo data) {
|
||||||
|
return <String, dynamic>{
|
||||||
|
'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<String, dynamic> _readMacOsDeviceInfo(MacOsDeviceInfo data) {
|
||||||
|
return <String, dynamic>{
|
||||||
|
'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<String, dynamic> _readWindowsDeviceInfo(WindowsDeviceInfo data) {
|
||||||
|
return <String, dynamic>{
|
||||||
|
'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<String, dynamic> deviceData) {
|
||||||
|
String systemName = deviceData['systemName'];
|
||||||
|
String systemVersion = deviceData['systemVersion'];
|
||||||
|
String model = deviceData['model'];
|
||||||
|
|
||||||
|
return '$systemName $systemVersion; $model';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
|
import 'package:mobdr/service/shared_prefs.dart';
|
||||||
|
|
||||||
|
class directories {
|
||||||
|
Future<void> 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<FileSystemEntity> 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}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<void> 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<String, String>? props,
|
||||||
|
}) {
|
||||||
|
PlausibleUtil.plausible?.event(
|
||||||
|
name: name,
|
||||||
|
page: page,
|
||||||
|
referrer: referrer ?? '',
|
||||||
|
props: props ?? {},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -117,6 +117,13 @@ class SharedPrefs {
|
||||||
_sharedPrefs.setString('photosDir', value);
|
_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
|
/// get/set url MP4
|
||||||
String get urlMP4 => _sharedPrefs.getString('urlMP4') ?? "";
|
String get urlMP4 => _sharedPrefs.getString('urlMP4') ?? "";
|
||||||
|
|
||||||
|
|
@ -138,4 +145,12 @@ class SharedPrefs {
|
||||||
set lastCalendarRefresh(String value) {
|
set lastCalendarRefresh(String value) {
|
||||||
_sharedPrefs.setString('key_lastCalendarRefresh', 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,11 @@ import 'package:mobdr/main.dart';
|
||||||
import 'package:mobdr/ui/account/tab_account.dart';
|
import 'package:mobdr/ui/account/tab_account.dart';
|
||||||
import 'package:mobdr/ui/home/tab_home.dart';
|
import 'package:mobdr/ui/home/tab_home.dart';
|
||||||
import 'package:mobdr/ui/mp4/tab_mp4.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/config/constant.dart';
|
||||||
import 'package:mobdr/events.dart';
|
import 'package:mobdr/events.dart';
|
||||||
|
import 'package:mobdr/service/plausible.dart';
|
||||||
|
|
||||||
class HomePage extends StatefulWidget {
|
class HomePage extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
|
|
@ -100,6 +101,8 @@ class _HomePageState extends State<HomePage>
|
||||||
_pageController.jumpToPage(value);
|
_pageController.jumpToPage(value);
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
});
|
});
|
||||||
|
// analytics
|
||||||
|
_trackPageView(value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
selectedFontSize: 8,
|
selectedFontSize: 8,
|
||||||
|
|
@ -138,4 +141,24 @@ class _HomePageState extends State<HomePage>
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
<key>NSPhotoLibraryUsageDescription</key>
|
|
||||||
<string>I need this permission to test upload photo</string>
|
|
||||||
<key>NSCameraUsageDescription</key>
|
|
||||||
<string>I need this permission to test upload photo</string>
|
|
||||||
<key>NSMicrophoneUsageDescription</key>
|
|
||||||
<string>I need this permission to test upload photo</string>
|
|
||||||
|
|
||||||
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
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
|
||||||
|
|
||||||
- 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<PhotoPickPage> {
|
|
||||||
// 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: <Widget>[
|
|
||||||
SingleChildScrollView(
|
|
||||||
padding: EdgeInsets.all(20),
|
|
||||||
child: Column(
|
|
||||||
children: <Widget>[
|
|
||||||
_getImageWidget(),
|
|
||||||
SizedBox(height: 30),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
(!kIsWeb)
|
|
||||||
? GestureDetector(
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: <Widget>[
|
|
||||||
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: <Widget>[
|
|
||||||
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<Color>(
|
|
||||||
(Set<MaterialState> 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:webview_flutter/webview_flutter.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/service/shared_prefs.dart';
|
||||||
import 'package:mobdr/main.dart';
|
import 'package:mobdr/main.dart';
|
||||||
|
|
@ -13,7 +19,7 @@ class TabMP4Page extends StatefulWidget {
|
||||||
|
|
||||||
class _TabMP4PageState extends State<TabMP4Page>
|
class _TabMP4PageState extends State<TabMP4Page>
|
||||||
with AutomaticKeepAliveClientMixin {
|
with AutomaticKeepAliveClientMixin {
|
||||||
late final WebViewController controller;
|
late final WebViewController _controller;
|
||||||
late StreamSubscription sub;
|
late StreamSubscription sub;
|
||||||
|
|
||||||
int loadingPercentage = 0;
|
int loadingPercentage = 0;
|
||||||
|
|
@ -23,27 +29,76 @@ class _TabMP4PageState extends State<TabMP4Page>
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
controller = WebViewController()
|
// #docregion platform_features
|
||||||
..setNavigationDelegate(NavigationDelegate(
|
late final PlatformWebViewControllerCreationParams params;
|
||||||
onPageStarted: (url) {
|
if (WebViewPlatform.instance is WebKitWebViewPlatform) {
|
||||||
setState(() {
|
params = WebKitWebViewControllerCreationParams(
|
||||||
loadingPercentage = 0;
|
allowsInlineMediaPlayback: true,
|
||||||
});
|
mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
|
||||||
},
|
|
||||||
onProgress: (progress) {
|
|
||||||
setState(() {
|
|
||||||
loadingPercentage = progress;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onPageFinished: (url) {
|
|
||||||
setState(() {
|
|
||||||
loadingPercentage = 100;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
))
|
|
||||||
..loadRequest(
|
|
||||||
Uri.parse(url),
|
|
||||||
);
|
);
|
||||||
|
} 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<UrlEvent>().listen((e) {
|
sub = eventBus.on<UrlEvent>().listen((e) {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|
@ -67,7 +122,7 @@ class _TabMP4PageState extends State<TabMP4Page>
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
WebViewWidget(
|
WebViewWidget(
|
||||||
controller: controller,
|
controller: _controller,
|
||||||
),
|
),
|
||||||
if (loadingPercentage < 100)
|
if (loadingPercentage < 100)
|
||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:mobdr/service/shared_prefs.dart';
|
import 'package:mobdr/service/shared_prefs.dart';
|
||||||
|
import 'package:mobdr/service/plausible.dart';
|
||||||
|
|
||||||
import 'package:mobdr/config/constant.dart';
|
import 'package:mobdr/config/constant.dart';
|
||||||
import 'package:mobdr/ui/onboarding.dart';
|
import 'package:mobdr/ui/onboarding.dart';
|
||||||
import 'package:mobdr/ui/home.dart';
|
import 'package:mobdr/ui/home.dart';
|
||||||
|
|
@ -28,9 +30,11 @@ class _SplashScreenPageState extends State<SplashScreenPage> {
|
||||||
|
|
||||||
if (SharedPrefs().onboarding == 0) {
|
if (SharedPrefs().onboarding == 0) {
|
||||||
SharedPrefs().onboarding = 1;
|
SharedPrefs().onboarding = 1;
|
||||||
|
PlausibleUtil.addEvent(name: 'pageview', page: 'onboarding');
|
||||||
Navigator.pushReplacement(context,
|
Navigator.pushReplacement(context,
|
||||||
MaterialPageRoute(builder: (context) => OnBoardingPage()));
|
MaterialPageRoute(builder: (context) => OnBoardingPage()));
|
||||||
} else {
|
} else {
|
||||||
|
PlausibleUtil.addEvent(name: 'pageview', page: 'tab_home');
|
||||||
Navigator.pushReplacement(
|
Navigator.pushReplacement(
|
||||||
context, MaterialPageRoute(builder: (context) => HomePage()));
|
context, MaterialPageRoute(builder: (context) => HomePage()));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ class _SynchronizationPageState extends State<SynchronizationPage>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _clearVisitPhotoCache() async {
|
Future<void> _cleanVisitPhotoDir() async {
|
||||||
List<VisitPhoto> _visitPhotoListTokeep;
|
List<VisitPhoto> _visitPhotoListTokeep;
|
||||||
Directory photosDir = Directory(SharedPrefs().photosDir);
|
Directory photosDir = Directory(SharedPrefs().photosDir);
|
||||||
|
|
||||||
|
|
@ -174,6 +174,26 @@ class _SynchronizationPageState extends State<SynchronizationPage>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _clearPhotoCache() async {
|
||||||
|
Directory cacheDir = Directory(SharedPrefs().cacheDir);
|
||||||
|
|
||||||
|
// Get a list of all files in the "cache" directory
|
||||||
|
final List<FileSystemEntity> 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 {
|
void startSync() async {
|
||||||
// disable navigation bar buttons
|
// disable navigation bar buttons
|
||||||
eventBus.fire(SynchronizationEvent(true));
|
eventBus.fire(SynchronizationEvent(true));
|
||||||
|
|
@ -205,7 +225,10 @@ class _SynchronizationPageState extends State<SynchronizationPage>
|
||||||
// supprimer les visites "ancienne" sans photo !!
|
// supprimer les visites "ancienne" sans photo !!
|
||||||
|
|
||||||
// deletes photos that are no longer in any visits
|
// deletes photos that are no longer in any visits
|
||||||
await _clearVisitPhotoCache();
|
await _cleanVisitPhotoDir();
|
||||||
|
|
||||||
|
// deleting photos in the cache directory
|
||||||
|
await _clearPhotoCache();
|
||||||
|
|
||||||
// last synchronization date
|
// last synchronization date
|
||||||
SharedPrefs().lastCalendarRefresh =
|
SharedPrefs().lastCalendarRefresh =
|
||||||
|
|
@ -234,12 +257,12 @@ class _SynchronizationPageState extends State<SynchronizationPage>
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('Data synchronization'),
|
title: Text('Data synchronization'),
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: SingleChildScrollView(
|
||||||
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 20),
|
|
||||||
Center(
|
Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 100,
|
width: 100,
|
||||||
|
|
@ -272,7 +295,7 @@ class _SynchronizationPageState extends State<SynchronizationPage>
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 20),
|
SizedBox(height: 10),
|
||||||
Text(
|
Text(
|
||||||
!_isInternetConnexion
|
!_isInternetConnexion
|
||||||
? "No internet connection ..."
|
? "No internet connection ..."
|
||||||
|
|
@ -289,7 +312,7 @@ class _SynchronizationPageState extends State<SynchronizationPage>
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
SizedBox(height: 40),
|
SizedBox(height: 30),
|
||||||
SyncItem(
|
SyncItem(
|
||||||
icon: Icons.business,
|
icon: Icons.business,
|
||||||
title: 'Backoffice',
|
title: 'Backoffice',
|
||||||
|
|
@ -319,6 +342,7 @@ class _SynchronizationPageState extends State<SynchronizationPage>
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -34,8 +34,6 @@ class _VisitPhotoTypologyPageState extends State<VisitPhotoTypologyPage> {
|
||||||
));
|
));
|
||||||
Navigator.push(context, route).then(onGoBack);
|
Navigator.push(context, route).then(onGoBack);
|
||||||
},
|
},
|
||||||
|
|
||||||
/// TODO objectbox.noteBox.remove(PhotoTypology[index].id),
|
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
border: Border(bottom: BorderSide(color: Colors.black12)),
|
border: Border(bottom: BorderSide(color: Colors.black12)),
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,10 @@ import 'package:mobdr/main.dart';
|
||||||
import 'package:mobdr/db/box_visit_photo.dart';
|
import 'package:mobdr/db/box_visit_photo.dart';
|
||||||
import 'package:mobdr/model/visit_model.dart';
|
import 'package:mobdr/model/visit_model.dart';
|
||||||
import 'package:mobdr/ui/reusable/global_widget.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/ui/visit/visit_photo_typology_detail.dart';
|
||||||
import 'package:mobdr/events.dart';
|
import 'package:mobdr/events.dart';
|
||||||
|
|
||||||
// TODO Il faut supprimer les possibles photos du répertoire cache !
|
|
||||||
|
|
||||||
extension FileNameExtension on File {
|
extension FileNameExtension on File {
|
||||||
String getFileName() {
|
String getFileName() {
|
||||||
String fileName = path.split('/').last;
|
String fileName = path.split('/').last;
|
||||||
|
|
|
||||||
22
pubspec.lock
22
pubspec.lock
|
|
@ -157,18 +157,18 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: camera
|
name: camera
|
||||||
sha256: "7afc256902062cab191540c09908b98bc71e93d5e20b6486dbee51aa7731e9b2"
|
sha256: ebebead3d5ec3d148249331d751d462d7e8c98102b8830a9b45ec96a2bd4333f
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.10.4"
|
version: "0.10.5+2"
|
||||||
camera_android:
|
camera_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: camera_android
|
name: camera_android
|
||||||
sha256: "772c111c78f31f868b98dbf6dbeda8d6ff77acea773a92ea5705ee2f7949ebfb"
|
sha256: f83e406d34f5faa80bf0f5c3beee4b4c11da94a94e9621c1bb8e312988621b4b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.10.5"
|
version: "0.10.8+2"
|
||||||
camera_avfoundation:
|
camera_avfoundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -181,10 +181,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: camera_platform_interface
|
name: camera_platform_interface
|
||||||
sha256: "00d972adee2e8a282b4d7445e8e694aa1dc0c36b70455b99afa96fbf5e814119"
|
sha256: "60fa0bb62a4f3bf3a7c413e31e4cd01b69c779ccc8e4668904a24581b86c316b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.5.1"
|
||||||
camera_web:
|
camera_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -245,10 +245,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: connectivity_plus
|
name: connectivity_plus
|
||||||
sha256: b74247fad72c171381dbe700ca17da24deac637ab6d43c343b42867acb95c991
|
sha256: "8599ae9edca5ff96163fca3e36f8e481ea917d1e71cdad912c084b5579913f34"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.6"
|
version: "4.0.1"
|
||||||
connectivity_plus_platform_interface:
|
connectivity_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -622,7 +622,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.8.0"
|
version: "4.8.0"
|
||||||
logger:
|
logger:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: logger
|
name: logger
|
||||||
sha256: db2ff852ed77090ba9f62d3611e4208a3d11dfa35991a81ae724c113fcb3e3f7
|
sha256: db2ff852ed77090ba9f62d3611e4208a3d11dfa35991a81ae724c113fcb3e3f7
|
||||||
|
|
@ -1254,10 +1254,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: webview_flutter
|
name: webview_flutter
|
||||||
sha256: "1a37bdbaaf5fbe09ad8579ab09ecfd473284ce482f900b5aea27cf834386a567"
|
sha256: "5604dac1178680a34fbe4a08c7b69ec42cca6601dc300009ec9ff69bef284cc2"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.0"
|
version: "4.2.1"
|
||||||
webview_flutter_android:
|
webview_flutter_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
20
pubspec.yaml
20
pubspec.yaml
|
|
@ -44,20 +44,27 @@ dependencies:
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: 1.0.5
|
cupertino_icons: 1.0.5
|
||||||
fluttertoast: 8.1.3
|
fluttertoast: 8.1.3
|
||||||
|
|
||||||
package_info_plus: ^3.0.3
|
package_info_plus: ^3.0.3
|
||||||
|
|
||||||
flutter_bloc: 8.1.2
|
flutter_bloc: 8.1.2
|
||||||
|
|
||||||
flutter_html: 3.0.0-alpha.6
|
flutter_html: 3.0.0-alpha.6
|
||||||
|
|
||||||
|
# https://pub.dev/packages/wakelock
|
||||||
wakelock: ^0.6.2
|
wakelock: ^0.6.2
|
||||||
|
|
||||||
|
# https://pub.dev/packages/
|
||||||
shimmer: 2.0.0
|
shimmer: 2.0.0
|
||||||
|
|
||||||
# https://pub.dev/packages/image_picker
|
# https://pub.dev/packages/image_picker
|
||||||
image_picker: ^0.8.7+4
|
image_picker: ^0.8.7+4
|
||||||
|
|
||||||
# https://pub.dev/packages/camera
|
# https://pub.dev/packages/camera
|
||||||
camera: ^0.10.4
|
camera: ^0.10.5+2
|
||||||
|
|
||||||
permission_handler: 10.2.0
|
permission_handler: 10.2.0
|
||||||
|
|
||||||
image: ^4.0.15
|
image: ^4.0.15
|
||||||
|
|
||||||
# https://pub.dev/packages/super_tag_editor
|
# https://pub.dev/packages/super_tag_editor
|
||||||
|
|
@ -69,21 +76,25 @@ dependencies:
|
||||||
intl: 0.17.0
|
intl: 0.17.0
|
||||||
|
|
||||||
carousel_slider: 4.2.1
|
carousel_slider: 4.2.1
|
||||||
|
|
||||||
cached_network_image: 3.2.3
|
cached_network_image: 3.2.3
|
||||||
|
|
||||||
get_it: ^7.2.0
|
get_it: ^7.2.0
|
||||||
|
|
||||||
# https://pub.dev/packages/shared_preferences
|
# https://pub.dev/packages/shared_preferences
|
||||||
shared_preferences: ^2.1.0
|
shared_preferences: ^2.1.0
|
||||||
|
|
||||||
timelines: ^0.1.0
|
timelines: ^0.1.0
|
||||||
|
|
||||||
universal_io: 2.2.0
|
universal_io: 2.2.0
|
||||||
|
|
||||||
xml: ^6.2.2
|
xml: ^6.2.2
|
||||||
|
|
||||||
# https://pub.dev/packages/device_info_plus/
|
# https://pub.dev/packages/device_info_plus/
|
||||||
device_info_plus: ^8.2.0
|
device_info_plus: ^8.2.0
|
||||||
|
|
||||||
# https://pub.dev/packages/webview_flutter/
|
# https://pub.dev/packages/webview_flutter/
|
||||||
webview_flutter: ^4.2.0
|
webview_flutter: ^4.2.1
|
||||||
|
|
||||||
# https://pub.dev/packages/event_bus_plus/
|
# https://pub.dev/packages/event_bus_plus/
|
||||||
event_bus_plus: ^0.6.1
|
event_bus_plus: ^0.6.1
|
||||||
|
|
@ -92,7 +103,10 @@ dependencies:
|
||||||
badges: ^3.1.1
|
badges: ^3.1.1
|
||||||
|
|
||||||
# https://pub.dev/packages/connectivity_plus
|
# 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:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue