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.
40 lines
1.8 KiB
Dart
40 lines
1.8 KiB
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 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();
|
|
}
|
|
|
|
/// 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 {
|
|
// 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; // denied, timeout, channel error…
|
|
}
|
|
}
|
|
}
|