Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added google geocoding to get city/state/country #83

Open
wants to merge 12 commits into
base: designs_v1.1
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
.buildlog/
.history
.svn/
.env*

# IntelliJ related
*.iml
Expand Down
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ android {
release {
signingConfig signingConfigs.release
shrinkResources false
minifyEnabled true
minifyEnabled false
proguardFiles "${background_geolocation.projectDir}/proguard-rules.pro"
}
debug {
Expand Down
3 changes: 2 additions & 1 deletion lib/location_updates.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ class LocationUpdates {
static Future<Location> currentLocation() async => bg.BackgroundGeolocation.getCurrentPosition();

static Future<bool> isWithinAvailableGeoLocation() async {
var success = await PushNotifications.updateLoggedInUser();
var location = await LocationUpdates.currentLocation();
var success = await ApiRepository.isExistsInLocationGate(location);
return success;
}

Expand Down
2 changes: 2 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import 'package:corona_trace/ui_v1_1/notification_location/onboarding_notificati
import 'package:corona_trace/ui_v1_1/onboarding_get_started.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:instabug_flutter/Instabug.dart';
import 'package:instabug_flutter/Surveys.dart';

import 'utils/app_localization.dart';

void main() async {
await DotEnv().load('.env');
WidgetsFlutterBinding.ensureInitialized();
Crashlytics.instance.enableInDevMode = true;

Expand Down
30 changes: 30 additions & 0 deletions lib/network/airtable/airtable_record.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import 'package:corona_trace/network/airtable/airtable_record_fields.dart';

class AirtableRecord {
String _id;
AirtableRecordFields _fields;
String _createdTime;

String get id => _id;

AirtableRecordFields get fields => _fields;

String get createdTime => _createdTime;

AirtableRecord(
this._id, this._fields, this._createdTime);

AirtableRecord.map(dynamic obj) {
_id = obj["id"];
_fields = AirtableRecordFields.map(obj["fields"]);
_createdTime = obj["createdTime"];
}

Map<String, dynamic> toMap() {
var map = <String, dynamic>{};
map["Id"] = _id;
map["fields"] = _fields;
map["createdTime"] = _createdTime;
return map;
}
}
19 changes: 19 additions & 0 deletions lib/network/airtable/airtable_record_fields.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

class AirtableRecordFields {
String _name;

String get name => _name;

AirtableRecordFields(
this._name);

AirtableRecordFields.map(dynamic obj) {
_name = obj["Name"];
}

Map<String, dynamic> toMap() {
var map = <String, dynamic>{};
map["Name"] = _name;
return map;
}
}
82 changes: 82 additions & 0 deletions lib/network/airtable/airtable_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import 'dart:convert' as JSON;

import 'package:corona_trace/analytics/CTAnalyticsManager.dart';
import 'package:corona_trace/app_constants.dart';
import 'package:corona_trace/network/airtable/airtable_response.dart';
import 'package:corona_trace/network/notification/response_notification.dart';
import 'package:dio/dio.dart';
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

class AirtableRepository {
static final AirtableRepository _instance = AirtableRepository._internal();

factory AirtableRepository() => _instance;

static AirtableRepository get instance => _instance;

AirtableRepository._internal();

static BaseOptions dioOptions =
new BaseOptions(
connectTimeout: 15000,
receiveTimeout: 30000,
headers: {
"Authorization":"Bearer ${DotEnv().env['AIRTABLE_API_KEY']}"
});
static Dio _dio = Dio(dioOptions);

static const AIRTABLE_API_BASE_URL = "https://api.airtable.com/v0/appeeTR6FdXwMaHYo";
static const STATES_URL = "$AIRTABLE_API_BASE_URL/States";
static const COUNTRIES_URL = "$AIRTABLE_API_BASE_URL/Countries";
static const CITIES_URL = "$AIRTABLE_API_BASE_URL/Cities";

static String getAirtableQueryURL(String url, String name) {
return "$url?fields%5B%5D=Name&filterByFormula=AND(%7BAvailability%7D%2C%7BName%7D%3D%22$name%22)";
}

static Future<bool> checkIfAvailableInStatesList(String state) async {
try {
var url = getAirtableQueryURL(STATES_URL, state);
Response response = await _dio.get(url);
var statusCode = response.statusCode;
debugPrint("$statusCode - $url");
var records = AirtableResponse.map(response.data).records;
return records.isNotEmpty;
} catch (ex) {
debugPrint('checkIfAvailableInStatesList Failed: $ex');
throw ex;
}
}

static Future<bool> checkIfAvailableInCountriesList(String country) async {
try {
var url = getAirtableQueryURL(COUNTRIES_URL, country);
Response response = await _dio.get(url);
var statusCode = response.statusCode;
debugPrint("$statusCode - $url");
var records = AirtableResponse.map(response.data).records;
return records.isNotEmpty;
} catch (ex) {
debugPrint('checkIfAvailableInCountriesList Failed: $ex');
throw ex;
}
}

static Future<bool> checkIfAvailableInCitiesList(String city) async {
try {
var url = getAirtableQueryURL(CITIES_URL, city);
Response response = await _dio.get(url);
var statusCode = response.statusCode;
debugPrint("$statusCode - $url");
var records = AirtableResponse.map(response.data).records;
return records.isNotEmpty;
} catch (ex) {
debugPrint('checkIfAvailableInCitiesList Failed: $ex');
throw ex;
}
}
}
27 changes: 27 additions & 0 deletions lib/network/airtable/airtable_response.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:corona_trace/network/airtable/airtable_record.dart';

class AirtableResponse {
List<AirtableRecord> _records;
String _offset;

List<AirtableRecord> get records => _records;

String get offset => _offset;

AirtableResponse(this._records, this._offset);

AirtableResponse.map(dynamic obj) {
_records = obj["records"] == null
? null
: new List<AirtableRecord>.from(
obj["records"].map((x) => AirtableRecord.map(x)));
_offset = obj["offset"] ?? "";
}

Map<String, dynamic> toMap() {
var map = <String, dynamic>{};
map["records"] = _records;
map["offset"] = _offset;
return map;
}
}
61 changes: 56 additions & 5 deletions lib/network/api_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ import 'dart:convert' as JSON;

import 'package:corona_trace/analytics/CTAnalyticsManager.dart';
import 'package:corona_trace/app_constants.dart';
import 'package:corona_trace/network/airtable/airtable_repository.dart';
import 'package:corona_trace/network/notification/response_notification.dart';
import 'package:dio/dio.dart';
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:google_geocoding/google_geocoding.dart' as GoogleGeo;
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

import '../app_constants.dart';

class ApiRepository {
static final ApiRepository _instance = ApiRepository._internal();

Expand Down Expand Up @@ -43,8 +48,7 @@ class ApiRepository {
var deviceID = await AppConstants.getDeviceId();
var url = "$API_URL/users";
var body = tokenRequestBody(token, deviceID, currentLocation);
Response response =
await _dio.post(url, data: JSON.jsonEncode(body));
Response response = await _dio.post(url, data: JSON.jsonEncode(body));
var statusCode = response.statusCode;
debugPrint("$statusCode - $url");
if (response.statusCode == 200) {
Expand All @@ -54,9 +58,48 @@ class ApiRepository {
return false;
}

static Map<String, dynamic> tokenRequestBody(String token, String deviceID, Location location) =>
{
"token": token,
static Future<bool> isExistsInLocationGate(Location fromLocation) async {
try {
GoogleGeo.GoogleGeocoding googleGeocoding =
GoogleGeo.GoogleGeocoding(DotEnv().env['API_KEY_GEOCODING']);
var geocodingResponse = await googleGeocoding.geocoding.getReverse(
GoogleGeo.LatLon(
fromLocation.coords.latitude, fromLocation.coords.longitude));
var address = geocodingResponse.results.first.addressComponents;
Triple csc = extractCSC(address);
return await AirtableRepository.checkIfAvailableInCitiesList(csc.c);
} catch (ex) {
print(ex);
}
return false;
}

static Triple extractCSC(List<GoogleGeo.AddressComponent> address) {
var country;
var state;
var city;
if (address.isNotEmpty) {
address.forEach((addressComponent) {
addressComponent.types.forEach((type) {
if (type == "country") {
country = addressComponent.longName;
}
if (type == "administrative_area_level_1") {
state = addressComponent.longName;
}
if (type == "locality") {
city = addressComponent.longName;
}
});
});
}
return Triple(country, state, city);
}

static Map<String, dynamic> tokenRequestBody(String token, String deviceID,
Location location) =>
{
"token": token,
"userId": deviceID,
"location": {
"latitude": location.coords.latitude,
Expand Down Expand Up @@ -162,3 +205,11 @@ class ApiRepository {
await sharedPrefs.setBool(DID_ALLOW_NOTIFY_WHEN_AVAILABLE, shouldNotify);
}
}

class Triple<T extends String, S extends String, C extends String> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice name 😅 How about LocationGateTriple

T a;
S b;
C c;

Triple(this.a, this.b, this.c);
}
1 change: 0 additions & 1 deletion lib/service/push_notifications/push_notifications.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ class PushNotifications {
'Zero channel',
playSound: false,
enableVibration: false,
style: AndroidNotificationStyle.BigText,
importance: Importance.Max,
priority: Priority.High,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,6 @@ class OnboardingLocationPermissionState
}

Future onPremissionAvailable(BuildContext context) async {
showLoadingDialog(tapDismiss: false);
hideLoadingDialog();
navigateNotificationPermission(context);
}

Expand Down
Loading