mirror of
https://github.com/TrentSPalmer/libre_gps_parser.git
synced 2025-08-23 04:23:57 -07:00
merge --squash devel, version 0.1.0
This commit is contained in:
156
lib/about.dart
Normal file
156
lib/about.dart
Normal file
@@ -0,0 +1,156 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'global_helper_functions.dart';
|
||||
|
||||
InkWell aboutApp(BuildContext context) {
|
||||
return InkWell(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'About',
|
||||
style: TextStyle(
|
||||
color: candyApple,
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.info,
|
||||
size: 48.0,
|
||||
color: Colors.black,
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
final double textHeight = 1.5;
|
||||
return AlertDialog(
|
||||
backgroundColor: ivory,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
),
|
||||
title: Text(
|
||||
'Libre Gps Parser',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: candyApple,
|
||||
),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'The essence of Libre Gps Parser, is to parse gps coordinates from '
|
||||
'a map link that you share from the Google Maps Application. '
|
||||
'After that you can use the gps coordinates to make api calls for '
|
||||
'weather, elevation, and timezoneoffset.\n\n'
|
||||
'Parsing the gps coordinates is accomplished by getting the Google Map '
|
||||
'link with an http request, and then filtering the raw text result. '
|
||||
'Locally, data is cached in an sqlite database, in order to economize '
|
||||
'network requests.\n\n'
|
||||
'This version of the application requires that you set up an elevation '
|
||||
'api server, and provide an openweathermap api key. '
|
||||
'Or you can disable elevation and weather in settings.'
|
||||
'',
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
top: 40,
|
||||
bottom: 10,
|
||||
),
|
||||
child: Wrap(
|
||||
runSpacing: 30,
|
||||
alignment: WrapAlignment.center,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: 15,
|
||||
),
|
||||
child: ButtonTheme(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
),
|
||||
height: 75,
|
||||
child: RaisedButton(
|
||||
color: peacockBlue,
|
||||
child: Icon(
|
||||
Icons.arrow_back,
|
||||
size: 48.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: 15,
|
||||
),
|
||||
child: ButtonTheme(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
),
|
||||
height: 75,
|
||||
child: RaisedButton(
|
||||
color: peacockBlue,
|
||||
child: Text(
|
||||
"License",
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: 15,
|
||||
),
|
||||
child: ButtonTheme(
|
||||
height: 75,
|
||||
child: RaisedButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
),
|
||||
color: peacockBlue,
|
||||
child: Text(
|
||||
"Other Licenses",
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
showLicensePage(context: context);
|
||||
}
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
@@ -1,27 +1,34 @@
|
||||
// https://github.com/tekartik/sqflite/blob/master/sqflite/doc/migration_example.md
|
||||
import 'dart:io';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class DatabaseHelper {
|
||||
|
||||
static final _databaseName = "LnLShare.db";
|
||||
static final _databaseVersion = 1;
|
||||
static final String _databaseName = "LibreGpsParser.db";
|
||||
static final int _databaseVersion = 4;
|
||||
|
||||
static final table = 'gmaplocations';
|
||||
static final weatherTable = 'weather';
|
||||
|
||||
static final columnMapLocation = 'mapLocation';
|
||||
static final columnLatLong = 'latLong';
|
||||
static final columnLnlTime = 'lnlTime';
|
||||
static final columnViewTime = 'viewTime';
|
||||
static final columnElev = 'elev';
|
||||
static final columnElevTime = 'elevTime';
|
||||
static final columnWeatherID = 'weatherID';
|
||||
static final String table = 'gmaplocations';
|
||||
static final String weatherTable = 'weather';
|
||||
|
||||
static final columnWeatherWeatherID = 'weatherID';
|
||||
static final columnWeather = 'weather';
|
||||
static final columnWeatherForecast = 'weatherForecast';
|
||||
static final String columnMapLocation = 'mapLocation';
|
||||
static final String columnLatLong = 'latLong';
|
||||
static final String columnLnlTime = 'lnlTime';
|
||||
static final String columnViewTime = 'viewTime';
|
||||
static final String columnElev = 'elev';
|
||||
static final String columnElevTime = 'elevTime';
|
||||
static final String columnWeatherID = 'weatherID';
|
||||
static final String columnTimeOffSet = 'timeOffSet';
|
||||
static final String columnTimeOffSetTime = 'timeOffSetTime';
|
||||
|
||||
static final String columnWeatherWeatherID = 'weatherID';
|
||||
static final String columnWeather = 'weather';
|
||||
static final String columnWeatherForecast = 'weatherForecast';
|
||||
static final String columnNotes = 'notes';
|
||||
|
||||
// is the timeOffSet automatically set by consulting teleport api
|
||||
// (not manually set by spinner)
|
||||
static final String columnIsAutoTimeOffset = 'isAutoTimeOffset';
|
||||
|
||||
// make this a singleton class
|
||||
DatabaseHelper._privateConstructor();
|
||||
@@ -35,14 +42,32 @@ class DatabaseHelper {
|
||||
_database = await _initDatabase();
|
||||
return _database;
|
||||
}
|
||||
|
||||
|
||||
// this opens the database (and creates it if it doesn't exist)
|
||||
_initDatabase() async {
|
||||
Directory documentsDirectory = await getApplicationDocumentsDirectory();
|
||||
String path = join(documentsDirectory.path, _databaseName);
|
||||
return await openDatabase(path,
|
||||
version: _databaseVersion,
|
||||
onCreate: _onCreate);
|
||||
String path = join(
|
||||
documentsDirectory.path,
|
||||
_databaseName
|
||||
);
|
||||
return await openDatabase(
|
||||
path,
|
||||
version: _databaseVersion,
|
||||
onCreate: _onCreate,
|
||||
onUpgrade: _onUpgrade,
|
||||
);
|
||||
}
|
||||
|
||||
// sqflite seems to be able to figure out the versions
|
||||
// automatically?
|
||||
// you can only ADD one column at a time?
|
||||
Future _onUpgrade(Database db, int oldVersion, int newVersion) async {
|
||||
if ((oldVersion == 1) || (oldVersion == 2)) {
|
||||
await db.execute('''ALTER TABLE $table ADD $columnIsAutoTimeOffset INT''');
|
||||
await db.execute('''ALTER TABLE $table ADD $columnNotes TEXT''');
|
||||
} else if (oldVersion == 3) {
|
||||
await db.execute('''ALTER TABLE $table ADD $columnNotes TEXT''');
|
||||
}
|
||||
}
|
||||
|
||||
// SQL code to create the database table
|
||||
@@ -55,18 +80,22 @@ class DatabaseHelper {
|
||||
$columnViewTime INT,
|
||||
$columnElev INT,
|
||||
$columnElevTime INT,
|
||||
$columnWeatherID INT
|
||||
$columnWeatherID INT,
|
||||
$columnTimeOffSet INT,
|
||||
$columnTimeOffSetTime INT,
|
||||
$columnIsAutoTimeOffset INT,
|
||||
$columnNotes TEXT
|
||||
)
|
||||
''');
|
||||
await db.execute('''
|
||||
CREATE TABLE $weatherTable (
|
||||
$columnWeatherWeatherID INT UNIQUE,
|
||||
$columnWeatherWeatherID INT UNIQUE ON CONFLICT REPLACE,
|
||||
$columnWeather TEXT,
|
||||
$columnWeatherForecast TEXT
|
||||
)
|
||||
''');
|
||||
}
|
||||
|
||||
|
||||
Future<int> insert(Map<String, dynamic> row) async {
|
||||
Database db = await instance.database;
|
||||
return await db.insert(table, row);
|
||||
@@ -84,73 +113,144 @@ class DatabaseHelper {
|
||||
|
||||
Future<int> queryRowCount() async {
|
||||
Database db = await instance.database;
|
||||
return Sqflite.firstIntValue(await db.rawQuery('SELECT COUNT(*) FROM $table'));
|
||||
return Sqflite.firstIntValue(
|
||||
await db.rawQuery('SELECT COUNT(*) FROM $table'));
|
||||
}
|
||||
|
||||
Future<int> queryRowExists(String mapLocation) async {
|
||||
String depostrophedMapLocation = mapLocation.replaceAll('\'', '\'\'');
|
||||
Database db = await instance.database;
|
||||
return Sqflite.firstIntValue(await db.rawQuery('SELECT EXISTS (SELECT $columnMapLocation FROM $table WHERE $columnMapLocation=\'$mapLocation\')'));
|
||||
return Sqflite.firstIntValue(await db.rawQuery(
|
||||
'SELECT EXISTS (SELECT $columnMapLocation FROM $table WHERE $columnMapLocation=\'$depostrophedMapLocation\')'));
|
||||
}
|
||||
|
||||
Future<int> queryWeatherIDExists(int weatherID) async {
|
||||
Database db = await instance.database;
|
||||
return Sqflite.firstIntValue(await db.rawQuery('SELECT EXISTS (SELECT $columnWeatherWeatherID FROM $weatherTable WHERE $columnWeatherWeatherID=\'$weatherID\')'));
|
||||
return Sqflite.firstIntValue(await db.rawQuery(
|
||||
'SELECT EXISTS (SELECT $columnWeatherWeatherID FROM $weatherTable WHERE $columnWeatherWeatherID=\'$weatherID\')'));
|
||||
}
|
||||
|
||||
Future<String> queryNewestMapLocation() async {
|
||||
Database db = await instance.database;
|
||||
List<Map> result = await db.rawQuery('SELECT $columnMapLocation FROM $table ORDER BY $columnViewTime DESC LIMIT 1');
|
||||
return (result.length == 0) ? 'Plataea\nGreece\nhttps://maps.app.goo.gl/1NW9z': result[0]['mapLocation'];
|
||||
List<Map> result = await db.rawQuery(
|
||||
'SELECT $columnMapLocation FROM $table ORDER BY $columnViewTime DESC LIMIT 1');
|
||||
return (result.length == 0)
|
||||
? 'Plataea\nGreece\nhttps://maps.app.goo.gl/1NW9z'
|
||||
: result[0]['mapLocation'];
|
||||
}
|
||||
|
||||
Future<String> querySecondNewestMapLocation() async {
|
||||
Database db = await instance.database;
|
||||
List<Map> result = await db.rawQuery(
|
||||
'SELECT $columnMapLocation FROM $table ORDER BY $columnViewTime DESC LIMIT 2');
|
||||
return (result.length == 2)
|
||||
? result[1]['mapLocation']
|
||||
: 'none';
|
||||
}
|
||||
|
||||
|
||||
Future<List<String>> sortedMapLocations() async {
|
||||
Database db = await instance.database;
|
||||
var result = await db.rawQuery('SELECT $columnMapLocation FROM $table ORDER BY $columnViewTime DESC');
|
||||
List<String> result_list = new List<String>();
|
||||
var result = await db.rawQuery(
|
||||
'SELECT $columnMapLocation FROM $table ORDER BY $columnViewTime DESC');
|
||||
List<String> resultList = List<String>();
|
||||
for (var i = 0; i < result.length; i++) {
|
||||
result_list.add(result[i]['mapLocation']);
|
||||
resultList.add(result[i]['mapLocation']);
|
||||
}
|
||||
return result_list;
|
||||
return resultList;
|
||||
}
|
||||
|
||||
Future<String> queryLatNLong(String mapLocation) async {
|
||||
Database db = await instance.database;
|
||||
List<Map> result = await db.rawQuery('SELECT $columnLatLong FROM $table WHERE $columnMapLocation = ?',[mapLocation]);
|
||||
List<Map> result = await db.rawQuery(
|
||||
'SELECT $columnLatLong FROM $table WHERE $columnMapLocation = ?',
|
||||
[mapLocation]);
|
||||
return (result[0]['latLong'] == null) ? 'NA' : result[0]['latLong'];
|
||||
}
|
||||
|
||||
Future<String> queryNotes(String mapLocation) async {
|
||||
Database db = await instance.database;
|
||||
List<Map> result = await db.rawQuery(
|
||||
'SELECT $columnNotes FROM $table WHERE $columnMapLocation = ?',
|
||||
[mapLocation]);
|
||||
return result[0]['notes'] ?? '';
|
||||
}
|
||||
|
||||
Future<String> queryWeather(int weatherID) async {
|
||||
Database db = await instance.database;
|
||||
List<Map> result = await db.rawQuery('SELECT $columnWeather FROM $weatherTable WHERE $columnWeatherWeatherID = ?',[weatherID]);
|
||||
return result[0]['weather'];
|
||||
List<Map> result = await db.rawQuery(
|
||||
'SELECT $columnWeather FROM $weatherTable WHERE $columnWeatherWeatherID = ?',
|
||||
[weatherID]);
|
||||
return (result.length > 0) ? result[0]['weather'] : 'NA';
|
||||
}
|
||||
|
||||
Future<String> queryWeatherForeCast(int weatherID) async {
|
||||
Database db = await instance.database;
|
||||
List<Map> result = await db.rawQuery(
|
||||
'SELECT $columnWeatherForecast FROM $weatherTable WHERE $columnWeatherWeatherID = ?',
|
||||
[weatherID]);
|
||||
return result[0]['weatherForecast'] ?? 'NA';
|
||||
}
|
||||
|
||||
Future<int> queryElevation(String mapLocation) async {
|
||||
Database db = await instance.database;
|
||||
List<Map> result = await db.rawQuery('SELECT $columnElev FROM $table WHERE $columnMapLocation = ?',[mapLocation]);
|
||||
List<Map> result = await db.rawQuery(
|
||||
'SELECT $columnElev FROM $table WHERE $columnMapLocation = ?',
|
||||
[mapLocation]);
|
||||
return result[0]['elev'];
|
||||
}
|
||||
|
||||
Future<int> queryIsAutoTimeOffSet(String mapLocation) async {
|
||||
Database db = await instance.database;
|
||||
List<Map> result = await db.rawQuery(
|
||||
'SELECT $columnIsAutoTimeOffset FROM $table WHERE $columnMapLocation = ?',
|
||||
[mapLocation]);
|
||||
return result[0]['isAutoTimeOffset'] ?? 1;
|
||||
}
|
||||
|
||||
Future<int> queryTimeOffSetTime(String mapLocation) async {
|
||||
Database db = await instance.database;
|
||||
List<Map> result = await db.rawQuery(
|
||||
'SELECT $columnTimeOffSetTime FROM $table WHERE $columnMapLocation = ?',
|
||||
[mapLocation]);
|
||||
return result[0]['timeOffSetTime'] ??
|
||||
-1; // returns -1 if result[0]['timeOffSetTime'] is null
|
||||
}
|
||||
|
||||
Future<int> queryTimeOffSet(String mapLocation) async {
|
||||
Database db = await instance.database;
|
||||
List<Map> result = await db.rawQuery(
|
||||
'SELECT $columnTimeOffSet FROM $table WHERE $columnMapLocation = ?',
|
||||
[mapLocation]);
|
||||
return result[0]['timeOffSet'] ??
|
||||
-1; // returns -1 if result[0]['timeOffSet'] is null
|
||||
}
|
||||
|
||||
Future<int> queryWeatherID(String mapLocation) async {
|
||||
Database db = await instance.database;
|
||||
List<Map> result = await db.rawQuery('SELECT $columnWeatherID FROM $table WHERE $columnMapLocation = ?',[mapLocation]);
|
||||
List<Map> result = await db.rawQuery(
|
||||
'SELECT $columnWeatherID FROM $table WHERE $columnMapLocation = ?',
|
||||
[mapLocation]);
|
||||
return result[0]['weatherID'];
|
||||
}
|
||||
|
||||
Future<int> update(Map<String, dynamic> row) async {
|
||||
Database db = await instance.database;
|
||||
String mapLocation = row[columnMapLocation];
|
||||
return await db.update(table, row, where: '$columnMapLocation = ?', whereArgs: [mapLocation]);
|
||||
return await db.update(table, row,
|
||||
where: '$columnMapLocation = ?', whereArgs: [mapLocation]);
|
||||
}
|
||||
|
||||
Future<int> updateWeathTbl(Map<String, dynamic> row) async {
|
||||
Database db = await instance.database;
|
||||
int weatherID = row[columnWeatherWeatherID];
|
||||
return await db.update(weatherTable, row, where: '$columnWeatherWeatherID = ?', whereArgs: [weatherID]);
|
||||
return await db.update(weatherTable, row,
|
||||
where: '$columnWeatherWeatherID = ?', whereArgs: [weatherID]);
|
||||
}
|
||||
|
||||
Future<int> delete(String ml) async {
|
||||
Database db = await instance.database;
|
||||
return await db.delete(table, where: '$columnMapLocation = ?', whereArgs: [ml]);
|
||||
return await db
|
||||
.delete(table, where: '$columnMapLocation = ?', whereArgs: [ml]);
|
||||
}
|
||||
}
|
||||
|
17
lib/default_plataea_notes.dart
Normal file
17
lib/default_plataea_notes.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
final String defaultPlataeaNotes = """
|
||||

|
||||
___
|
||||
# Example Notes For Plataea
|
||||
|
||||
## Greek Coalitian that fought Persia
|
||||
* Sparta
|
||||
* Athens
|
||||
* Corinth
|
||||
* Megara
|
||||
|
||||
## Major Battles Preceeding Plataea
|
||||
1. Marathon
|
||||
2. Thermopylae
|
||||
3. Sacking of Athens
|
||||
4. Naval Battle of Salami
|
||||
""";
|
330
lib/edit_notes.dart
Normal file
330
lib/edit_notes.dart
Normal file
@@ -0,0 +1,330 @@
|
||||
import 'package:permission_handler/permission_handler.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 EditNotes extends StatefulWidget {
|
||||
final String mapLocation;
|
||||
|
||||
EditNotes({
|
||||
Key key,
|
||||
this.mapLocation,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_EditNotesState createState() => _EditNotesState();
|
||||
}
|
||||
|
||||
class _EditNotesState extends State<EditNotes> {
|
||||
|
||||
final double textHeight = 1.5;
|
||||
final _textEditingController = TextEditingController();
|
||||
final dbHelper = DatabaseHelper.instance;
|
||||
String _inputText = '';
|
||||
String _oldNotes = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
loadNotes();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Future<bool> _requestPop() {
|
||||
if (_inputText != _oldNotes) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: ivory,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Discard your changes?',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
top: 40,
|
||||
bottom: 10,
|
||||
),
|
||||
child: Wrap(
|
||||
runSpacing: 30,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: 15,
|
||||
),
|
||||
child: ButtonTheme(
|
||||
height: 75,
|
||||
child: RaisedButton(
|
||||
color: peacockBlue,
|
||||
child: Text(
|
||||
"No",
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: 15,
|
||||
),
|
||||
child: ButtonTheme(
|
||||
height: 75,
|
||||
child: RaisedButton(
|
||||
color: peacockBlue,
|
||||
child: Text(
|
||||
"Discard",
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
return Future.value(false);
|
||||
} else {
|
||||
return Future.value(true);
|
||||
}
|
||||
}
|
||||
|
||||
return WillPopScope(
|
||||
onWillPop: _requestPop,
|
||||
child: Scaffold(
|
||||
backgroundColor: ivory,
|
||||
appBar: AppBar(
|
||||
title: const Text('Notes, MarkDown Supported'),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.save),
|
||||
onPressed: () async{
|
||||
Navigator.of(context).pop(this._inputText);
|
||||
Map<String, dynamic> row = {
|
||||
DatabaseHelper.columnMapLocation: widget.mapLocation,
|
||||
DatabaseHelper.columnNotes: this._inputText,
|
||||
};
|
||||
await dbHelper.update(row);
|
||||
}
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget> [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _textEditingController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
maxLines: null,
|
||||
onChanged: (text) {
|
||||
_inputText = text;
|
||||
},
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: <Widget> [
|
||||
Expanded(
|
||||
child: Container(),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.all(20.0),
|
||||
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(
|
||||
'Import',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
),
|
||||
),
|
||||
onPressed: () async{
|
||||
Map<PermissionGroup, PermissionStatus> _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 markdown (or plain text) file.',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: candyApple,
|
||||
),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
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 _contents = await File(_filePath).readAsString();
|
||||
setState(() {
|
||||
this._textEditingController.text = _contents;
|
||||
this._inputText = _contents;
|
||||
});
|
||||
} 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: <Widget>[
|
||||
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<void> loadNotes() async{
|
||||
String _notes = await dbHelper.queryNotes(widget.mapLocation);
|
||||
setState(() {
|
||||
_oldNotes = _notes;
|
||||
_textEditingController.text = _notes;
|
||||
_inputText = _notes;
|
||||
});
|
||||
}
|
||||
}
|
155
lib/elevation.dart
Normal file
155
lib/elevation.dart
Normal file
@@ -0,0 +1,155 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'global_helper_functions.dart';
|
||||
|
||||
final double textHeight = 1.5;
|
||||
|
||||
InkWell elevAtion(BuildContext context, int elevation, int feetElevation) {
|
||||
return InkWell(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Elevation: ',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: candyApple,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
children: <TextSpan>[
|
||||
TextSpan(
|
||||
text: feetElevation.toString(),
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: ' feet, ',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
children: <TextSpan>[
|
||||
TextSpan(
|
||||
text: elevation.toString(),
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: ' meters',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: ivory,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'You have to set up an Open-Elevation Api Server, and specify this in '
|
||||
'settings. The instructions are a little bit out-dated, but it\'s not '
|
||||
'too difficult to muddle through and set something up on a vps or '
|
||||
'RaspberryPi or whatever.\n\n'
|
||||
'A \$5 Digital Ocean Droplet doesn\'t have enough disk space, but '
|
||||
'a \$5 LightSail instance does.',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
top: 40,
|
||||
bottom: 10,
|
||||
),
|
||||
child: ButtonTheme(
|
||||
height: 75,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
),
|
||||
child: RaisedButton(
|
||||
color: peacockBlue,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 150,
|
||||
margin: EdgeInsets.only(
|
||||
top: 10,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'github',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.open_in_browser,
|
||||
size: 48,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
bottom: 15,
|
||||
),
|
||||
child: Text(
|
||||
'open-elevation',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
urlLaunch('https://github.com/Jorl17/open-elevation/blob/master/docs/host-your-own.md');
|
||||
}
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
@@ -1,51 +1,226 @@
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'database_helper.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
final EdgeInsets myBoxPadding = EdgeInsets.all(10.0);
|
||||
final Color navy = Color(0xff00293C);
|
||||
final Color peacock_blue = Color(0xff1e656d);
|
||||
final Color peacockBlue = Color(0xff1e656d);
|
||||
final Color ivory = Color(0xfff1f3ce);
|
||||
final Color candy_apple = Color(0xfff62a00);
|
||||
final String weatherApiUrlPrefix = 'https://api.openweathermap.org/data/2.5/weather?';
|
||||
final String weatherForecastApiUrlPrefix = 'https://api.openweathermap.org/data/2.5/forecast?';
|
||||
final Color candyApple = Color(0xfff62a00);
|
||||
final String weatherApiUrlPrefix =
|
||||
'https://api.openweathermap.org/data/2.5/weather?';
|
||||
final String weatherForecastApiUrlPrefix =
|
||||
'https://api.openweathermap.org/data/2.5/forecast?';
|
||||
|
||||
Future<String> getWeather(String mapLocation, String lat_Long) async {
|
||||
Future<List<int>> refreshTimeOffSet(String mapLocation, String latLong) async {
|
||||
final dbHelper = DatabaseHelper.instance;
|
||||
final int _timeStamp = newTimeStamp();
|
||||
int _isAutoTimeOffset = await dbHelper.queryIsAutoTimeOffSet(mapLocation);
|
||||
String timeZoneApiUrl =
|
||||
'https://api.teleport.org/api/locations/$latLong/?embed=location:nearest-cities/location:nearest-city/city:timezone/tz:offsets-now';
|
||||
Response response;
|
||||
try {
|
||||
response = await get(timeZoneApiUrl);
|
||||
} catch (e) {
|
||||
print('$e in source file global_helper_functions.dart.getTimeOffSet');
|
||||
}
|
||||
if (response.statusCode == 200) {
|
||||
Map<String, dynamic> responseJson;
|
||||
int offSetMin = 2000;
|
||||
try {
|
||||
responseJson = jsonDecode(response.body);
|
||||
offSetMin = responseJson['_embedded']['location:nearest-cities'][0]
|
||||
['_embedded']['location:nearest-city']['_embedded']
|
||||
['city:timezone']['_embedded']['tz:offsets-now']
|
||||
['total_offset_min'] ??
|
||||
2000;
|
||||
} catch (e) {
|
||||
print('$e in source file global_helper_functions.dart.getTimeOffSet, can\'t assign Map<String, dynamic> responseJson');
|
||||
}
|
||||
Map<String, dynamic> row = {
|
||||
DatabaseHelper.columnMapLocation: mapLocation,
|
||||
DatabaseHelper.columnTimeOffSet: offSetMin,
|
||||
DatabaseHelper.columnTimeOffSetTime: _timeStamp
|
||||
};
|
||||
await dbHelper.update(row);
|
||||
return ([offSetMin, _timeStamp, _isAutoTimeOffset]);
|
||||
} else {
|
||||
Map<String, dynamic> row = {
|
||||
DatabaseHelper.columnMapLocation: mapLocation,
|
||||
DatabaseHelper.columnTimeOffSet: 2000,
|
||||
DatabaseHelper.columnTimeOffSetTime: _timeStamp
|
||||
};
|
||||
await dbHelper.update(row);
|
||||
return ([2000, _timeStamp, _isAutoTimeOffset]);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<int>> getTimeOffSet(String mapLocation, String latLong) async{
|
||||
final dbHelper = DatabaseHelper.instance;
|
||||
final int _timeStamp = newTimeStamp();
|
||||
int timeOffSetTime = await dbHelper.queryTimeOffSetTime(mapLocation);
|
||||
int _isAutoTimeOffset = await dbHelper.queryIsAutoTimeOffSet(mapLocation);
|
||||
if ((_timeStamp - timeOffSetTime) > 604800) {
|
||||
String timeZoneApiUrl =
|
||||
'https://api.teleport.org/api/locations/$latLong/?embed=location:nearest-cities/location:nearest-city/city:timezone/tz:offsets-now';
|
||||
Response response;
|
||||
try {
|
||||
response = await get(timeZoneApiUrl);
|
||||
} catch (e) {
|
||||
print('$e in source file global_helper_functions.dart.getTimeOffSet');
|
||||
int timeOffSet = await dbHelper.queryTimeOffSet(mapLocation);
|
||||
return ([timeOffSet, timeOffSetTime, _isAutoTimeOffset]);
|
||||
}
|
||||
if (response.statusCode == 200) {
|
||||
Map<String, dynamic> responseJson;
|
||||
int offSetMin = 2000;
|
||||
try {
|
||||
responseJson = jsonDecode(response.body);
|
||||
offSetMin = responseJson['_embedded']['location:nearest-cities'][0]
|
||||
['_embedded']['location:nearest-city']['_embedded']
|
||||
['city:timezone']['_embedded']['tz:offsets-now']
|
||||
['total_offset_min'] ??
|
||||
2000;
|
||||
} catch (e) {
|
||||
print('$e in source file global_helper_functions.dart.getTimeOffSet, can\'t assign Map<String, dynamic> responseJson');
|
||||
}
|
||||
Map<String, dynamic> row = {
|
||||
DatabaseHelper.columnMapLocation: mapLocation,
|
||||
DatabaseHelper.columnTimeOffSet: offSetMin,
|
||||
DatabaseHelper.columnTimeOffSetTime: _timeStamp
|
||||
};
|
||||
await dbHelper.update(row);
|
||||
return ([offSetMin, _timeStamp, _isAutoTimeOffset]);
|
||||
} else {
|
||||
Map<String, dynamic> row = {
|
||||
DatabaseHelper.columnMapLocation: mapLocation,
|
||||
DatabaseHelper.columnTimeOffSet: 2000,
|
||||
DatabaseHelper.columnTimeOffSetTime: _timeStamp
|
||||
};
|
||||
await dbHelper.update(row);
|
||||
return ([2000, _timeStamp, _isAutoTimeOffset]);
|
||||
}
|
||||
}
|
||||
int timeOffSet = await dbHelper.queryTimeOffSet(mapLocation);
|
||||
return ([timeOffSet, timeOffSetTime, _isAutoTimeOffset]);
|
||||
}
|
||||
|
||||
// should be similar to getWeatherForeCast();
|
||||
Future<String> getWeather(
|
||||
String mapLocation, String lattLong, bool stale) async {
|
||||
// https://dragosholban.com/2018/07/01/how-to-build-a-simple-weather-app-in-flutter
|
||||
final dbHelper = DatabaseHelper.instance;
|
||||
String weatherApiID = await getPreferenceOpenWeatherMapApiKey();
|
||||
if (weatherApiID == 'none?') { return null;}
|
||||
else {
|
||||
if (weatherApiID == 'none?') {
|
||||
return null;
|
||||
} else {
|
||||
int weatherID = await dbHelper.queryWeatherID(mapLocation);
|
||||
if (weatherID == null) {
|
||||
List<String> latLong = lat_Long.split(',');
|
||||
String weatherApiUrl = '${weatherApiUrlPrefix}lat=${latLong[0]}&lon=${latLong[1]}&units=imperial&appid=${weatherApiID}';
|
||||
Response response = await get(weatherApiUrl);
|
||||
if (response.statusCode == 200) {
|
||||
Map<String, dynamic> responseJson = jsonDecode(response.body);
|
||||
weatherID = responseJson['id'];
|
||||
Map<String, dynamic> row = {
|
||||
DatabaseHelper.columnMapLocation: mapLocation,
|
||||
DatabaseHelper.columnWeatherID: weatherID
|
||||
};
|
||||
await dbHelper.update(row);
|
||||
Map<String, dynamic> row2 = {
|
||||
DatabaseHelper.columnWeatherWeatherID: weatherID,
|
||||
DatabaseHelper.columnWeather: response.body
|
||||
};
|
||||
int weatherIDExists = await dbHelper.queryWeatherIDExists(weatherID);
|
||||
if (weatherIDExists == 0) {
|
||||
dbHelper.insertWeatherRow(row2);
|
||||
} else {
|
||||
dbHelper.updateWeathTbl(row2);
|
||||
if ((weatherID == null) || (stale == true)) {
|
||||
List<String> latLong = lattLong.split(',');
|
||||
String weatherApiUrl;
|
||||
if ((weatherID == null) || (weatherID < 0)) {
|
||||
weatherApiUrl =
|
||||
'${weatherApiUrlPrefix}lat=${latLong[0]}&lon=${latLong[1]}&units=imperial&appid=$weatherApiID';
|
||||
} else {
|
||||
weatherApiUrl =
|
||||
'${weatherApiUrlPrefix}id=$weatherID&units=imperial&appid=$weatherApiID';
|
||||
}
|
||||
try {
|
||||
Response response = await get(weatherApiUrl);
|
||||
if (response.statusCode == 200) {
|
||||
Map<String, dynamic> responseJson = jsonDecode(response.body);
|
||||
weatherID = (responseJson['id'] != 0) ? responseJson['id'] : generateFakeWeatherId(lattLong);
|
||||
Map<String, dynamic> row = {
|
||||
DatabaseHelper.columnMapLocation: mapLocation,
|
||||
DatabaseHelper.columnWeatherID: weatherID
|
||||
};
|
||||
await dbHelper.update(row);
|
||||
Map<String, dynamic> row2 = {
|
||||
DatabaseHelper.columnWeatherWeatherID: weatherID,
|
||||
DatabaseHelper.columnWeather: response.body
|
||||
};
|
||||
int weatherIDExists = await dbHelper.queryWeatherIDExists(weatherID);
|
||||
if (weatherIDExists == 0) {
|
||||
dbHelper.insertWeatherRow(row2);
|
||||
} else {
|
||||
dbHelper.updateWeathTbl(row2);
|
||||
}
|
||||
return response.body;
|
||||
}
|
||||
return response.body;
|
||||
} catch(e) {
|
||||
print('$e, can\'t connect to openweathermap in global_helper_functions');
|
||||
weatherID ??= generateFakeWeatherId(lattLong); // assign if null
|
||||
String weather = await dbHelper.queryWeather(weatherID);
|
||||
return weather ?? 'NA';
|
||||
}
|
||||
} else {
|
||||
String weather = await dbHelper.queryWeather(weatherID);
|
||||
return(weather);
|
||||
return weather ?? 'NA';
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// should be similar to getWeather();
|
||||
Future<String> getWeatherForeCast(
|
||||
String mapLocation, String lattLong, bool stale) async {
|
||||
// https://dragosholban.com/2018/07/01/how-to-build-a-simple-weather-app-in-flutter
|
||||
final dbHelper = DatabaseHelper.instance;
|
||||
String weatherApiID = await getPreferenceOpenWeatherMapApiKey();
|
||||
if (weatherApiID == 'none?') {
|
||||
return null;
|
||||
} else {
|
||||
int weatherID = await dbHelper.queryWeatherID(mapLocation);
|
||||
if (weatherID == 0) {
|
||||
weatherID = generateFakeWeatherId(lattLong);
|
||||
}
|
||||
int weatherIDExists = await dbHelper.queryWeatherIDExists(weatherID);
|
||||
if (weatherIDExists == 0) { stale = true; }
|
||||
if ((weatherID == null) || (stale == true)) {
|
||||
List<String> latLong = lattLong.split(',');
|
||||
String weatherForeCastApiUrl;
|
||||
if ((weatherID == null) || (weatherID < 0)) {
|
||||
weatherForeCastApiUrl =
|
||||
'${weatherForecastApiUrlPrefix}lat=${latLong[0]}&lon=${latLong[1]}&units=imperial&appid=$weatherApiID';
|
||||
} else {
|
||||
weatherForeCastApiUrl =
|
||||
'${weatherForecastApiUrlPrefix}id=$weatherID&units=imperial&appid=$weatherApiID';
|
||||
}
|
||||
try {
|
||||
Response response = await get(weatherForeCastApiUrl);
|
||||
if (response.statusCode == 200) {
|
||||
Map<String, dynamic> responseJson = jsonDecode(response.body);
|
||||
weatherID = responseJson['city']['id'];
|
||||
weatherID ??= generateFakeWeatherId(lattLong); // assign if null
|
||||
Map<String, dynamic> row = {
|
||||
DatabaseHelper.columnMapLocation: mapLocation,
|
||||
DatabaseHelper.columnWeatherID: weatherID
|
||||
};
|
||||
await dbHelper.update(row);
|
||||
Map<String, dynamic> row2 = {
|
||||
DatabaseHelper.columnWeatherWeatherID: weatherID,
|
||||
DatabaseHelper.columnWeatherForecast: response.body
|
||||
};
|
||||
int weatherIDExists = await dbHelper.queryWeatherIDExists(weatherID);
|
||||
if (weatherIDExists == 0) {
|
||||
dbHelper.insertWeatherRow(row2);
|
||||
} else {
|
||||
dbHelper.updateWeathTbl(row2);
|
||||
}
|
||||
return response.body;
|
||||
}
|
||||
} catch(e) {
|
||||
print('$e, probably cant connect to openweathermap... no network connection? global_helper_functions.getWeatherForeCast');
|
||||
weatherID ??= generateFakeWeatherId(lattLong); // assign if null
|
||||
String weatherForeCast = await dbHelper.queryWeatherForeCast(weatherID);
|
||||
return (weatherForeCast);
|
||||
}
|
||||
} else {
|
||||
String weatherForeCast = await dbHelper.queryWeatherForeCast(weatherID);
|
||||
return (weatherForeCast);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -55,45 +230,130 @@ BoxDecoration myBoxDecoration(Color color) {
|
||||
return BoxDecoration(
|
||||
color: color,
|
||||
border: Border.all(
|
||||
width: 2.0,
|
||||
width: 2.0,
|
||||
),
|
||||
borderRadius: new BorderRadius.all(new Radius.circular(6.0)),
|
||||
boxShadow: [
|
||||
new BoxShadow(
|
||||
offset: new Offset(2.0, 1.0),
|
||||
blurRadius: 1.0,
|
||||
spreadRadius: 1.0,
|
||||
)
|
||||
],
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
offset: Offset(2.0, 1.0),
|
||||
blurRadius: 1.0,
|
||||
spreadRadius: 1.0,
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
int newTimeStamp() {
|
||||
DateTime date = new DateTime.now();
|
||||
return (date.millisecondsSinceEpoch / 1000).floor();
|
||||
DateTime date = DateTime.now();
|
||||
return (date.millisecondsSinceEpoch / 1000).floor();
|
||||
}
|
||||
|
||||
Future<String> parseMapUrl(String mapUrl) async {
|
||||
Response response = await get(mapUrl);
|
||||
RegExp gmapUrl = new RegExp(r'https://www.google.com/(maps/preview)/(place)/([^/]*)/([^/]*)/data');
|
||||
String mapInfo = response.body;
|
||||
Match match = gmapUrl.firstMatch(mapInfo);
|
||||
String subMapInfo = match.group(4);
|
||||
RegExp subGmapUrl = new RegExp(r'@([^,]*,[^,]*),([^,]*,[^,]*)');
|
||||
Match subMatch = subGmapUrl.firstMatch(subMapInfo);
|
||||
return subMatch.group(1);
|
||||
try {
|
||||
Response response = await get(mapUrl);
|
||||
RegExp gmapUrl = RegExp(
|
||||
r'https://www.google.com/(maps/preview)/(place)/([^/]*)/([^/]*)/data');
|
||||
String mapInfo = response.body;
|
||||
Match match = gmapUrl.firstMatch(mapInfo);
|
||||
String subMapInfo = match.group(4);
|
||||
RegExp subGmapUrl = RegExp(r'@([^,]*,[^,]*),([^,]*,[^,]*)');
|
||||
Match subMatch = subGmapUrl.firstMatch(subMapInfo);
|
||||
return subMatch.group(1);
|
||||
} catch(e) {
|
||||
print('$e can\'t connect to internet in global_helper_functions.parseMapUrl');
|
||||
}
|
||||
}
|
||||
|
||||
Future<int> fetchElevation(String mapLocation) async {
|
||||
final dbHelper = DatabaseHelper.instance;
|
||||
String latLong = await dbHelper.queryLatNLong(mapLocation);
|
||||
if (latLong == 'NA') {
|
||||
return null;
|
||||
} else {
|
||||
String elevationServer = await getPreferenceElevationServer();
|
||||
String elevationApiUrl =
|
||||
elevationServer + '/api/v1/lookup\?locations\=' + latLong;
|
||||
Response response;
|
||||
try {
|
||||
response = await get(elevationApiUrl);
|
||||
} catch (e) {
|
||||
print('$e in source file global_helper_functions.dart.fetchElevation');
|
||||
return null;
|
||||
}
|
||||
if (response.statusCode == 200) {
|
||||
Map<String, dynamic> responseJson = jsonDecode(response.body);
|
||||
int elevation = responseJson['results'][0]['elevation'];
|
||||
return elevation;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String convertCoordinates(String latNLong) {
|
||||
try {
|
||||
List<String> latLong = latNLong.split(',');
|
||||
List<String> lat = latLong[0].split('.');
|
||||
List<String> long = latLong[1].split('.');
|
||||
String latMinSec = getMinSec(lat[1]);
|
||||
String longMinSec = getMinSec(long[1]);
|
||||
String gpsDMS =
|
||||
lat[0] + '\u00B0 ' + latMinSec + ',' + long[0] + '\u00B0 ' + longMinSec;
|
||||
return gpsDMS;
|
||||
} catch(e) {
|
||||
print('$e in source file global_helper_functions.convertCoordinates, function parameter probably not a valid latNLong string');
|
||||
}
|
||||
}
|
||||
|
||||
String getMinSec(String numString) {
|
||||
String numStringX = '0.' + numString;
|
||||
double num = double.parse(numStringX);
|
||||
int mins = (num * 100 * 0.6).floor();
|
||||
double secs = ((((num * 100 * 0.6) % 1) * 100000 * .6).round() / 1000);
|
||||
String minSecs = mins.toString() + '\' ' + secs.toString();
|
||||
return minSecs;
|
||||
}
|
||||
|
||||
|
||||
String weatherConditions(String weatherConditions) {
|
||||
int numSpaces = (' '.allMatches(weatherConditions)).length;
|
||||
if (numSpaces > 1) {
|
||||
int firstSpace = weatherConditions.indexOf(' ');
|
||||
int secondSpace = (weatherConditions.indexOf(' ', firstSpace + 1));
|
||||
return weatherConditions.replaceRange(secondSpace, (secondSpace + 1), '\n').toUpperCase();
|
||||
} else {
|
||||
return weatherConditions.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> urlLaunch(String url) async{
|
||||
if (await canLaunch(url)) {
|
||||
await launch(url);
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
}
|
||||
|
||||
int generateFakeWeatherId(String latLong) {
|
||||
List<String> _latLongParts = latLong.split(',');
|
||||
List<String> _shortLat = _latLongParts[0].split('.');
|
||||
List<String> _shortLong = _latLongParts[1].split('.');
|
||||
String _fakeIDString = _shortLat[0].replaceFirst('-','8') + _shortLong[0].replaceFirst('-','8');
|
||||
// you know a weatherID is fake because it is less than zero
|
||||
return (0 - int.parse(_fakeIDString));
|
||||
}
|
||||
|
||||
Future<String> getPreferenceElevationServer() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
String elevationServer = prefs.getString("elevationServer") ?? "https://example.com";
|
||||
String elevationServer =
|
||||
prefs.getString("elevationServer") ?? "https://example.com";
|
||||
return elevationServer;
|
||||
}
|
||||
|
||||
Future<bool> setPreferenceElevationServer(String elevationServer) async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
prefs.setString("elevationServer",elevationServer);
|
||||
return prefs.commit();
|
||||
bool committed = await prefs.setString("elevationServer", elevationServer);
|
||||
return (committed);
|
||||
}
|
||||
|
||||
Future<String> getPreferenceOpenWeatherMapApiKey() async {
|
||||
@@ -104,41 +364,30 @@ Future<String> getPreferenceOpenWeatherMapApiKey() async {
|
||||
|
||||
Future<bool> setPreferenceOpenWeatherMapApiKey(String apiKey) async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
prefs.setString("openWeatherMapApiKey",apiKey);
|
||||
return prefs.commit();
|
||||
bool committed = await prefs.setString("openWeatherMapApiKey", apiKey);
|
||||
return (committed);
|
||||
}
|
||||
|
||||
Future<int> fetchElevation(String mapLocation) async {
|
||||
final dbHelper = DatabaseHelper.instance;
|
||||
String latLong = await dbHelper.queryLatNLong(mapLocation);
|
||||
if (latLong == 'NA') { return null;}
|
||||
else {
|
||||
String elevationServer = await getPreferenceElevationServer();
|
||||
String elevationApiUrl = elevationServer + '/api/v1/lookup\?locations\=' + latLong;
|
||||
Response response = await get(elevationApiUrl);
|
||||
if (response.statusCode == 200) {
|
||||
Map<String, dynamic> responseJson = jsonDecode(response.body);
|
||||
int elevation = responseJson['results'][0]['elevation'];
|
||||
return elevation;
|
||||
} else {return null;}
|
||||
}
|
||||
Future<bool> getPreferenceUseElevation() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
bool _useElev = prefs.getBool("useElevation") ?? false;
|
||||
return _useElev;
|
||||
}
|
||||
|
||||
String convertCoordinates(String latNLong) {
|
||||
List<String> latLong = latNLong.split(',');
|
||||
List<String> lat = latLong[0].split('.');
|
||||
List<String> long = latLong[1].split('.');
|
||||
String latMinSec = getMinSec(lat[1]);
|
||||
String longMinSec = getMinSec(long[1]);
|
||||
String gpsDMS = lat[0] + '\u00B0 ' + latMinSec + ',' + long[0] + '\u00B0 ' + longMinSec;
|
||||
return gpsDMS;
|
||||
Future<bool> setPreferenceUseElevation(bool useElev) async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
bool committed = await prefs.setBool("useElevation", useElev);
|
||||
return (committed);
|
||||
}
|
||||
|
||||
String getMinSec(String numString) {
|
||||
String numString_x = '0.' + numString;
|
||||
double num = double.parse(numString_x);
|
||||
int mins = (num * 100 * 0.6).floor();
|
||||
double secs = ((((num * 100 * 0.6) % 1) * 100000 * .6).round() / 1000);
|
||||
String minSecs = mins.toString() + '\' ' + secs.toString();
|
||||
return minSecs;
|
||||
Future<bool> getPreferenceUseWeather() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
bool _useElev = prefs.getBool("useWeather") ?? false;
|
||||
return _useElev;
|
||||
}
|
||||
|
||||
Future<bool> setPreferenceUseWeather(bool useWeather) async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
bool committed = await prefs.setBool("useWeather", useWeather);
|
||||
return (committed);
|
||||
}
|
||||
|
129
lib/lnl_dec.dart
Normal file
129
lib/lnl_dec.dart
Normal file
@@ -0,0 +1,129 @@
|
||||
import 'package:android_intent/android_intent.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'global_helper_functions.dart';
|
||||
import 'package:share/share.dart';
|
||||
|
||||
final double textHeight = 1.5;
|
||||
|
||||
Row lnlDec(String latnLong) {
|
||||
|
||||
Future<void> _launchLnL() async{
|
||||
AndroidIntent intent = AndroidIntent(
|
||||
action: 'action_view',
|
||||
data: Uri.encodeFull('geo:$latnLong?z=12'),
|
||||
package: 'com.google.android.apps.maps',
|
||||
);
|
||||
await intent.launch();
|
||||
}
|
||||
|
||||
List<String> _latNLong = ['x','y'];
|
||||
if ((latnLong == 'none') || (latnLong == null)) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Check Data Connection',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Probably Offline',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else if (!(latnLong.contains(','))) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Check Data Connection',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Probably Offline',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
_latNLong = latnLong.split(',');
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.share),
|
||||
tooltip: 'share link to another app',
|
||||
iconSize: 48,
|
||||
onPressed: () {
|
||||
Share.share(latnLong);
|
||||
},
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Decimal',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: candyApple,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
_latNLong[0],
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
_latNLong[1],
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.map),
|
||||
tooltip: 'share link to maps',
|
||||
iconSize: 48,
|
||||
onPressed: () {
|
||||
_launchLnL();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
108
lib/lnl_deg.dart
Normal file
108
lib/lnl_deg.dart
Normal file
@@ -0,0 +1,108 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'global_helper_functions.dart';
|
||||
import 'package:share/share.dart';
|
||||
|
||||
final double textHeight = 1.5;
|
||||
|
||||
Row lnlDeg(String latnLongDMS) {
|
||||
List<String> _latNLong = ['x','y'];
|
||||
if ((latnLongDMS == 'none') || (latnLongDMS == null)) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Check Data Connection',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Probably Offline',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else if (!(latnLongDMS.contains(','))) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Check Data Connection',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Probably Offline',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
_latNLong = latnLongDMS.split(',');
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.share),
|
||||
tooltip: 'share link to another app',
|
||||
iconSize: 48,
|
||||
onPressed: () {
|
||||
Share.share(latnLongDMS);
|
||||
},
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Deg, Min, Sec',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: candyApple,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
_latNLong[0],
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
_latNLong[1],
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
218
lib/location.dart
Normal file
218
lib/location.dart
Normal file
@@ -0,0 +1,218 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:android_intent/android_intent.dart';
|
||||
import 'package:share/share.dart';
|
||||
import 'global_helper_functions.dart';
|
||||
|
||||
class Location extends StatefulWidget {
|
||||
final String mapLocation;
|
||||
|
||||
Location({
|
||||
Key key,
|
||||
this.mapLocation,
|
||||
}) : super(key: key);
|
||||
@override
|
||||
_LocationState createState() => _LocationState();
|
||||
}
|
||||
|
||||
class _LocationState extends State<Location> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double textHeight = 1.5;
|
||||
List<String> locationStringList = _getlocationStringList(widget.mapLocation);
|
||||
|
||||
Future<void> _launchUrl() async{
|
||||
AndroidIntent intent = AndroidIntent(
|
||||
action: 'action_view',
|
||||
data: Uri.encodeFull(locationStringList[locationStringList.length - 1]),
|
||||
);
|
||||
await intent.launch();
|
||||
}
|
||||
|
||||
List<Container> createLocation(List<String> location) {
|
||||
return location.map((line) {
|
||||
line = (line.length < 25) ? line :
|
||||
(line.substring(0,20) + '.....' );
|
||||
return Container(
|
||||
child: Text(
|
||||
line,
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
fontSize: 16.0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.share),
|
||||
tooltip: 'share link to another app',
|
||||
iconSize: 48,
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: ivory,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
),
|
||||
title: Text(
|
||||
'Sharing Options',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: candyApple,
|
||||
),
|
||||
),
|
||||
content: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.share),
|
||||
tooltip: 'share link to another app',
|
||||
iconSize: 48,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
Share.share(widget.mapLocation);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.map),
|
||||
tooltip: 'share link to maps',
|
||||
iconSize: 48,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
_launchUrl();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 7,
|
||||
child: InkWell(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: createLocation(locationStringList),
|
||||
),
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: ivory,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
bottom: 25,
|
||||
),
|
||||
child: Text(
|
||||
widget.mapLocation,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
RaisedButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
),
|
||||
color: peacockBlue,
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(
|
||||
top: 12,
|
||||
bottom: 20,
|
||||
),
|
||||
child: Text(
|
||||
'ok',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
} catch(e) {
|
||||
print('$e, probably could not call createLocation(locationStringList)');
|
||||
return Wrap(
|
||||
spacing: 20.0,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Pending...',
|
||||
style: TextStyle(height: textHeight),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
List<String> _getlocationStringList(String mapLocation) {
|
||||
List<String> stringList;
|
||||
try {
|
||||
stringList = mapLocation.split('\n');
|
||||
} catch(e) {
|
||||
print('$e in source file location.dart._getlocationStringList::first_try');
|
||||
}
|
||||
if (stringList.length < 2) {
|
||||
return ['','pending...',''];
|
||||
} else {
|
||||
try {
|
||||
if (stringList[1].contains(stringList[0])) {
|
||||
stringList.removeAt(0);
|
||||
}
|
||||
} catch(e) {
|
||||
print('$e in source file location.dart._getlocationStringList::second_try');
|
||||
}
|
||||
// split up long address lines on the second to last comma
|
||||
for (int i=0; i<stringList.length; i++) {
|
||||
int numCommas = (','.allMatches(stringList[i])).length;
|
||||
if (numCommas > 1) {
|
||||
String currentLine = stringList[i];
|
||||
stringList.removeAt(i);
|
||||
int commaCount = 0;
|
||||
for (int j=0; j<currentLine.length; j++) {
|
||||
if (currentLine[j] == ',') {
|
||||
commaCount += 1;
|
||||
if (commaCount == (numCommas -1)) {
|
||||
String firstHalf = currentLine.substring(0,j+1);
|
||||
String secondHalf = currentLine.substring(j+2);
|
||||
stringList.insert(i, firstHalf);
|
||||
stringList.insert(i+1, secondHalf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return stringList;
|
||||
}
|
||||
}
|
||||
}
|
1150
lib/main.dart
1150
lib/main.dart
File diff suppressed because it is too large
Load Diff
296
lib/render_notes.dart
Normal file
296
lib/render_notes.dart
Normal file
@@ -0,0 +1,296 @@
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'global_helper_functions.dart';
|
||||
import 'dart:io';
|
||||
|
||||
class RenderNotes extends StatefulWidget {
|
||||
final String notes;
|
||||
|
||||
RenderNotes({
|
||||
Key key,
|
||||
this.notes,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_RenderNotesState createState() => _RenderNotesState();
|
||||
}
|
||||
|
||||
class _RenderNotesState extends State<RenderNotes> {
|
||||
final double textHeight = 1.5;
|
||||
final _textEditingController = TextEditingController();
|
||||
String _exportFileNameString = '';
|
||||
String fileName = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: ivory,
|
||||
body: CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
SliverAppBar(
|
||||
floating: true,
|
||||
snap: false,
|
||||
pinned: false,
|
||||
expandedHeight: 50,
|
||||
title: const Text('Notes, MarkDown Supported'),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget> [
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: 10.0,
|
||||
),
|
||||
child: MarkdownBody(
|
||||
data: widget.notes,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: <Widget> [
|
||||
Expanded(
|
||||
child: Container(),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.all(20.0),
|
||||
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{
|
||||
Map<PermissionGroup, PermissionStatus> _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(
|
||||
'File Name To Save As?',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: candyApple,
|
||||
),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
TextField(
|
||||
controller: _textEditingController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
maxLines: null,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'i.e. timbuktu.md',
|
||||
),
|
||||
onChanged: (text) {
|
||||
_exportFileNameString = text;
|
||||
},
|
||||
),
|
||||
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{
|
||||
if (this._exportFileNameString.length > 0) {
|
||||
String _fileName = '${sdcard.path}/${this._exportFileNameString}';
|
||||
File _file = File(_fileName);
|
||||
File _result = await _file.writeAsString(widget.notes);
|
||||
Navigator.of(context).pop();
|
||||
if (_result == null) {
|
||||
setState(() {
|
||||
this.fileName = _fileName;
|
||||
});
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: ivory,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
),
|
||||
title: Text(
|
||||
'Writing To\n\'$fileName\'\n(sdcard) Failed!',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: candyApple,
|
||||
),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
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 {
|
||||
setState(() {
|
||||
this.fileName = _fileName;
|
||||
});
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: ivory,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
),
|
||||
title: Text(
|
||||
'Writing To\n\'$fileName\'\n(sdcard) Succeeded!',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: candyApple,
|
||||
),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
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();
|
||||
}
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,18 +1,20 @@
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'global_helper_functions.dart';
|
||||
|
||||
class Settings extends StatefulWidget {
|
||||
@override
|
||||
_SettingsState createState() => new _SettingsState();
|
||||
_SettingsState createState() => _SettingsState();
|
||||
}
|
||||
|
||||
class _SettingsState extends State<Settings> {
|
||||
String elevationServer = "none";
|
||||
String openWeatherMapKey = "none";
|
||||
String openWeatherMapKey = "none??";
|
||||
String shortOWMAK = "none";
|
||||
final _elevationServerController = TextEditingController();
|
||||
final _oWMKController = TextEditingController();
|
||||
bool _useElevation = false;
|
||||
bool _useWeather = false;
|
||||
double textHeight = 1.5;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -22,132 +24,218 @@ class _SettingsState extends State<Settings> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Scaffold(
|
||||
backgroundColor: peacock_blue,
|
||||
appBar: new AppBar(
|
||||
title: const Text('Settings'),
|
||||
),
|
||||
body: new ListView(
|
||||
children: <Widget> [
|
||||
new Container(
|
||||
padding: myBoxPadding,
|
||||
decoration: myBoxDecoration(ivory),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
'Open-Elevation Api Server to Use',
|
||||
),
|
||||
Text(
|
||||
'Current Value: ' + this.elevationServer,
|
||||
),
|
||||
TextField(
|
||||
controller: _elevationServerController,
|
||||
decoration: InputDecoration(
|
||||
hintText: this.elevationServer,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
top: 20.0,
|
||||
bottom: 5.0,
|
||||
),
|
||||
child: RaisedButton.icon(
|
||||
label: Text(
|
||||
"Update Elevation Server",
|
||||
),
|
||||
icon: Icon(
|
||||
Icons.refresh,
|
||||
size: 50.0,
|
||||
),
|
||||
color: candy_apple,
|
||||
onPressed: () { _updateElevationServer(); },
|
||||
return WillPopScope(
|
||||
onWillPop: _popBack,
|
||||
child: Scaffold(
|
||||
backgroundColor: peacockBlue,
|
||||
appBar: AppBar(
|
||||
title: const Text('Settings'),
|
||||
),
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: myBoxPadding,
|
||||
decoration: myBoxDecoration(ivory),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Enable Elevation Api Server?',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
new Container(
|
||||
padding: myBoxPadding,
|
||||
decoration: myBoxDecoration(ivory),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
'Open Weather Map Api Key to Use',
|
||||
),
|
||||
Text(
|
||||
'Current Value: ${this.shortOWMAK}...',
|
||||
),
|
||||
TextField(
|
||||
controller: _oWMKController,
|
||||
decoration: InputDecoration(
|
||||
hintText: '${this.shortOWMAK}...',
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
top: 20.0,
|
||||
bottom: 5.0,
|
||||
),
|
||||
child: RaisedButton.icon(
|
||||
label: Text(
|
||||
"Update ... Api Key",
|
||||
),
|
||||
icon: Icon(
|
||||
Icons.refresh,
|
||||
size: 50.0,
|
||||
),
|
||||
color: candy_apple,
|
||||
onPressed: () { _updateOpenWeatherMapApiKey(); },
|
||||
Container(
|
||||
padding: EdgeInsets.all(15.0),
|
||||
child: Transform.scale(
|
||||
scale: 2,
|
||||
child: Switch(
|
||||
value: this._useElevation,
|
||||
onChanged: (value) {
|
||||
setPreferenceUseElevation(value).then((bool committed) {
|
||||
setState(() {
|
||||
this._useElevation = value;
|
||||
});
|
||||
});
|
||||
},
|
||||
activeTrackColor: peacockBlue,
|
||||
activeColor: navy,
|
||||
inactiveThumbColor: candyApple,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
((this._useElevation) ? Container(
|
||||
padding: myBoxPadding,
|
||||
decoration: myBoxDecoration(ivory),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
'Open-Elevation Api Server to Use',
|
||||
),
|
||||
Text(
|
||||
'Current Value: ${this.elevationServer}',
|
||||
),
|
||||
TextField(
|
||||
controller: _elevationServerController,
|
||||
decoration: InputDecoration(
|
||||
hintText: this.elevationServer,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
top: 20.0,
|
||||
bottom: 5.0,
|
||||
),
|
||||
child: RaisedButton.icon(
|
||||
label: Text(
|
||||
"Update Elevation Server",
|
||||
),
|
||||
icon: Icon(
|
||||
Icons.refresh,
|
||||
size: 50.0,
|
||||
),
|
||||
color: candyApple,
|
||||
onPressed: () {
|
||||
_updateElevationServer();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
) : Container()),
|
||||
Container(
|
||||
padding: myBoxPadding,
|
||||
decoration: myBoxDecoration(ivory),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Use OpenWeatherMap Weather?',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.all(15.0),
|
||||
child: Transform.scale(
|
||||
scale: 2,
|
||||
child: Switch(
|
||||
value: this._useWeather,
|
||||
onChanged: (value) {
|
||||
setPreferenceUseWeather(value).then((bool committed) {
|
||||
setState(() {
|
||||
this._useWeather = value;
|
||||
});
|
||||
});
|
||||
},
|
||||
activeTrackColor: peacockBlue,
|
||||
activeColor: navy,
|
||||
inactiveThumbColor: candyApple,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
((this._useWeather) ? Container(
|
||||
padding: myBoxPadding,
|
||||
decoration: myBoxDecoration(ivory),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
'Open Weather Map Api Key to Use',
|
||||
),
|
||||
Text(
|
||||
'Current Value: ${this.shortOWMAK}...',
|
||||
),
|
||||
TextField(
|
||||
controller: _oWMKController,
|
||||
decoration: InputDecoration(
|
||||
hintText: '${this.shortOWMAK}...',
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
top: 20.0,
|
||||
bottom: 5.0,
|
||||
),
|
||||
child: RaisedButton.icon(
|
||||
label: Text(
|
||||
"Update ... Api Key",
|
||||
),
|
||||
icon: Icon(
|
||||
Icons.refresh,
|
||||
size: 50.0,
|
||||
),
|
||||
color: candyApple,
|
||||
onPressed: () {
|
||||
_updateOpenWeatherMapApiKey();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
) : Container()),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> updateState() async {
|
||||
String evServer = await getPreferenceElevationServer();
|
||||
String oWMK = await getPreferenceOpenWeatherMapApiKey();
|
||||
if ((evServer != this.elevationServer) || (oWMK != this.openWeatherMapKey)) {
|
||||
setState((){
|
||||
this.elevationServer = evServer;
|
||||
this.openWeatherMapKey = oWMK;
|
||||
this.shortOWMAK = oWMK.substring(0,5);
|
||||
});
|
||||
}
|
||||
String _evServer = await getPreferenceElevationServer();
|
||||
String _oWMK = await getPreferenceOpenWeatherMapApiKey();
|
||||
bool _useElev = await getPreferenceUseElevation();
|
||||
bool _useWTHR = await getPreferenceUseWeather();
|
||||
setState(() {
|
||||
this.elevationServer = _evServer;
|
||||
this.openWeatherMapKey = _oWMK;
|
||||
this._useElevation = _useElev;
|
||||
this._useWeather = _useWTHR;
|
||||
this.shortOWMAK = _oWMK.substring(0, 5);
|
||||
});
|
||||
}
|
||||
|
||||
void _updateOpenWeatherMapApiKey() {
|
||||
String oWMK = _oWMKController.text;
|
||||
if (oWMK.length > 0) {
|
||||
setPreferenceOpenWeatherMapApiKey(oWMK).then((bool committed) {
|
||||
if (oWMK != this.openWeatherMapKey) {
|
||||
setState((){
|
||||
this.openWeatherMapKey = oWMK;
|
||||
this.shortOWMAK = oWMK.substring(0,5);
|
||||
String _oWMK = _oWMKController.text;
|
||||
if (_oWMK.length > 5) {
|
||||
setPreferenceOpenWeatherMapApiKey(_oWMK).then((bool committed) {
|
||||
if (_oWMK != this.openWeatherMapKey) {
|
||||
setState(() {
|
||||
this.openWeatherMapKey = _oWMK;
|
||||
this.shortOWMAK = _oWMK.substring(0, 5);
|
||||
});
|
||||
}
|
||||
});
|
||||
Navigator.pop(context);
|
||||
Navigator.pop(context, 2);
|
||||
}
|
||||
}
|
||||
|
||||
void _updateElevationServer() {
|
||||
String elevServer = _elevationServerController.text;
|
||||
RegExp urlValidator = new RegExp(r'^http[s]?://.+$');
|
||||
if (urlValidator.hasMatch(elevServer)) {
|
||||
setPreferenceElevationServer(elevServer).then((bool committed) {
|
||||
if (elevServer != this.elevationServer) {
|
||||
setState((){
|
||||
this.elevationServer = elevServer;
|
||||
String _elevServer = _elevationServerController.text;
|
||||
RegExp _urlValidator = RegExp(r'^http[s]?://.+$');
|
||||
if (_urlValidator.hasMatch(_elevServer)) {
|
||||
setPreferenceElevationServer(_elevServer).then((bool committed) {
|
||||
if (_elevServer != this.elevationServer) {
|
||||
setState(() {
|
||||
this.elevationServer = _elevServer;
|
||||
});
|
||||
}
|
||||
});
|
||||
Navigator.pop(context);
|
||||
Navigator.pop(context, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _popBack() {
|
||||
Navigator.pop(context, 3);
|
||||
return Future.value(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
53
lib/street_view.dart
Normal file
53
lib/street_view.dart
Normal file
@@ -0,0 +1,53 @@
|
||||
import 'package:android_intent/android_intent.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'global_helper_functions.dart';
|
||||
|
||||
final double textHeight = 1.5;
|
||||
|
||||
InkWell streetView(String latnLong) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
AndroidIntent _intent = AndroidIntent(
|
||||
action: 'action_view',
|
||||
data: Uri.encodeFull('google.streetview:cbll=$latnLong'),
|
||||
package: 'com.google.android.apps.maps');
|
||||
_intent.launch();
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
Align(
|
||||
child: Container(
|
||||
width: 50,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Street',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: candyApple,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'View',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: candyApple,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.streetview,
|
||||
size: 48.0,
|
||||
color: Colors.black,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
371
lib/timezone.dart
Normal file
371
lib/timezone.dart
Normal file
@@ -0,0 +1,371 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'global_helper_functions.dart';
|
||||
import 'timezones.dart';
|
||||
|
||||
class TimeZone extends StatefulWidget {
|
||||
final int timeOffSet;
|
||||
final int timeOffSetTime;
|
||||
final int isAutoTimeOffSet;
|
||||
|
||||
final ValueChanged<int> parentAction;
|
||||
|
||||
TimeZone({
|
||||
Key key,
|
||||
this.timeOffSet,
|
||||
this.timeOffSetTime,
|
||||
this.parentAction,
|
||||
this.isAutoTimeOffSet,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_TimeZoneState createState() => _TimeZoneState();
|
||||
}
|
||||
|
||||
class _TimeZoneState extends State<TimeZone> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
int _localOffSet = 5;
|
||||
final int now = newTimeStamp();
|
||||
final int elapsedHours = ((now - widget.timeOffSetTime) / 3600).round();
|
||||
final String offSet = (widget.timeOffSet < 0)
|
||||
? '(UTC${widget.timeOffSet})'
|
||||
: '(UTC+${widget.timeOffSet})';
|
||||
final double textHeight = 1.5;
|
||||
|
||||
return Row(
|
||||
children: <Widget> [
|
||||
Expanded(
|
||||
flex: 7,
|
||||
child: InkWell(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget> [
|
||||
Text(
|
||||
'Time Offset:',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: candyApple,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
children: <TextSpan>[
|
||||
TextSpan(
|
||||
text: (widget.timeOffSet != 2000) ? '$offSet' : 'INVALID',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: (widget.timeOffSet != 2000) ? ' minutes, ' : '',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
children: <TextSpan>[
|
||||
TextSpan(
|
||||
text: (widget.isAutoTimeOffSet == 1) ? 'as of' : 'MANUAL SETTING',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: candyApple,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: (widget.isAutoTimeOffSet == 1) ? ' $elapsedHours' : '',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: (widget.isAutoTimeOffSet == 1) ? ' hrs' : '',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: (widget.isAutoTimeOffSet == 1) ? ' ago' : '',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
final FixedExtentScrollController scrollController =
|
||||
FixedExtentScrollController(initialItem: _getTimeZoneListIndex());
|
||||
return AlertDialog(
|
||||
backgroundColor: ivory,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
ButtonTheme(
|
||||
height: 75,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
),
|
||||
child: RaisedButton(
|
||||
onPressed: () {
|
||||
if (!mounted) {
|
||||
return;
|
||||
} else {
|
||||
Navigator.of(context).pop();
|
||||
if (_localOffSet != 5) {
|
||||
widget.parentAction(_localOffSet);
|
||||
}
|
||||
}
|
||||
},
|
||||
color: candyApple,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.save,
|
||||
size: 48.0,
|
||||
color: Colors.white,
|
||||
),
|
||||
Text(
|
||||
'save tz offset',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: (MediaQuery.of(context).size.height * .5),
|
||||
child: CupertinoPicker(
|
||||
scrollController: scrollController,
|
||||
backgroundColor: ivory,
|
||||
itemExtent: 100,
|
||||
looping: true,
|
||||
onSelectedItemChanged: (int index) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
} else {
|
||||
setState(() {
|
||||
_localOffSet = timeZoneList[index]['offset'];
|
||||
});
|
||||
}
|
||||
},
|
||||
children: List<Widget>.generate(timeZoneList.length, (int index) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: 10,
|
||||
color: ivory,
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment(0.0,0.0),
|
||||
height: 80,
|
||||
width: (MediaQuery.of(context).size.width * .9),
|
||||
decoration: BoxDecoration(
|
||||
color: peacockBlue,
|
||||
border: Border.all(
|
||||
width: 2.0,
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
_parseTimeZoneOffSet(timeZoneList[index]['offset']),
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${timeZoneList[index]['tz']}',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 10,
|
||||
color: ivory,
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
width: 1.0,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: InkWell(
|
||||
child: Icon(
|
||||
Icons.info,
|
||||
size: 48.0,
|
||||
color: Colors.black,
|
||||
),
|
||||
/*
|
||||
onTap: () {
|
||||
urlLaunch('https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations');
|
||||
}
|
||||
*/
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: ivory,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'The timezone offset should be set and refreshed automatically, '
|
||||
'by querying an api from teleport. However, because the api is '
|
||||
'free, the query is only refreshed once a week.\n'
|
||||
'Additionally, in some cases the result is invalid. Tap the '
|
||||
'left side of TimeZone Widget to manually set the timezone offset.',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
top: 40,
|
||||
bottom: 10,
|
||||
),
|
||||
child: ButtonTheme(
|
||||
height: 75,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0)),
|
||||
),
|
||||
child: RaisedButton(
|
||||
color: peacockBlue,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 150,
|
||||
margin: EdgeInsets.only(
|
||||
top: 10,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'List',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.open_in_browser,
|
||||
size: 48,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
bottom: 15,
|
||||
),
|
||||
child: Text(
|
||||
'Of TimeZones',
|
||||
style: TextStyle(
|
||||
height: textHeight,
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
urlLaunch('https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations');
|
||||
}
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
int _getTimeZoneListIndex() {
|
||||
// 2001 is a magic number which means auto
|
||||
// as in the tz offset is automatically set
|
||||
return timeZoneList.indexOf(timeZoneList.singleWhere((timeZone) => timeZone['offset'] == ((widget.isAutoTimeOffSet == 1) ? 2001 : widget.timeOffSet)));
|
||||
}
|
||||
|
||||
String _parseTimeZoneOffSet(int offset) {
|
||||
if (offset < 2000) {
|
||||
return offset.toString();
|
||||
} else if (offset == 2000) {
|
||||
return 'INVALID';
|
||||
} else if (offset == 2001) {
|
||||
return 'AUTO';
|
||||
}
|
||||
}
|
||||
}
|
48
lib/timezones.dart
Normal file
48
lib/timezones.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
// https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations
|
||||
|
||||
final List<Map<String, Object>> timeZoneList = [
|
||||
|
||||
{ 'offset' : -720, 'tz' : 'BIT, IDLW' },
|
||||
{ 'offset' : -660, 'tz' : 'NUT, SST' },
|
||||
{ 'offset' : -600, 'tz' : 'HST, TAHT' },
|
||||
{ 'offset' : -570, 'tz' : 'MART, MIT' },
|
||||
{ 'offset' : -540, 'tz' : 'AKST, HDT' },
|
||||
{ 'offset' : -480, 'tz' : 'AKDT, PST' },
|
||||
{ 'offset' : -420, 'tz' : 'MST, PDT' },
|
||||
{ 'offset' : -360, 'tz' : 'CST, GALT' },
|
||||
{ 'offset' : -300, 'tz' : 'EST, PET' },
|
||||
{ 'offset' : -240, 'tz' : 'AMT, AST' },
|
||||
{ 'offset' : -210, 'tz' : 'NST, NT' },
|
||||
{ 'offset' : -180, 'tz' : 'ADT, FKST' },
|
||||
{ 'offset' : -150, 'tz' : 'NDT' },
|
||||
{ 'offset' : -120, 'tz' : 'GST, UYST' },
|
||||
{ 'offset' : -60, 'tz' : 'CVT, EGT' },
|
||||
{ 'offset' : 0, 'tz' : 'GMT, UTC' },
|
||||
{ 'offset' : 60, 'tz' : 'CET, BST' },
|
||||
{ 'offset' : 120, 'tz' : 'EET, CEST' },
|
||||
{ 'offset' : 180, 'tz' : 'FET, EEST' },
|
||||
{ 'offset' : 210, 'tz' : 'IRST' },
|
||||
{ 'offset' : 240, 'tz' : 'AMT, VOLT' },
|
||||
{ 'offset' : 270, 'tz' : 'AFT, IRDT' },
|
||||
{ 'offset' : 300, 'tz' : 'PKT, TFT' },
|
||||
{ 'offset' : 330, 'tz' : 'IST, SLST' },
|
||||
{ 'offset' : 345, 'tz' : 'NPT' },
|
||||
{ 'offset' : 360, 'tz' : 'BST, VOST' },
|
||||
{ 'offset' : 390, 'tz' : 'CCT, MMT' },
|
||||
{ 'offset' : 420, 'tz' : 'CXT, THA' },
|
||||
{ 'offset' : 480, 'tz' : 'AWST, HKT' },
|
||||
{ 'offset' : 525, 'tz' : 'ACWST' },
|
||||
{ 'offset' : 540, 'tz' : 'JST, YAKT' },
|
||||
{ 'offset' : 570, 'tz' : 'ACST' },
|
||||
{ 'offset' : 600, 'tz' : 'AEST, VLAT' },
|
||||
{ 'offset' : 630, 'tz' : 'ACDT, LHST' },
|
||||
{ 'offset' : 660, 'tz' : 'AEDT, VUT' },
|
||||
{ 'offset' : 720, 'tz' : 'NZST, WAKT' },
|
||||
{ 'offset' : 765, 'tz' : 'CHAST' },
|
||||
{ 'offset' : 780, 'tz' : 'NZDT, TOT' },
|
||||
{ 'offset' : 825, 'tz' : 'CHADT' },
|
||||
{ 'offset' : 840, 'tz' : 'LINT' },
|
||||
{ 'offset' : 2000, 'tz' : 'INVLD' },
|
||||
{ 'offset' : 2001, 'tz' : 'AUTO' },
|
||||
|
||||
];
|
1261
lib/weather.dart
1261
lib/weather.dart
File diff suppressed because it is too large
Load Diff
1070
lib/weather_forecast.dart
Normal file
1070
lib/weather_forecast.dart
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user