deactivate geocoding and Google maps when Play Services are unavailable
This commit is contained in:
parent
910dd1fe54
commit
ab96741a18
11 changed files with 78 additions and 46 deletions
41
lib/model/availability.dart
Normal file
41
lib/model/availability.dart
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import 'package:connectivity/connectivity.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:google_api_availability/google_api_availability.dart';
|
||||||
|
|
||||||
|
final AvesAvailability availability = AvesAvailability._private();
|
||||||
|
|
||||||
|
class AvesAvailability {
|
||||||
|
bool _isConnected, _hasPlayServices;
|
||||||
|
|
||||||
|
AvesAvailability._private() {
|
||||||
|
Connectivity().onConnectivityChanged.listen(_updateConnectivityFromResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onResume() => _isConnected = null;
|
||||||
|
|
||||||
|
Future<bool> get isConnected async {
|
||||||
|
if (_isConnected != null) return SynchronousFuture(_isConnected);
|
||||||
|
final result = await (Connectivity().checkConnectivity());
|
||||||
|
_updateConnectivityFromResult(result);
|
||||||
|
return _isConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateConnectivityFromResult(ConnectivityResult result) {
|
||||||
|
final newValue = result != ConnectivityResult.none;
|
||||||
|
if (_isConnected != newValue) {
|
||||||
|
_isConnected = newValue;
|
||||||
|
debugPrint('Device is connected=$_isConnected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> get hasPlayServices async {
|
||||||
|
if (_hasPlayServices != null) return SynchronousFuture(_hasPlayServices);
|
||||||
|
final result = await GoogleApiAvailability.instance.checkGooglePlayServicesAvailability();
|
||||||
|
_hasPlayServices = result == GooglePlayServicesAvailability.success;
|
||||||
|
debugPrint('Device has Play Services=$_hasPlayServices');
|
||||||
|
return _hasPlayServices;
|
||||||
|
}
|
||||||
|
|
||||||
|
// local geolocation with `geocoder` requires Play Services
|
||||||
|
Future<bool> get canGeolocate => Future.wait<bool>([isConnected, hasPlayServices]).then((results) => results.every((result) => result));
|
||||||
|
}
|
|
@ -1,28 +0,0 @@
|
||||||
import 'package:connectivity/connectivity.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
|
|
||||||
final AvesConnectivity connectivity = AvesConnectivity._private();
|
|
||||||
|
|
||||||
class AvesConnectivity {
|
|
||||||
bool _isConnected;
|
|
||||||
|
|
||||||
AvesConnectivity._private() {
|
|
||||||
Connectivity().onConnectivityChanged.listen(_updateFromResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onResume() => _isConnected = null;
|
|
||||||
|
|
||||||
Future<bool> get isConnected async {
|
|
||||||
if (_isConnected != null) return SynchronousFuture(_isConnected);
|
|
||||||
final result = await (Connectivity().checkConnectivity());
|
|
||||||
_updateFromResult(result);
|
|
||||||
return _isConnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> get canGeolocate => isConnected;
|
|
||||||
|
|
||||||
void _updateFromResult(ConnectivityResult result) {
|
|
||||||
_isConnected = result != ConnectivityResult.none;
|
|
||||||
debugPrint('Device is connected=$_isConnected');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -42,6 +42,10 @@ class AvesEntry {
|
||||||
|
|
||||||
final AChangeNotifier imageChangeNotifier = AChangeNotifier(), metadataChangeNotifier = AChangeNotifier(), addressChangeNotifier = AChangeNotifier();
|
final AChangeNotifier imageChangeNotifier = AChangeNotifier(), metadataChangeNotifier = AChangeNotifier(), addressChangeNotifier = AChangeNotifier();
|
||||||
|
|
||||||
|
// Local geocoding requires Google Play Services
|
||||||
|
// Google remote geocoding requires an API key and is not free
|
||||||
|
final Future<List<Address>> Function(Coordinates coordinates) _findAddresses = Geocoder.local.findAddressesFromCoordinates;
|
||||||
|
|
||||||
// TODO TLAD make it dynamic if it depends on OS/lib versions
|
// TODO TLAD make it dynamic if it depends on OS/lib versions
|
||||||
static const List<String> undecodable = [MimeTypes.crw, MimeTypes.psd];
|
static const List<String> undecodable = [MimeTypes.crw, MimeTypes.psd];
|
||||||
|
|
||||||
|
@ -441,7 +445,7 @@ class AvesEntry {
|
||||||
|
|
||||||
final coordinates = Coordinates(latitude, longitude);
|
final coordinates = Coordinates(latitude, longitude);
|
||||||
try {
|
try {
|
||||||
Future<List<Address>> call() => Geocoder.local.findAddressesFromCoordinates(coordinates);
|
Future<List<Address>> call() => _findAddresses(coordinates);
|
||||||
final addresses = await (background
|
final addresses = await (background
|
||||||
? servicePolicy.call(
|
? servicePolicy.call(
|
||||||
call,
|
call,
|
||||||
|
@ -475,7 +479,7 @@ class AvesEntry {
|
||||||
|
|
||||||
final coordinates = Coordinates(latitude, longitude);
|
final coordinates = Coordinates(latitude, longitude);
|
||||||
try {
|
try {
|
||||||
final addresses = await Geocoder.local.findAddressesFromCoordinates(coordinates);
|
final addresses = await _findAddresses(coordinates);
|
||||||
if (addresses != null && addresses.isNotEmpty) {
|
if (addresses != null && addresses.isNotEmpty) {
|
||||||
final address = addresses.first;
|
final address = addresses.first;
|
||||||
return address.addressLine;
|
return address.addressLine;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:aves/model/connectivity.dart';
|
import 'package:aves/model/availability.dart';
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/filters/location.dart';
|
import 'package:aves/model/filters/location.dart';
|
||||||
import 'package:aves/model/metadata.dart';
|
import 'package:aves/model/metadata.dart';
|
||||||
|
@ -28,7 +28,7 @@ mixin LocationMixin on SourceBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> locateEntries() async {
|
Future<void> locateEntries() async {
|
||||||
if (!(await connectivity.canGeolocate)) return;
|
if (!(await availability.canGeolocate)) return;
|
||||||
|
|
||||||
// final stopwatch = Stopwatch()..start();
|
// final stopwatch = Stopwatch()..start();
|
||||||
final byLocated = groupBy<AvesEntry, bool>(rawEntries.where((entry) => entry.hasGps), (entry) => entry.isLocated);
|
final byLocated = groupBy<AvesEntry, bool>(rawEntries.where((entry) => entry.hasGps), (entry) => entry.isLocated);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:aves/main.dart';
|
import 'package:aves/main.dart';
|
||||||
import 'package:aves/model/connectivity.dart';
|
import 'package:aves/model/availability.dart';
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/filters/filters.dart';
|
import 'package:aves/model/filters/filters.dart';
|
||||||
import 'package:aves/model/settings/home_page.dart';
|
import 'package:aves/model/settings/home_page.dart';
|
||||||
|
@ -115,7 +115,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
// cataloguing is essential for coordinates and video rotation
|
// cataloguing is essential for coordinates and video rotation
|
||||||
await entry.catalog();
|
await entry.catalog();
|
||||||
// locating is fine in the background
|
// locating is fine in the background
|
||||||
unawaited(connectivity.canGeolocate.then((connected) {
|
unawaited(availability.canGeolocate.then((connected) {
|
||||||
if (connected) {
|
if (connected) {
|
||||||
entry.locate();
|
entry.locate();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:aves/model/connectivity.dart';
|
import 'package:aves/model/availability.dart';
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/source/collection_lens.dart';
|
import 'package:aves/model/source/collection_lens.dart';
|
||||||
import 'package:aves/widgets/common/magnifier/pan/scroll_physics.dart';
|
import 'package:aves/widgets/common/magnifier/pan/scroll_physics.dart';
|
||||||
|
@ -152,7 +152,7 @@ class _ViewerVerticalPageViewState extends State<ViewerVerticalPageView> {
|
||||||
// make sure to locate the entry,
|
// make sure to locate the entry,
|
||||||
// so that we can display the address instead of coordinates
|
// so that we can display the address instead of coordinates
|
||||||
// even when initial collection locating has not reached this entry yet
|
// even when initial collection locating has not reached this entry yet
|
||||||
connectivity.canGeolocate.then((connected) {
|
availability.canGeolocate.then((connected) {
|
||||||
if (connected) {
|
if (connected) {
|
||||||
entry.locate();
|
entry.locate();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:aves/model/connectivity.dart';
|
import 'package:aves/model/availability.dart';
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/filters/filters.dart';
|
import 'package:aves/model/filters/filters.dart';
|
||||||
import 'package:aves/model/settings/screen_on.dart';
|
import 'package:aves/model/settings/screen_on.dart';
|
||||||
|
@ -151,7 +151,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with SingleTickerPr
|
||||||
_pauseVideoControllers();
|
_pauseVideoControllers();
|
||||||
break;
|
break;
|
||||||
case AppLifecycleState.resumed:
|
case AppLifecycleState.resumed:
|
||||||
connectivity.onResume();
|
availability.onResume();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import 'package:aves/model/connectivity.dart';
|
import 'package:aves/model/availability.dart';
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/filters/location.dart';
|
import 'package:aves/model/filters/location.dart';
|
||||||
import 'package:aves/model/settings/coordinate_format.dart';
|
import 'package:aves/model/settings/coordinate_format.dart';
|
||||||
|
@ -104,7 +104,7 @@ class _LocationSectionState extends State<LocationSection> with TickerProviderSt
|
||||||
children: [
|
children: [
|
||||||
if (widget.showTitle) SectionRow(AIcons.location),
|
if (widget.showTitle) SectionRow(AIcons.location),
|
||||||
FutureBuilder<bool>(
|
FutureBuilder<bool>(
|
||||||
future: connectivity.isConnected,
|
future: availability.isConnected,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.data != true) return SizedBox();
|
if (snapshot.data != true) return SizedBox();
|
||||||
return NotificationListener(
|
return NotificationListener(
|
||||||
|
@ -181,7 +181,7 @@ class _AddressInfoGroupState extends State<_AddressInfoGroup> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_addressLineLoader = connectivity.canGeolocate.then((connected) {
|
_addressLineLoader = availability.canGeolocate.then((connected) {
|
||||||
if (connected) {
|
if (connected) {
|
||||||
return entry.findAddressLine();
|
return entry.findAddressLine();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:aves/model/availability.dart';
|
||||||
import 'package:aves/model/settings/map_style.dart';
|
import 'package:aves/model/settings/map_style.dart';
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/services/android_app_service.dart';
|
import 'package:aves/services/android_app_service.dart';
|
||||||
|
@ -73,13 +74,19 @@ class MapButtonPanel extends StatelessWidget {
|
||||||
MapOverlayButton(
|
MapOverlayButton(
|
||||||
icon: AIcons.layers,
|
icon: AIcons.layers,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
final hasPlayServices = await availability.hasPlayServices;
|
||||||
|
final availableStyles = EntryMapStyle.values.where((style) => !style.isGoogleMaps || hasPlayServices);
|
||||||
|
final preferredStyle = settings.infoMapStyle;
|
||||||
|
final initialStyle = availableStyles.contains(preferredStyle) ? preferredStyle : availableStyles.first;
|
||||||
final style = await showDialog<EntryMapStyle>(
|
final style = await showDialog<EntryMapStyle>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AvesSelectionDialog<EntryMapStyle>(
|
builder: (context) {
|
||||||
initialValue: settings.infoMapStyle,
|
return AvesSelectionDialog<EntryMapStyle>(
|
||||||
options: Map.fromEntries(EntryMapStyle.values.map((v) => MapEntry(v, v.name))),
|
initialValue: initialStyle,
|
||||||
title: 'Map Style',
|
options: Map.fromEntries(availableStyles.map((v) => MapEntry(v, v.name))),
|
||||||
),
|
title: 'Map Style',
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
// wait for the dialog to hide because switching to Google Maps layer may block the UI
|
// wait for the dialog to hide because switching to Google Maps layer may block the UI
|
||||||
await Future.delayed(Durations.dialogTransitionAnimation * timeDilation);
|
await Future.delayed(Durations.dialogTransitionAnimation * timeDilation);
|
||||||
|
|
|
@ -387,6 +387,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.0"
|
||||||
|
google_api_availability:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: google_api_availability
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.4"
|
||||||
google_maps_flutter:
|
google_maps_flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -55,6 +55,7 @@ dependencies:
|
||||||
flutter_staggered_animations:
|
flutter_staggered_animations:
|
||||||
flutter_svg:
|
flutter_svg:
|
||||||
geocoder:
|
geocoder:
|
||||||
|
google_api_availability:
|
||||||
google_maps_flutter:
|
google_maps_flutter:
|
||||||
intl:
|
intl:
|
||||||
latlong: # for flutter_map
|
latlong: # for flutter_map
|
||||||
|
|
Loading…
Reference in a new issue