From a07e283841e93deab6d85fe5a5561cd3bcdecff7 Mon Sep 17 00:00:00 2001 From: Trent Palmer Date: Thu, 25 Jul 2019 09:26:13 -0700 Subject: [PATCH] implement database import/export to json file --- lib/about.dart | 2 +- lib/database_helper.dart | 7 + lib/global_helper_functions.dart | 38 +++ lib/settings.dart | 501 +++++++++++++++++++++++++++++++ lib/weather.dart | 2 +- lib/weather_forecast.dart | 2 +- 6 files changed, 549 insertions(+), 3 deletions(-) diff --git a/lib/about.dart b/lib/about.dart index 09b9a6d..77c332c 100644 --- a/lib/about.dart +++ b/lib/about.dart @@ -53,7 +53,7 @@ InkWell aboutApp(BuildContext context) { mainAxisAlignment: MainAxisAlignment.start, children: [ Text( - 'Version: 0.1.1\n', + 'Version: 0.1.2\n', style: TextStyle( color: candyApple, ), diff --git a/lib/database_helper.dart b/lib/database_helper.dart index 769aeeb..474bd29 100644 --- a/lib/database_helper.dart +++ b/lib/database_helper.dart @@ -1,5 +1,6 @@ // https://github.com/tekartik/sqflite/blob/master/sqflite/doc/migration_example.md import 'dart:io'; +import 'dart:convert'; import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; import 'package:path_provider/path_provider.dart'; @@ -253,4 +254,10 @@ class DatabaseHelper { return await db .delete(table, where: '$columnMapLocation = ?', whereArgs: [ml]); } + + Future queryDBExport() async{ + Database db = await instance.database; + List result = await db.rawQuery('SELECT $columnMapLocation,$columnNotes,$columnIsAutoTimeOffset,$columnTimeOffSet FROM $table'); + return(jsonEncode(result)); + } } diff --git a/lib/global_helper_functions.dart b/lib/global_helper_functions.dart index f6cc15e..4b4f7b3 100644 --- a/lib/global_helper_functions.dart +++ b/lib/global_helper_functions.dart @@ -391,3 +391,41 @@ Future setPreferenceUseWeather(bool useWeather) async { bool committed = await prefs.setBool("useWeather", useWeather); return (committed); } + +Future getPreferenceDBExportFileName() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String dbExportFileName = prefs.getString("dbExportFileName") ?? "libre_gps_parser.json"; + return (dbExportFileName.length > 0) ? dbExportFileName : "libre_gps_parser.json"; +} + +Future setPreferenceDBExportFileName(String dbExportFileName) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + bool committed = await prefs.setString("dbExportFileName", dbExportFileName); + return (committed); +} + +Future importDataBase(String importString) { + final dbHelper = DatabaseHelper.instance; + final int _timeStamp = newTimeStamp(); + List _importJson = jsonDecode(importString); + int _importJsonLength = _importJson.length; + for (int i = 0; i < _importJsonLength; i++) { + dbHelper.queryRowExists(_importJson[i]['mapLocation']).then((int rowExists) { + if (rowExists == 0) { + Map row = { + DatabaseHelper.columnMapLocation: _importJson[i]['mapLocation'], + }; + if (_importJson[i]['notes'] != null) { + row['notes'] = _importJson[i]['notes']; + } + if (_importJson[i]['isAutoTimeOffset'] == 0) { + row['timeOffSet'] = _importJson[i]['timeOffSet']; + row['timeOffSetTime'] = _timeStamp; + row['isAutoTimeOffset'] = _importJson[i]['isAutoTimeOffset']; + } + dbHelper.insert(row); + } + }); + } + return Future.value(true); +} diff --git a/lib/settings.dart b/lib/settings.dart index 9b374a6..1b87ce5 100644 --- a/lib/settings.dart +++ b/lib/settings.dart @@ -1,5 +1,10 @@ +import 'package:permission_handler/permission_handler.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'global_helper_functions.dart'; +import 'database_helper.dart'; +import 'dart:io'; class Settings extends StatefulWidget { @override @@ -7,9 +12,12 @@ class Settings extends StatefulWidget { } class _SettingsState extends State { + // String absoluteDBExportFileName = ''; + String _dbExportFileNameString = ''; String elevationServer = "none"; String openWeatherMapKey = "none??"; String shortOWMAK = "none"; + final _dbExportFileNameController = TextEditingController(); final _elevationServerController = TextEditingController(); final _oWMKController = TextEditingController(); bool _useElevation = false; @@ -24,6 +32,394 @@ class _SettingsState extends State { @override Widget build(BuildContext context) { + + Future _showDBImportDialog() async{ + Map _storagePermissions; + PermissionStatus _storagePermission = await PermissionHandler().checkPermissionStatus(PermissionGroup.storage); + if (_storagePermission == PermissionStatus.denied) { + _storagePermissions = await PermissionHandler().requestPermissions([PermissionGroup.storage]); + } + if ((_storagePermission == PermissionStatus.granted) || (_storagePermissions.toString() == PermissionStatus.granted.toString())) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + backgroundColor: ivory, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(6.0)), + ), + title: Text( + 'You can import a\njson backup of\nyour database', + textAlign: TextAlign.center, + style: TextStyle( + color: candyApple, + ), + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: EdgeInsets.only( + top: 40, + bottom: 10, + ), + child: ButtonTheme( + height: 55, + padding: EdgeInsets.only( + top: 5, + right: 12, + bottom: 12, + left: 10 + ), + child: RaisedButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(6.0)), + ), + color: peacockBlue, + child: Text( + 'Select File', + style: TextStyle( + height: textHeight, + color: Colors.white, + fontSize: 24, + ), + ), + onPressed: () async{ + String _filePath; + _filePath = await FilePicker.getFilePath(type: FileType.ANY); + Navigator.of(context).pop(); + try { + String _importString = await File(_filePath).readAsString(); + bool _imported = await importDataBase(_importString); + if (_imported) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + backgroundColor: ivory, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(6.0)), + ), + title: Text( + 'Importing From\n\'$_filePath\'\n Succeeded!', + textAlign: TextAlign.center, + style: TextStyle( + color: candyApple, + ), + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: EdgeInsets.only( + top: 40, + bottom: 10, + ), + child: ButtonTheme( + height: 55, + padding: EdgeInsets.only( + top: 5, + right: 12, + bottom: 12, + left: 10 + ), + child: RaisedButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(6.0)), + ), + color: peacockBlue, + child: Text( + 'OK', + style: TextStyle( + height: textHeight, + color: Colors.white, + fontSize: 24, + ), + ), + onPressed: () async{ + Navigator.of(context).pop(); + } + ), + ), + ), + ], + ), + ); + } + ); + } + } catch(e) { + print(e); + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + backgroundColor: ivory, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(6.0)), + ), + title: Text( + 'Oops, something went wrong', + textAlign: TextAlign.center, + style: TextStyle( + color: candyApple, + ), + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: EdgeInsets.only( + top: 40, + bottom: 10, + ), + child: ButtonTheme( + height: 55, + padding: EdgeInsets.only( + top: 5, + right: 12, + bottom: 12, + left: 10 + ), + child: RaisedButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(6.0)), + ), + color: peacockBlue, + child: Text( + 'OK', + style: TextStyle( + height: textHeight, + color: Colors.white, + fontSize: 24, + ), + ), + onPressed: () async{ + Navigator.of(context).pop(); + } + ), + ), + ), + ], + ), + ); + } + ); + } + } + ), + ), + ), + ], + ), + ); + } + ); + } + } + + Future _showDBExportDialog() async{ + Map _storagePermissions; + PermissionStatus _storagePermission = await PermissionHandler().checkPermissionStatus(PermissionGroup.storage); + if (_storagePermission == PermissionStatus.denied) { + _storagePermissions = await PermissionHandler().requestPermissions([PermissionGroup.storage]); + } + if ((_storagePermission == PermissionStatus.granted) || (_storagePermissions.toString() == PermissionStatus.granted.toString())) { + Directory sdcard = await getExternalStorageDirectory(); + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + backgroundColor: ivory, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(6.0)), + ), + title: Text( + 'Export File Name?', + textAlign: TextAlign.center, + style: TextStyle( + color: candyApple, + ), + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'DataBase will be\nexported to json file.', + style: TextStyle( + fontSize: 16, + ), + ), + TextField( + controller: _dbExportFileNameController, + keyboardType: TextInputType.multiline, + maxLines: null, + decoration: InputDecoration( + hintText: this._dbExportFileNameString, + ), + onChanged: (text) { + this._dbExportFileNameString = (text.length > 0) ? text : "libre_gps_parser.json"; + }, + ), + Container( + margin: EdgeInsets.only( + top: 40, + bottom: 10, + ), + child: ButtonTheme( + height: 55, + padding: EdgeInsets.only( + top: 5, + right: 12, + bottom: 12, + left: 10 + ), + child: RaisedButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(6.0)), + ), + color: peacockBlue, + child: Text( + 'Export', + style: TextStyle( + height: textHeight, + color: Colors.white, + fontSize: 24, + ), + ), + onPressed: () async{ + _updatedbExportFileName(); + final dbHelper = DatabaseHelper.instance; + String _dbExportString = await dbHelper.queryDBExport(); + String _absoluteDBExportFileName = '${sdcard.path}/${this._dbExportFileNameString}'; + File _file = File(_absoluteDBExportFileName); + File _result = await _file.writeAsString(_dbExportString); + Navigator.of(context).pop(); + if (_result == null) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + backgroundColor: ivory, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(6.0)), + ), + title: Text( + 'Writing To\n\'$_absoluteDBExportFileName\'\n(sdcard) Failed!', + textAlign: TextAlign.center, + style: TextStyle( + color: candyApple, + ), + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: EdgeInsets.only( + top: 40, + bottom: 10, + ), + child: ButtonTheme( + height: 55, + padding: EdgeInsets.only( + top: 5, + right: 12, + bottom: 12, + left: 10 + ), + child: RaisedButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(6.0)), + ), + color: peacockBlue, + child: Text( + 'OK', + style: TextStyle( + height: textHeight, + color: Colors.white, + fontSize: 24, + ), + ), + onPressed: () async{ + Navigator.of(context).pop(); + } + ), + ), + ), + ], + ), + ); + } + ); + } else { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + backgroundColor: ivory, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(6.0)), + ), + title: Text( + 'Writing To\n\'$_absoluteDBExportFileName\'\n(sdcard) Succeeded!', + textAlign: TextAlign.center, + style: TextStyle( + color: candyApple, + ), + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: EdgeInsets.only( + top: 40, + bottom: 10, + ), + child: ButtonTheme( + height: 55, + padding: EdgeInsets.only( + top: 5, + right: 12, + bottom: 12, + left: 10 + ), + child: RaisedButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(6.0)), + ), + color: peacockBlue, + child: Text( + 'OK', + style: TextStyle( + height: textHeight, + color: Colors.white, + fontSize: 24, + ), + ), + onPressed: () async{ + Navigator.of(context).pop(); + } + ), + ), + ), + ], + ), + ); + } + ); + } + } + ), + ), + ), + ], + ), + ); + } + ); + } + } + return WillPopScope( onWillPop: _popBack, child: Scaffold( @@ -183,6 +579,100 @@ class _SettingsState extends State { ], ), ) : Container()), + Container( + padding: myBoxPadding, + decoration: myBoxDecoration(ivory), + child: Row( + children: [ + Expanded( + flex: 2, + child: IconButton( + alignment: Alignment.center, + icon: Icon( + Icons.arrow_back, + color: candyApple, + ), + iconSize: 60.0, + onPressed: () { + _showDBExportDialog(); + }, + ), + ), + Expanded( + flex: 6, + child: Text( + 'Export DataBase', + textAlign: TextAlign.center, + style: TextStyle( + height: textHeight, + fontSize: 20, + ), + ), + ), + Expanded( + flex: 2, + child: IconButton( + alignment: Alignment.center, + icon: Icon( + Icons.arrow_forward, + color: candyApple, + ), + iconSize: 60.0, + onPressed: () { + _showDBExportDialog(); + }, + ), + ), + ], + ), + ), + Container( + padding: myBoxPadding, + decoration: myBoxDecoration(ivory), + child: Row( + children: [ + Expanded( + flex: 2, + child: IconButton( + alignment: Alignment.center, + icon: Icon( + Icons.arrow_forward, + color: candyApple, + ), + iconSize: 60.0, + onPressed: () { + _showDBImportDialog(); + }, + ), + ), + Expanded( + flex: 6, + child: Text( + 'Import DataBase', + textAlign: TextAlign.center, + style: TextStyle( + height: textHeight, + fontSize: 20, + ), + ), + ), + Expanded( + flex: 2, + child: IconButton( + alignment: Alignment.center, + icon: Icon( + Icons.arrow_back, + color: candyApple, + ), + iconSize: 60.0, + onPressed: () { + _showDBImportDialog(); + }, + ), + ), + ], + ), + ), ], ), ), @@ -192,6 +682,7 @@ class _SettingsState extends State { Future updateState() async { String _evServer = await getPreferenceElevationServer(); String _oWMK = await getPreferenceOpenWeatherMapApiKey(); + String _dbEFNS = await getPreferenceDBExportFileName(); bool _useElev = await getPreferenceUseElevation(); bool _useWTHR = await getPreferenceUseWeather(); setState(() { @@ -200,6 +691,16 @@ class _SettingsState extends State { this._useElevation = _useElev; this._useWeather = _useWTHR; this.shortOWMAK = _oWMK.substring(0, 5); + this._dbExportFileNameString = _dbEFNS; + }); + } + + void _updatedbExportFileName() { + String _dbEFNS = this._dbExportFileNameString; + setPreferenceDBExportFileName(_dbEFNS).then((bool committed) { + setState(() { + this._dbExportFileNameString = _dbEFNS; + }); }); } diff --git a/lib/weather.dart b/lib/weather.dart index b9dc59a..0a82255 100644 --- a/lib/weather.dart +++ b/lib/weather.dart @@ -901,7 +901,7 @@ class _WeatherState extends State { return Container( width: 100, child: CachedNetworkImage( - imageUrl: 'https://openweathermap.org/img/w/${widget.weatherConditionsIcon}.png', + imageUrl: 'http://openweathermap.org/img/w/${widget.weatherConditionsIcon}.png', ), ); } diff --git a/lib/weather_forecast.dart b/lib/weather_forecast.dart index c928c6c..cffd161 100644 --- a/lib/weather_forecast.dart +++ b/lib/weather_forecast.dart @@ -81,7 +81,7 @@ class _WeatherForeCastState extends State { return Container( width: 100, child: CachedNetworkImage( - imageUrl: 'https://openweathermap.org/img/w/${foreCast['weather'][0]['icon']}.png', + imageUrl: 'http://openweathermap.org/img/w/${foreCast['weather'][0]['icon']}.png', ), ); }