diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 236b0cd..4722667 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -12,7 +12,7 @@ android:fullBackupContent="@xml/backup_rules"> + + + + + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 9efd4e3..2cc694f 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -32,6 +32,17 @@ everydoor + + + CFBundleTypeRole + Editor + CFBundleURLName + geo + CFBundleURLSchemes + + geo + + CFBundleVersion $(FLUTTER_BUILD_NUMBER) diff --git a/lib/helpers/navigation_helper.dart b/lib/helpers/navigation_helper.dart new file mode 100644 index 0000000..27fa274 --- /dev/null +++ b/lib/helpers/navigation_helper.dart @@ -0,0 +1,11 @@ +import 'package:every_door/screens/browser.dart'; +import 'package:flutter/material.dart'; + +class NavigationHelper { + static Widget navigateByUri(Uri uri) { + if (uri.scheme == 'geo' && uri.path.isNotEmpty) { + return BrowserPage(); + } + return BrowserPage(); + } +} diff --git a/lib/main.dart b/lib/main.dart index e68fa72..823f072 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -46,21 +46,21 @@ class EveryDoorApp extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { return Portal( child: MaterialApp( - title: kAppTitle, - theme: ThemeData( - primarySwatch: Colors.blue, - hintColor: Theme.of(context).colorScheme.onSurface.withOpacity(0.3), - useMaterial3: false, - ), - localizationsDelegates: AppLocalizations.localizationsDelegates, - // Adding "en" to the front so it's used by default. - supportedLocales: [Locale('en')] + AppLocalizations.supportedLocales, - locale: ref.watch(languageProvider), - home: LoadingPage(), - builder: (context, child) => Stack(children: [ - if (child != null) child, - DropdownAlert(delayDismiss: 5000), - ]), + title: kAppTitle, + theme: ThemeData( + primarySwatch: Colors.blue, + hintColor: Theme.of(context).colorScheme.onSurface.withOpacity(0.3), + useMaterial3: false, + ), + localizationsDelegates: AppLocalizations.localizationsDelegates, + // Adding "en" to the front so it's used by default. + supportedLocales: [Locale('en')] + AppLocalizations.supportedLocales, + locale: ref.watch(languageProvider), + home: LoadingPage(), + builder: (context, child) => Stack(children: [ + if (child != null) child, + DropdownAlert(delayDismiss: 5000), + ]), ), ); } diff --git a/lib/providers/app_links_provider.dart b/lib/providers/app_links_provider.dart new file mode 100644 index 0000000..9081423 --- /dev/null +++ b/lib/providers/app_links_provider.dart @@ -0,0 +1,69 @@ +import 'package:every_door/providers/geolocation.dart'; +import 'package:every_door/providers/location.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:app_links/app_links.dart'; +import 'package:latlong2/latlong.dart'; +import 'package:logging/logging.dart'; + +final geoIntentProvider = Provider((ref) => GeoIntentController(ref)); + +class GeoIntentController { + final Ref _ref; + + GeoIntentController(this._ref) { + initStreamListener(); + } + + initStreamListener() async { + await for (final uri in AppLinks().uriLinkStream) { + _handleGeoIntent(uri); + } + } + + checkLatestIntent() async { + final latest = await AppLinks().getLatestLink(); + if (latest != null) _handleGeoIntent(latest); + } + + _handleGeoIntent(Uri uri) { + if (uri.scheme == 'geo' && uri.path.isNotEmpty) { + final location = _parseLatLngFromGeoUri(uri); + if (location != null) { + _ref.read(geolocationProvider.notifier).disableTracking(); + _ref.read(effectiveLocationProvider.notifier).set(location); + } + } + } +} + +final uriLinkStreamProvider = StreamProvider((ref) async* { + await for (final uri in AppLinks().uriLinkStream) { + if (uri.scheme == 'geo' && uri.path.isNotEmpty) { + _handleGeoIntent(uri, ref); + } + + yield uri; + } +}); + +void _handleGeoIntent(Uri uri, StreamProviderRef ref) { + final location = _parseLatLngFromGeoUri(uri); + if (location != null) { + ref.read(geolocationProvider.notifier).disableTracking(); + ref.read(effectiveLocationProvider.notifier).set(location); + } +} + +LatLng? _parseLatLngFromGeoUri(Uri uri) { + try { + final coords = uri.path.split(','); + if (coords.length == 2) { + final lat = double.parse(coords[0]); + final lng = double.parse(coords[1]); + return LatLng(lat, lng); + } + } catch (e) { + Logger('AppLinks').warning('Failed to parse coordinates'); + } + return null; +} diff --git a/lib/providers/navigation_provider.dart b/lib/providers/navigation_provider.dart new file mode 100644 index 0000000..0512b88 --- /dev/null +++ b/lib/providers/navigation_provider.dart @@ -0,0 +1,6 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final navigatonKeyProvider = Provider>((ref) { + return GlobalKey(); +}); diff --git a/lib/screens/browser.dart b/lib/screens/browser.dart index 369bbe6..f0448fa 100644 --- a/lib/screens/browser.dart +++ b/lib/screens/browser.dart @@ -69,7 +69,7 @@ class _BrowserPageState extends ConsumerState { final location = ref.read(effectiveLocationProvider); final bbox = boundsFromRadius(location, kVisibilityRadius); final status = await area.getAreaStatus(bbox); - if (status != areaStatus) { + if (status != areaStatus && mounted) { setState(() { areaStatus = status; }); diff --git a/lib/screens/loading.dart b/lib/screens/loading.dart index cd7c8bc..84ea619 100644 --- a/lib/screens/loading.dart +++ b/lib/screens/loading.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:every_door/providers/app_links_provider.dart'; import 'package:every_door/providers/changes.dart'; import 'package:every_door/providers/changeset_tags.dart'; import 'package:every_door/providers/geolocation.dart'; @@ -79,6 +80,7 @@ class _LoadingPageState extends ConsumerState { if (location != null) { ref.read(effectiveLocationProvider.notifier).set(location); } + await ref.read(geoIntentProvider).checkLatestIntent(); // Alert if there are too many changes loaded. final needSizeAlert = diff --git a/pubspec.lock b/pubspec.lock index d16f8f8..3662ebb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -38,6 +38,38 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.11" + app_links: + dependency: "direct main" + description: + name: app_links + sha256: ad1a6d598e7e39b46a34f746f9a8b011ee147e4c275d407fa457e7a62f84dd99 + url: "https://pub.dev" + source: hosted + version: "6.3.2" + app_links_linux: + dependency: transitive + description: + name: app_links_linux + sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81 + url: "https://pub.dev" + source: hosted + version: "1.0.3" + app_links_platform_interface: + dependency: transitive + description: + name: app_links_platform_interface + sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + app_links_web: + dependency: transitive + description: + name: app_links_web + sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555 + url: "https://pub.dev" + source: hosted + version: "1.0.4" appkit_ui_element_colors: dependency: transitive description: @@ -499,6 +531,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + gtk: + dependency: transitive + description: + name: gtk + sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c + url: "https://pub.dev" + source: hosted + version: "2.1.0" http: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index c3f8df1..a26bea7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -52,6 +52,7 @@ dependencies: url_launcher: ^6.1.14 uuid: ^4.3.3 xml: ^6.3.0 + app_links: ^6.3.2 dependency_overrides: flutter_dropdown_alert: