fix(android): drop geolocator for a native LocationManager channel
Removes the geolocator dependency, which transitively embedded com.google.android.gms.* (play-services-location) — proprietary classes that F-Droid's scanner rejects. The optional coarse-location button now talks to Android's own LocationManager over a small platform channel (org.comunes.tane/coarse_location): permission prompt, single coarse fix with a 12s timeout, last-known fallback, null on any failure. Behavior is unchanged and the CoarseLocationProvider interface is preserved, so the UI, i18n and widget tests need no changes. Also sets dependenciesInfo.includeInApk/Bundle = false so the AGP dependency-metadata block (also flagged by F-Droid) is not embedded. Neither change affects the Google Play build.
This commit is contained in:
parent
7f1118a243
commit
d0fabb80a9
9 changed files with 264 additions and 93 deletions
|
|
@ -1,50 +1,40 @@
|
|||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
/// Supplies the device's *approximate* location (or null when unavailable or
|
||||
/// denied). Behind an interface so the UI is fakeable in tests and the concrete
|
||||
/// platform plugin stays at the composition root. The caller immediately reduces
|
||||
/// platform code stays at the composition root. The caller immediately reduces
|
||||
/// the result to a low-precision geohash — a precise fix never leaves the device.
|
||||
abstract interface class CoarseLocationProvider {
|
||||
Future<({double lat, double lon})?> currentCoarseLatLon();
|
||||
}
|
||||
|
||||
/// [geolocator]-backed implementation. Requests low accuracy only, and returns
|
||||
/// null on any denial/error rather than throwing, so the UI can degrade quietly.
|
||||
class GeolocatorCoarseLocation implements CoarseLocationProvider {
|
||||
const GeolocatorCoarseLocation();
|
||||
/// Platform-channel implementation backed by Android's own LocationManager —
|
||||
/// deliberately no Google Play Services, so the APK stays free of proprietary
|
||||
/// classes (F-Droid inclusion). The native side handles the permission prompt,
|
||||
/// a coarse single fix with a timeout, and a last-known-position fallback.
|
||||
/// Returns null on any denial/error rather than throwing, so the UI can
|
||||
/// degrade quietly. Android only; other platforms return null.
|
||||
class NativeCoarseLocation implements CoarseLocationProvider {
|
||||
const NativeCoarseLocation();
|
||||
|
||||
static const _channel = MethodChannel('org.comunes.tane/coarse_location');
|
||||
|
||||
@override
|
||||
Future<({double lat, double lon})?> currentCoarseLatLon() async {
|
||||
if (defaultTargetPlatform != TargetPlatform.android) return null;
|
||||
try {
|
||||
var permission = await Geolocator.checkPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
}
|
||||
// Denied (or turned off): return null quietly — the UI shows an actionable
|
||||
// message. We don't yank the user into system settings.
|
||||
if (permission == LocationPermission.denied ||
|
||||
permission == LocationPermission.deniedForever) {
|
||||
return null;
|
||||
}
|
||||
if (!await Geolocator.isLocationServiceEnabled()) return null;
|
||||
|
||||
// A fresh coarse fix can take a while (or never come indoors); fall back
|
||||
// to the last known position so the button stays responsive.
|
||||
Position? position;
|
||||
try {
|
||||
position = await Geolocator.getCurrentPosition(
|
||||
locationSettings: const LocationSettings(
|
||||
accuracy: LocationAccuracy.low,
|
||||
timeLimit: Duration(seconds: 12),
|
||||
),
|
||||
);
|
||||
} catch (_) {
|
||||
position = await Geolocator.getLastKnownPosition();
|
||||
}
|
||||
if (position == null) return null;
|
||||
return (lat: position.latitude, lon: position.longitude);
|
||||
// Safety net over the native 12s fix timeout; the permission prompt can
|
||||
// legitimately keep the call open for a while, hence the wide margin.
|
||||
final result = await _channel
|
||||
.invokeMapMethod<String, double>('getCoarseLatLon')
|
||||
.timeout(const Duration(seconds: 60));
|
||||
final lat = result?['lat'];
|
||||
final lon = result?['lon'];
|
||||
if (lat == null || lon == null) return null;
|
||||
return (lat: lat, lon: lon);
|
||||
} catch (_) {
|
||||
return null; // plugin unsupported (desktop), etc.
|
||||
return null; // denied, timeout, channel error…
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue