You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
meshcore-client/lib/utils/gpx_export.dart

165 lines
4.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import 'package:flutter/foundation.dart';
import 'package:gpx/gpx.dart';
import 'package:meshcore_open/connector/meshcore_connector.dart';
import 'package:meshcore_open/connector/meshcore_protocol.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';
import 'package:share_plus/share_plus.dart';
class ContactExport {
final String name;
final double lat;
final double lon;
final String desc;
final double? ele;
ContactExport({
required this.name,
required this.lat,
required this.lon,
required this.desc,
this.ele,
});
}
const int GpxExportFailed = -1;
const int GpxExportSuccess = 1;
const int GpxExportNoContacts = 2;
const int GpxExportCancelled = 3;
const int GpxExportNotAvailable = 4;
class GpxExport {
MeshCoreConnector _connector;
List<ContactExport> _contacts = [];
GpxExport(this._connector);
void _addContact(String name, double lat, double lon, String desc, [double? ele]) {
_contacts.add(ContactExport(
name: name.trim(),
lat: lat,
lon: lon,
desc: desc.trim(),
ele: ele,
));
}
void addRepeaters() {
final contacts = _connector.contacts;
final repeaters = contacts.where((c) => c.type == advTypeRepeater || c.type == advTypeRoom).toList();
for (var repeater in repeaters) {
if (repeater.latitude == null || repeater.longitude == null) {
continue;
}
_addContact(
repeater.name,
repeater.latitude ?? 0.0,
repeater.longitude ?? 0.0,
"Type: ${repeater.typeLabel}\nPublic Key: ${repeater.publicKeyHex}",
);
}
}
void addContacts() {
final contacts = _connector.contacts;
final repeaters = contacts.where((c) => c.type == advTypeChat).toList();
for (var repeater in repeaters) {
if (repeater.latitude == null || repeater.longitude == null) {
continue;
}
_addContact(
repeater.name,
repeater.latitude ?? 0.0,
repeater.longitude ?? 0.0,
"Type: ${repeater.typeLabel}\nPublic Key: ${repeater.publicKeyHex}",
);
}
}
void addAll() {
final contacts = _connector.contacts;
for (var repeater in contacts.toList()) {
if (repeater.latitude == null || repeater.longitude == null) {
continue;
}
_addContact(
repeater.name,
repeater.latitude ?? 0.0,
repeater.longitude ?? 0.0,
"Type: ${repeater.typeLabel}\nPublic Key: ${repeater.publicKeyHex}",
);
}
}
Future<int> exportGPX() async {
if (_contacts.isEmpty) {
debugPrint("No repeaters to export nothing to share.");
return GpxExportNoContacts;
}
try {
// 1. Build GPX content (your existing logic unchanged here)
final gpx = Gpx()
..version = '1.1'
..creator = 'meshcore-open Repeater Exporter'
..metadata = Metadata(
name: 'Meshcore Repeaters',
desc: 'Repeater & room locations exported from meshcore-open',
time: DateTime.now().toUtc(),
);
gpx.wpts = _contacts.map((c) => Wpt(
lat: c.lat,
lon: c.lon,
ele: c.ele,
name: c.name,
desc: c.desc,
)).toList();
final xml = GpxWriter().asString(gpx, pretty: true);
// 2. Save to file
final dir = await getApplicationDocumentsDirectory();
final timestamp = DateTime.now().toUtc().toIso8601String()
.replaceAll(':', '-')
.replaceAll('.', '-')
.split('T')
.join('_');
final path = '${dir.path}/meshcore_repeaters_$timestamp.gpx';
final file = File(path);
await file.writeAsString(xml);
// 3. Modern share call (2025+ style)
final result = await SharePlus.instance.share(
ShareParams(
text: 'Repeater locations exported from meshcore-open app as GPX file.',
subject: 'Meshcore Repeaters GPX Export',
files: [XFile(path)],
// Optional: sharePositionOrigin: ... (if you want iPad popover positioning)
),
);
// 4. Handle result
switch (result.status) {
case ShareResultStatus.success:
debugPrint('Share successful user completed the action.');
return GpxExportSuccess;
case ShareResultStatus.dismissed:
debugPrint('Share sheet was dismissed / cancelled by user.');
return GpxExportCancelled;
case ShareResultStatus.unavailable:
debugPrint('Sharing is not available on this platform / context.');
return GpxExportNotAvailable;
}
// Optional cleanup (uncomment if you don't want to keep the file)
// await file.delete();
} catch (e, stack) {
debugPrint('Export or share failed: $e\n$stack');
// → here you could show a SnackBar / AlertDialog in real UI code
}
return GpxExportFailed;
}
}

Powered by TurnKey Linux.