import 'dart:convert'; import 'dart:io'; import 'package:diameter/localization_keys.dart'; import 'package:diameter/models/accuracy.dart'; import 'package:diameter/models/basal.dart'; import 'package:diameter/models/basal_profile.dart'; import 'package:diameter/models/bolus.dart'; import 'package:diameter/models/bolus_profile.dart'; import 'package:diameter/models/glucose_target.dart'; import 'package:diameter/models/log_bolus.dart'; import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_event.dart'; import 'package:diameter/models/log_event_type.dart'; import 'package:diameter/models/log_meal.dart'; import 'package:diameter/models/meal.dart'; import 'package:diameter/models/meal_category.dart'; import 'package:diameter/models/meal_portion_type.dart'; import 'package:diameter/models/meal_source.dart'; import 'package:diameter/models/settings.dart'; import 'package:flutter_translate/flutter_translate.dart'; // import 'package:flutter/material.dart'; import 'package:google_sign_in/google_sign_in.dart'; import 'package:intl/intl.dart'; import 'package:path_provider/path_provider.dart'; import 'package:http/http.dart' as http; import 'package:googleapis/drive/v3.dart' as drive; class GoogleAuthClient extends http.BaseClient { final Map _headers; final http.Client _client = http.Client(); GoogleAuthClient(this._headers); @override Future send(http.BaseRequest request) { return _client.send(request..headers.addAll(_headers)); } } class DataExport { static Map appDataToJson(DateTime timestamp) { final Map data = {}; data['exportDate'] = timestamp.toString(); data['accuracies'] = Accuracy.box.getAll().map((accuracy) => accuracy.toJson()).toList(); data['basalProfiles'] = BasalProfile.box .getAll() .map((basalProfile) => basalProfile.toJson()) .toList(); data['basalRates'] = Basal.box.getAll().map((basal) => basal.toJson()).toList(); data['bolusProfiles'] = BolusProfile.box .getAll() .map((bolusProfile) => bolusProfile.toJson()) .toList(); data['bolusRates'] = Bolus.box.getAll().map((bolusRates) => bolusRates.toJson()).toList(); data['glucoseTargets'] = GlucoseTarget.box .getAll() .map((glucoseTarget) => glucoseTarget.toJson()) .toList(); data['logBoli'] = LogBolus.box.getAll().map((logBolus) => logBolus.toJson()).toList(); data['logEntries'] = LogEntry.box.getAll().map((logEntry) => logEntry.toJson()).toList(); data['logEventTypes'] = LogEventType.box .getAll() .map((logEventType) => logEventType.toJson()) .toList(); data['logEvents'] = LogEvent.box.getAll().map((logEvent) => logEvent.toJson()).toList(); data['logMeals'] = LogMeal.box.getAll().map((logMeal) => logMeal.toJson()).toList(); data['mealCategories'] = MealCategory.box .getAll() .map((mealCategory) => mealCategory.toJson()) .toList(); data['mealPortionTypes'] = MealPortionType.box .getAll() .map((mealPortionType) => mealPortionType.toJson()) .toList(); data['mealSources'] = MealSource.box .getAll() .map((mealSource) => mealSource.toJson()) .toList(); data['meals'] = Meal.box.getAll().map((meal) => meal.toJson()).toList(); data['settings'] = Settings.toJson(); return data; } List appDataFromJson(File file, {String source = '', bool overrideExisting = true, bool skipDeleted = false}) { final Map data = json.decode(file.openRead().toString()); final List errors = []; final exportDate = DateTime.tryParse(data['exportDate']); if (exportDate != null && Settings.lastExportTimeStamp != null && exportDate.isAfter(Settings.lastExportTimeStamp!)) { if (data.keys.contains('accuracies')) { for (var entry in data['accuracies']) { if (!skipDeleted || entry['deleted'] == false) { final error = Accuracy.putFromJson(entry, overrideExisting, source); if (error != null) { errors.add(translate(LocalizationKeys.export_error, args: {"data": entry})); } } } } if (data.keys.contains('basalProfiles')) { for (var entry in data['basalProfiles']) { if (!skipDeleted || entry['deleted'] == false) { final error = BasalProfile.putFromJson(entry, overrideExisting, source); if (error != null) { errors.add(translate(LocalizationKeys.export_error, args: {"data": entry})); } } } } if (data.keys.contains('basalRates')) { for (var entry in data['basalRates']) { if (!skipDeleted || entry['deleted'] == false) { final error = Basal.putFromJson(entry, overrideExisting, source); if (error != null) { errors.add(translate(LocalizationKeys.export_error, args: {"data": entry})); } } } } if (data.keys.contains('bolusRates')) { for (var entry in data['bolusRates']) { if (!skipDeleted || entry['deleted'] == false) { final error = Bolus.putFromJson(entry, overrideExisting, source); if (error != null) { errors.add(translate(LocalizationKeys.export_error, args: {"data": entry})); } } } } if (data.keys.contains('glucoseTargets')) { for (var entry in data['glucoseTargets']) { if (!skipDeleted || entry['deleted'] == false) { final error = GlucoseTarget.putFromJson(entry, overrideExisting, source); if (error != null) { errors.add(translate(LocalizationKeys.export_error, args: {"data": entry})); } } } } if (data.keys.contains('logBoli')) { for (var entry in data['logBoli']) { if (!skipDeleted || entry['deleted'] == false) { final error = LogBolus.putFromJson(entry, overrideExisting, source); if (error != null) { errors.add(translate(LocalizationKeys.export_error, args: {"data": entry})); } } } } if (data.keys.contains('logEntries')) { for (var entry in data['logEntries']) { if (!skipDeleted || entry['deleted'] == false) { final error = LogEntry.putFromJson(entry, overrideExisting, source); if (error != null) { errors.add(translate(LocalizationKeys.export_error, args: {"data": entry})); } } } } if (data.keys.contains('logEvents')) { for (var entry in data['logEvents']) { if (!skipDeleted || entry['deleted'] == false) { final error = LogEvent.putFromJson(entry, overrideExisting, source); if (error != null) { errors.add(translate(LocalizationKeys.export_error, args: {"data": entry})); } } } } if (data.keys.contains('logMeals')) { for (var entry in data['logMeals']) { if (!skipDeleted || entry['deleted'] == false) { final error = LogMeal.putFromJson(entry, overrideExisting, source); if (error != null) { errors.add(translate(LocalizationKeys.export_error, args: {"data": entry})); } } } } if (data.keys.contains('mealCategories')) { for (var entry in data['mealCategories']) { if (!skipDeleted || entry['deleted'] == false) { final error = MealCategory.putFromJson(entry, overrideExisting, source); if (error != null) { errors.add(translate(LocalizationKeys.export_error, args: {"data": entry})); } } } } if (data.keys.contains('mealPortionTypes')) { for (var entry in data['mealPortionTypes']) { if (!skipDeleted || entry['deleted'] == false) { final error = MealPortionType.putFromJson(entry, overrideExisting, source); if (error != null) { errors.add(translate(LocalizationKeys.export_error, args: {"data": entry})); } } } } if (data.keys.contains('mealSources')) { for (var entry in data['mealSources']) { if (!skipDeleted || entry['deleted'] == false) { final error = MealSource.putFromJson(entry, overrideExisting, source); if (error != null) { errors.add(translate(LocalizationKeys.export_error, args: {"data": entry})); } } } } if (data.keys.contains('meals')) { for (var entry in data['meals']) { if (!skipDeleted || entry['deleted'] == false) { final error = Meal.putFromJson(entry, overrideExisting, source); if (error != null) { errors.add(translate(LocalizationKeys.export_error, args: {"data": entry})); } } } } } else { // show confirmation dialog because export is newer than last saved timestamp } return errors; } static Future exportDataToFile(DateTime timestamp) async { final appDocDir = await getApplicationDocumentsDirectory(); final appDocPath = appDocDir.path; final DateFormat formatter = DateFormat(DateFormat.YEAR_MONTH_DAY); final date = DateTime.now(); final file = File('$appDocPath/diameter_${formatter.format(date)}.json'); file.writeAsStringSync(json.encode(appDataToJson(timestamp))); return file; } static Future exportToGoogleDrive() async { final login = GoogleSignIn.standard(scopes: [drive.DriveApi.driveScope]); final GoogleSignInAccount? account = await login.signIn(); if (account != null) { final authenticateClient = GoogleAuthClient(await account.authHeaders); final driveApi = drive.DriveApi(authenticateClient); final timestamp = DateTime.now(); var localFile = await DataExport.exportDataToFile(timestamp); var media = drive.Media(localFile.openRead(), localFile.lengthSync()); final settings = Settings.get(); settings.lastExportTimestamp = timestamp; Settings.put(settings); drive.File driveFile = drive.File()..name = 'diameter.json'; await driveApi.files.create(driveFile, uploadMedia: media); } } static Future importFromGoogleDrive() async { final login = GoogleSignIn.standard(scopes: [drive.DriveApi.driveScope]); final GoogleSignInAccount? account = await login.signIn(); if (account != null) { final authenticateClient = GoogleAuthClient(await account.authHeaders); final driveApi = drive.DriveApi(authenticateClient); final timestamp = DateTime.now(); var localFile = await DataExport.exportDataToFile(timestamp); var media = drive.Media(localFile.openRead(), localFile.lengthSync()); final settings = Settings.get(); settings.lastExportTimestamp = timestamp; Settings.put(settings); drive.File driveFile = drive.File()..name = 'diameter.json'; await driveApi.files.create(driveFile, uploadMedia: media); } } } // class DataExportDialog extends StatefulWidget { // static const String routeName = '/data-export'; // const DataExportDialog({Key? key}) : super(key: key); // @override // _DataExportDialogState createState() => _DataExportDialogState(); // } // class _DataExportDialogState extends State { // final ScrollController _scrollController = ScrollController(); // bool _isSaving = false; // @override // void dispose() { // _scrollController.dispose(); // super.dispose(); // } // @override // void initState() { // super.initState(); // } // void onExport(BuildContext context) async { // setState(() { // _isSaving = true; // }); // Navigator.pop(context); // setState(() { // _isSaving = false; // }); // } // @override // Widget build(BuildContext context) { // return AlertDialog( // content: Column( // mainAxisSize: MainAxisSize.min, // crossAxisAlignment: CrossAxisAlignment.center, // children: const [ // // CheckboxListTile( // // value: showChart, // // onChanged: (_) => setState(() => showChart = !showChart), // // title: const Text('show Chart'), // // controlAffinity: ListTileControlAffinity.leading, // // ), // ], // ), // actions: [ // TextButton( // onPressed: () => Navigator.pop(context), // child: const Text('CANCEL'), // ), // ElevatedButton( // onPressed: !_isSaving ? () => onExport(context) : null, // child: const Text('EXPORT'), // ), // ]); // } // }