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: