merge --squash devel, version 0.1.0

This commit is contained in:
2019-06-27 17:29:40 -07:00
parent 0b773d4326
commit 4045eff06f
52 changed files with 6752 additions and 423 deletions

156
lib/about.dart Normal file
View 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);
}
),
),
),
],
),
),
],
),
),
),
],
),
);
}
);
},
);
}

View File

@@ -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]);
}
}

View File

@@ -0,0 +1,17 @@
final String defaultPlataeaNotes = """
![](https://upload.wikimedia.org/wikipedia/commons/d/d5/Scene_of_the_Battle_of_Plataea.jpg)
___
# 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
View 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
View 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');
}
),
),
),
],
),
);
}
);
},
);
}

View File

@@ -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
View 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
View 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
View 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;
}
}
}

File diff suppressed because it is too large Load Diff

296
lib/render_notes.dart Normal file
View 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();
}
),
),
),
],
),
);
}
);
}
}
}
),
),
),
],
),
);
}
);
}
}
),
),
),
],
),
],
),
),
],
),
);
}
}

View File

@@ -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
View 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
View 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
View 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' },
];

File diff suppressed because it is too large Load Diff

1070
lib/weather_forecast.dart Normal file

File diff suppressed because it is too large Load Diff