map style check

This commit is contained in:
Thibault Deckers 2022-05-22 17:44:38 +09:00
parent 068f4d499a
commit 8ced682bfd
15 changed files with 75 additions and 65 deletions

View file

@ -17,6 +17,7 @@ All notable changes to this project will be documented in this file.
- upgraded Flutter to stable v3.0.1
- stretching overscroll effect
- disabled Google Maps layer on Android Lollipop
### Fixed

View file

@ -32,10 +32,6 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
"canPinShortcut" to ShortcutManagerCompat.isRequestPinShortcutSupported(context),
"canPrint" to (sdkInt >= Build.VERSION_CODES.KITKAT),
"canRenderFlagEmojis" to (sdkInt >= Build.VERSION_CODES.LOLLIPOP),
// as of google_maps_flutter v2.1.1, minSDK is 20 because of default PlatformView usage,
// but using hybrid composition would make it usable on API 19 too,
// cf https://github.com/flutter/flutter/issues/23728
"canRenderGoogleMaps" to (sdkInt >= Build.VERSION_CODES.KITKAT_WATCH),
"showPinShortcutFeedback" to (sdkInt >= Build.VERSION_CODES.O),
"supportEdgeToEdgeUIMode" to (sdkInt >= Build.VERSION_CODES.Q),
)

View file

@ -1,5 +1,6 @@
import 'package:aves/model/device.dart';
import 'package:aves_services_platform/aves_services_platform.dart';
import 'package:aves/model/settings/enums/map_style.dart';
import 'package:aves/services/common/services.dart';
import 'package:aves_map/aves_map.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/foundation.dart';
@ -10,7 +11,7 @@ abstract class AvesAvailability {
Future<bool> get canLocatePlaces;
Future<bool> get canUseDeviceMaps;
List<EntryMapStyle> get mapStyles;
}
class LiveAvesAvailability implements AvesAvailability {
@ -42,11 +43,11 @@ class LiveAvesAvailability implements AvesAvailability {
// local geocoding with `geocoder` seems to require Google Play Services
// what about devices with Huawei Mobile Services?
@override
Future<bool> get canLocatePlaces => Future.wait<bool>([
isConnected,
PlatformMobileServices().isServiceAvailable(),
]).then((results) => results.every((result) => result));
Future<bool> get canLocatePlaces async => mobileServices.isServiceAvailable && await isConnected;
@override
Future<bool> get canUseDeviceMaps async => device.canRenderGoogleMaps && await PlatformMobileServices().isServiceAvailable();
List<EntryMapStyle> get mapStyles => [
...mobileServices.mapStyles,
...EntryMapStyle.values.where((v) => !v.needMobileService),
];
}

View file

@ -5,7 +5,7 @@ final Device device = Device._private();
class Device {
late final String _userAgent;
late final bool _canGrantDirectoryAccess, _canPinShortcut, _canPrint, _canRenderFlagEmojis, _canRenderGoogleMaps;
late final bool _canGrantDirectoryAccess, _canPinShortcut, _canPrint, _canRenderFlagEmojis;
late final bool _showPinShortcutFeedback, _supportEdgeToEdgeUIMode;
String get userAgent => _userAgent;
@ -18,8 +18,6 @@ class Device {
bool get canRenderFlagEmojis => _canRenderFlagEmojis;
bool get canRenderGoogleMaps => _canRenderGoogleMaps;
bool get showPinShortcutFeedback => _showPinShortcutFeedback;
bool get supportEdgeToEdgeUIMode => _supportEdgeToEdgeUIMode;
@ -35,7 +33,6 @@ class Device {
_canPinShortcut = capabilities['canPinShortcut'] ?? false;
_canPrint = capabilities['canPrint'] ?? false;
_canRenderFlagEmojis = capabilities['canRenderFlagEmojis'] ?? false;
_canRenderGoogleMaps = capabilities['canRenderGoogleMaps'] ?? false;
_showPinShortcutFeedback = capabilities['showPinShortcutFeedback'] ?? false;
_supportEdgeToEdgeUIMode = capabilities['supportEdgeToEdgeUIMode'] ?? false;
}

View file

@ -37,7 +37,7 @@ extension ExtraEntryMapStyle on EntryMapStyle {
}
}
bool get needDeviceService {
bool get needMobileService {
switch (this) {
case EntryMapStyle.osmHot:
case EntryMapStyle.stamenToner:

View file

@ -12,7 +12,6 @@ import 'package:aves/model/source/enums.dart';
import 'package:aves/services/accessibility_service.dart';
import 'package:aves/services/common/services.dart';
import 'package:aves_map/aves_map.dart';
import 'package:aves_services_platform/aves_services_platform.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -165,11 +164,11 @@ class Settings extends ChangeNotifier {
enableOverlayBlurEffect = performanceClass >= 29;
// availability
final isDeviceMapAvailable = await availability.canUseDeviceMaps;
if (isDeviceMapAvailable) {
infoMapStyle = PlatformMobileServices().defaultMapStyle;
final defaultMapStyle = mobileServices.defaultMapStyle;
if (mobileServices.mapStyles.contains(defaultMapStyle)) {
infoMapStyle = defaultMapStyle;
} else {
final styles = EntryMapStyle.values.whereNot((v) => v.needDeviceService).toList();
final styles = EntryMapStyle.values.whereNot((v) => v.needMobileService).toList();
infoMapStyle = styles[Random().nextInt(styles.length)];
}
@ -516,7 +515,11 @@ class Settings extends ChangeNotifier {
// info
EntryMapStyle get infoMapStyle => getEnumOrDefault(infoMapStyleKey, SettingsDefaults.infoMapStyle, EntryMapStyle.values);
EntryMapStyle get infoMapStyle {
final preferred = getEnumOrDefault(infoMapStyleKey, SettingsDefaults.infoMapStyle, EntryMapStyle.values);
final available = availability.mapStyles;
return available.contains(preferred) ? preferred : available.first;
}
set infoMapStyle(EntryMapStyle newValue) => setAndNotify(infoMapStyleKey, newValue.toString());

View file

@ -14,6 +14,8 @@ import 'package:aves/services/storage_service.dart';
import 'package:aves/services/window_service.dart';
import 'package:aves_report/aves_report.dart';
import 'package:aves_report_platform/aves_report_platform.dart';
import 'package:aves_services/aves_services.dart';
import 'package:aves_services_platform/aves_services_platform.dart';
import 'package:get_it/get_it.dart';
import 'package:path/path.dart' as p;
@ -33,6 +35,7 @@ final MediaFileService mediaFileService = getIt<MediaFileService>();
final MediaStoreService mediaStoreService = getIt<MediaStoreService>();
final MetadataEditService metadataEditService = getIt<MetadataEditService>();
final MetadataFetchService metadataFetchService = getIt<MetadataFetchService>();
final MobileServices mobileServices = getIt<MobileServices>();
final ReportService reportService = getIt<ReportService>();
final StorageService storageService = getIt<StorageService>();
final WindowService windowService = getIt<WindowService>();
@ -49,6 +52,7 @@ void initPlatformServices() {
getIt.registerLazySingleton<MediaStoreService>(PlatformMediaStoreService.new);
getIt.registerLazySingleton<MetadataEditService>(PlatformMetadataEditService.new);
getIt.registerLazySingleton<MetadataFetchService>(PlatformMetadataFetchService.new);
getIt.registerLazySingleton<MobileServices>(PlatformMobileServices.new);
getIt.registerLazySingleton<ReportService>(PlatformReportService.new);
getIt.registerLazySingleton<StorageService>(PlatformStorageService.new);
getIt.registerLazySingleton<WindowService>(PlatformWindowService.new);

View file

@ -15,7 +15,6 @@ import 'package:aves/widgets/common/action_mixins/feedback.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
import 'package:aves/widgets/common/identity/buttons.dart';
import 'package:aves_services_platform/aves_services_platform.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -145,7 +144,6 @@ class _BugReportState extends State<BugReport> with FeedbackMixin {
final packageInfo = await PackageInfo.fromPlatform();
final androidInfo = await DeviceInfoPlugin().androidInfo;
final installer = await androidAppService.getAppInstaller();
final hasMobileServices = await PlatformMobileServices().isServiceAvailable();
final flavor = context.read<AppFlavor>().toString().split('.')[1];
return [
'Aves version: ${packageInfo.version}-$flavor (Build ${packageInfo.buildNumber})',
@ -153,7 +151,7 @@ class _BugReportState extends State<BugReport> with FeedbackMixin {
'Android version: ${androidInfo.version.release} (SDK ${androidInfo.version.sdkInt})',
'Android build: ${androidInfo.display}',
'Device: ${androidInfo.manufacturer} ${androidInfo.model}',
'Mobile services: ${hasMobileServices ? 'ready' : 'not available'}',
'Mobile services: ${mobileServices.isServiceAvailable ? 'ready' : 'not available'}',
'System locales: ${WidgetsBinding.instance.window.locales.join(', ')}',
'Aves locale: ${settings.locale ?? 'system'} -> ${settings.appliedLocale}',
'Installer: $installer',

View file

@ -30,7 +30,6 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/providers/highlight_info_provider.dart';
import 'package:aves/widgets/home_page.dart';
import 'package:aves/widgets/welcome_page.dart';
import 'package:aves_services_platform/aves_services_platform.dart';
import 'package:equatable/equatable.dart';
import 'package:fijkplayer/fijkplayer.dart';
import 'package:flutter/foundation.dart';
@ -225,6 +224,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
final stopwatch = Stopwatch()..start();
await device.init();
await mobileServices.init();
await settings.init(monitorPlatformSettings: true);
settings.isRotationLocked = await windowService.isRotationLocked();
settings.areAnimationsRemoved = await AccessibilityService.areAnimationsRemoved();
@ -273,14 +273,13 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
FlutterError.onError = reportService.recordFlutterError;
final now = DateTime.now();
final hasMobileServices = await PlatformMobileServices().isServiceAvailable();
await reportService.setCustomKeys({
'build_mode': kReleaseMode
? 'release'
: kProfileMode
? 'profile'
: 'debug',
'has_mobile_services': hasMobileServices,
'has_mobile_services': mobileServices.isServiceAvailable,
'locales': WidgetsBinding.instance.window.locales.join(', '),
'time_zone': '${now.timeZoneName} (${now.timeZoneOffset})',
});

View file

@ -9,7 +9,6 @@ import 'package:aves/widgets/common/map/buttons/coordinate_filter.dart';
import 'package:aves/widgets/common/map/compass.dart';
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
import 'package:aves_map/aves_map.dart';
import 'package:aves_services_platform/aves_services_platform.dart';
import 'package:flutter/material.dart';
import 'package:latlong2/latlong.dart';
import 'package:provider/provider.dart';
@ -125,24 +124,15 @@ class MapButtonPanel extends StatelessWidget {
padding: EdgeInsets.only(top: padding),
child: MapOverlayButton(
icon: const Icon(AIcons.layers),
onPressed: () async {
final canUseDeviceMaps = await availability.canUseDeviceMaps;
final availableStyles = [
if (canUseDeviceMaps) ...PlatformMobileServices().mapStyles,
...EntryMapStyle.values.where((v) => !v.needDeviceService),
];
final preferredStyle = settings.infoMapStyle;
final initialStyle = availableStyles.contains(preferredStyle) ? preferredStyle : availableStyles.first;
await showSelectionDialog<EntryMapStyle>(
context: context,
builder: (context) => AvesSelectionDialog<EntryMapStyle>(
initialValue: initialStyle,
options: Map.fromEntries(availableStyles.map((v) => MapEntry(v, v.getName(context)))),
title: context.l10n.mapStyleTitle,
),
onSelection: (v) => settings.infoMapStyle = v,
);
},
onPressed: () => showSelectionDialog<EntryMapStyle>(
context: context,
builder: (context) => AvesSelectionDialog<EntryMapStyle>(
initialValue: settings.infoMapStyle,
options: Map.fromEntries(availability.mapStyles.map((v) => MapEntry(v, v.getName(context)))),
title: context.l10n.mapStyleTitle,
),
onSelection: (v) => settings.infoMapStyle = v,
),
tooltip: context.l10n.mapStyleTooltip,
),
),

View file

@ -5,6 +5,7 @@ import 'package:aves/model/entry.dart';
import 'package:aves/model/entry_images.dart';
import 'package:aves/model/settings/enums/map_style.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/services/common/services.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/utils/change_notifier.dart';
import 'package:aves/utils/constants.dart';
@ -15,7 +16,6 @@ import 'package:aves/widgets/common/map/decorator.dart';
import 'package:aves/widgets/common/map/leaflet/map.dart';
import 'package:aves/widgets/common/thumbnail/image.dart';
import 'package:aves_map/aves_map.dart';
import 'package:aves_services_platform/aves_services_platform.dart';
import 'package:collection/collection.dart';
import 'package:fluster/fluster.dart';
import 'package:flutter/material.dart';
@ -71,8 +71,6 @@ class _GeoMapState extends State<GeoMap> {
List<AvesEntry> get entries => widget.entries;
static final _platformMobileServices = PlatformMobileServices();
// cap initial zoom to avoid a zoom change
// when toggling overlay on Google map initial state
static const double minInitialZoom = 3;
@ -172,7 +170,7 @@ class _GeoMapState extends State<GeoMap> {
case EntryMapStyle.googleTerrain:
case EntryMapStyle.hmsNormal:
case EntryMapStyle.hmsTerrain:
child = _platformMobileServices.buildMap<AvesEntry>(
child = mobileServices.buildMap<AvesEntry>(
controller: widget.controller,
clusterListenable: _clusterChangeNotifier,
boundsNotifier: _boundsNotifier,

View file

@ -5,7 +5,9 @@ import 'package:flutter/widgets.dart';
import 'package:latlong2/latlong.dart';
abstract class MobileServices {
Future<bool> isServiceAvailable();
Future<void> init();
bool get isServiceAvailable;
EntryMapStyle get defaultMapStyle;

View file

@ -3,28 +3,43 @@ library aves_services_platform;
import 'package:aves_map/aves_map.dart';
import 'package:aves_services/aves_services.dart';
import 'package:aves_services_platform/src/map.dart';
import 'package:flutter/foundation.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/widgets.dart';
import 'package:google_api_availability/google_api_availability.dart';
import 'package:latlong2/latlong.dart';
class PlatformMobileServices extends MobileServices {
bool? _isAvailable;
bool _isAvailable = false;
bool _canRenderMaps = false;
@override
Future<bool> isServiceAvailable() async {
if (_isAvailable != null) return SynchronousFuture(_isAvailable!);
Future<void> init() async {
final result = await GoogleApiAvailability.instance.checkGooglePlayServicesAvailability();
_isAvailable = result == GooglePlayServicesAvailability.success;
debugPrint('Device has Google Play Services=$_isAvailable');
return _isAvailable!;
// as of google_maps_flutter v2.1.1, minSDK is 20 because of default PlatformView usage,
// but using hybrid composition would make it usable on API 19 too,
// cf https://github.com/flutter/flutter/issues/23728
// as of google_maps_flutter v2.1.5, Flutter v3.0.1 makes the map hide overlay widgets on API <=22
final androidInfo = await DeviceInfoPlugin().androidInfo;
_canRenderMaps = (androidInfo.version.sdkInt ?? 0) >= 23;
}
@override
bool get isServiceAvailable => _isAvailable;
@override
EntryMapStyle get defaultMapStyle => EntryMapStyle.googleNormal;
@override
List<EntryMapStyle> get mapStyles => [EntryMapStyle.googleNormal, EntryMapStyle.googleHybrid, EntryMapStyle.googleTerrain];
List<EntryMapStyle> get mapStyles => (isServiceAvailable && _canRenderMaps)
? [
EntryMapStyle.googleNormal,
EntryMapStyle.googleHybrid,
EntryMapStyle.googleTerrain,
]
: [];
@override
Widget buildMap<T>({

View file

@ -12,6 +12,7 @@ dependencies:
path: ../aves_map
aves_services:
path: ../aves_services
device_info_plus:
google_api_availability:
google_maps_flutter:
latlong2:

View file

@ -3,7 +3,6 @@ library aves_services_platform;
import 'package:aves_map/aves_map.dart';
import 'package:aves_services/aves_services.dart';
import 'package:aves_services_platform/src/map.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:huawei_hmsavailability/huawei_hmsavailability.dart';
import 'package:latlong2/latlong.dart';
@ -12,22 +11,28 @@ class PlatformMobileServices extends MobileServices {
// cf https://developer.huawei.com/consumer/en/doc/development/hmscore-common-References/huaweiapiavailability-0000001050121134#section9492524178
static const int _hmsCoreAvailable = 0;
bool? _isAvailable;
bool _isAvailable = false;
@override
Future<bool> isServiceAvailable() async {
if (_isAvailable != null) return SynchronousFuture(_isAvailable!);
Future<void> init() async {
final result = await HmsApiAvailability().isHMSAvailable();
_isAvailable = result == _hmsCoreAvailable;
debugPrint('Device has Huawei Mobile Services=$_isAvailable');
return _isAvailable!;
}
@override
bool get isServiceAvailable => _isAvailable;
@override
EntryMapStyle get defaultMapStyle => EntryMapStyle.hmsNormal;
@override
List<EntryMapStyle> get mapStyles => [EntryMapStyle.hmsNormal, EntryMapStyle.hmsTerrain];
List<EntryMapStyle> get mapStyles => isServiceAvailable
? [
EntryMapStyle.hmsNormal,
EntryMapStyle.hmsTerrain,
]
: [];
@override
Widget buildMap<T>({