add m.location support
Needs cross-platform testing. Currently only tested on Android.
This commit is contained in:
parent
979068fc3a
commit
3a8d977f11
3 changed files with 810 additions and 12 deletions
82
lib/helpers/current_location_helper.dart
Normal file
82
lib/helpers/current_location_helper.dart
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import "dart:async";
|
||||
import "dart:io";
|
||||
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:geolocator/geolocator.dart";
|
||||
import "package:latlong2/latlong.dart";
|
||||
import "package:nexus/controllers/portal.dart";
|
||||
import "package:nexus/models/location_exception.dart";
|
||||
|
||||
class CurrentLocationHelper(final Ref ref) {
|
||||
static const _timeLimit = Duration(seconds: 10);
|
||||
|
||||
/// The user's location and its accuracy in meters, or null if [askPermission]
|
||||
/// is off and they haven't allowed it.
|
||||
Future<(LatLng, double?)?> get({required bool askPermission}) async {
|
||||
// geolocator talks to GeoClue directly, which Flatpak's sandbox blocks, so
|
||||
// Linux uses the location portal, which also asks the user for permission
|
||||
if (Platform.isLinux) {
|
||||
// The portal can't say whether it's allowed without asking
|
||||
if (!askPermission) return null;
|
||||
|
||||
try {
|
||||
final portal = await ref.read(PortalController.provider.future);
|
||||
final location = await portal.location
|
||||
.createSession(accuracy: .exact)
|
||||
.first
|
||||
.timeout(_timeLimit);
|
||||
|
||||
return switch ((location.latitude, location.longitude)) {
|
||||
(final latitude?, final longitude?) => (
|
||||
LatLng(latitude, longitude),
|
||||
location.accuracy,
|
||||
),
|
||||
_ => throw LocationException.failed,
|
||||
};
|
||||
} on TimeoutException {
|
||||
throw LocationException.timedOut;
|
||||
} catch (_) {
|
||||
throw LocationException.failed;
|
||||
}
|
||||
}
|
||||
|
||||
if (!await Geolocator.isLocationServiceEnabled()) {
|
||||
if (!askPermission) return null;
|
||||
throw LocationException.servicesOff;
|
||||
}
|
||||
|
||||
var permission = await Geolocator.checkPermission();
|
||||
if (permission == .denied && askPermission) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
}
|
||||
if (permission == .denied || permission == .deniedForever) {
|
||||
if (!askPermission) return null;
|
||||
throw LocationException.denied;
|
||||
}
|
||||
|
||||
Position? position;
|
||||
try {
|
||||
position = await Geolocator.getCurrentPosition(
|
||||
locationSettings: Platform.isAndroid
|
||||
// Play services' location can hang without Google, like on microG,
|
||||
// so this uses Android's own, as other apps do
|
||||
? AndroidSettings(forceLocationManager: true, timeLimit: _timeLimit)
|
||||
: const LocationSettings(timeLimit: _timeLimit),
|
||||
);
|
||||
} on TimeoutException {
|
||||
// A fix can take long indoors, but another app may have a recent one
|
||||
position = await Geolocator.getLastKnownPosition(
|
||||
forceAndroidLocationManager: true,
|
||||
);
|
||||
} catch (_) {
|
||||
throw LocationException.failed;
|
||||
}
|
||||
if (position == null) throw LocationException.timedOut;
|
||||
|
||||
return (LatLng(position.latitude, position.longitude), position.accuracy);
|
||||
}
|
||||
|
||||
static final provider = Provider<CurrentLocationHelper>(
|
||||
CurrentLocationHelper.new,
|
||||
);
|
||||
}
|
||||
|
|
@ -173,7 +173,9 @@ extension BuildEventOptions on Event {
|
|||
),
|
||||
),
|
||||
|
||||
if (content is MessageContent && sender == userId)
|
||||
if (content is MessageContent &&
|
||||
content is! LocationMessageContent &&
|
||||
sender == userId)
|
||||
PopupMenuItem(
|
||||
onTap: () => onRelation(rowId, .edit),
|
||||
child: const ListTile(leading: Icon(Icons.edit), title: Text("Edit")),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ import "package:nexus/helpers/extensions/is_desktop.dart";
|
|||
extension ShowAdaptiveSheet on BuildContext {
|
||||
/// Shows the widget from [builder] in a dialog on desktop, or a bottom sheet
|
||||
/// on mobile. Use the context passed to [builder] to close it.
|
||||
Future<T?> showAdaptiveSheet<T>(WidgetBuilder builder) => isDesktop
|
||||
Future<T?> showAdaptiveSheet<T>(
|
||||
WidgetBuilder builder, {
|
||||
bool enableDrag = true,
|
||||
}) => isDesktop
|
||||
? showDialog<T>(
|
||||
context: this,
|
||||
builder: (context) => Dialog(
|
||||
|
|
@ -18,6 +21,7 @@ extension ShowAdaptiveSheet on BuildContext {
|
|||
: showModalBottomSheet<T>(
|
||||
context: this,
|
||||
isScrollControlled: true,
|
||||
enableDrag: enableDrag,
|
||||
constraints: .new(maxHeight: MediaQuery.sizeOf(this).height - 80),
|
||||
builder: (context) => SafeArea(child: builder(context)),
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue