From 2dd5f5cb87d062e5f2671eb7b7d35c70479e41a6 Mon Sep 17 00:00:00 2001 From: Anna Barsukova Date: Wed, 30 Oct 2024 19:56:14 +0300 Subject: [PATCH 1/2] Open geo intents --- android/app/src/main/AndroidManifest.xml | 9 ++++- ios/Runner/Info.plist | 11 ++++++ lib/helpers/navigation_helper.dart | 11 ++++++ lib/main.dart | 50 +++++++++++++++++------- lib/providers/app_links_provider.dart | 41 +++++++++++++++++++ lib/providers/navigation_provider.dart | 6 +++ lib/screens/browser.dart | 2 +- pubspec.lock | 40 +++++++++++++++++++ pubspec.yaml | 1 + 9 files changed, 154 insertions(+), 17 deletions(-) create mode 100644 lib/helpers/navigation_helper.dart create mode 100644 lib/providers/app_links_provider.dart create mode 100644 lib/providers/navigation_provider.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 236b0cd3..47226674 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 9efd4e36..2cc694f6 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 00000000..27fa2742 --- /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 e68fa72e..7b8d619c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,7 +2,10 @@ import 'dart:async'; import 'dart:io'; import 'package:every_door/helpers/log_store.dart'; +import 'package:every_door/helpers/navigation_helper.dart'; +import 'package:every_door/providers/app_links_provider.dart'; import 'package:every_door/providers/language.dart'; +import 'package:every_door/providers/navigation_provider.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:every_door/constants.dart'; @@ -44,23 +47,40 @@ class EveryDoorApp extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final navigatorKey = ref.read(navigatonKeyProvider); + final uriAsyncValue = ref.watch(uriLinkStreamProvider); + + WidgetsBinding.instance.addPostFrameCallback((_) { + uriAsyncValue.whenData((uri) { + if (uri != null) { + final screen = NavigationHelper.navigateByUri(uri); + if (context.mounted) { + navigatorKey.currentState?.push( + MaterialPageRoute(builder: (context) => screen), + ); + } + } + }); + }); + 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), - ]), + navigatorKey: navigatorKey, + 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 00000000..0ca62490 --- /dev/null +++ b/lib/providers/app_links_provider.dart @@ -0,0 +1,41 @@ +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 appLinksProvider = Provider((ref) => AppLinks()); + +final uriLinkStreamProvider = StreamProvider((ref) async* { + final appLinks = ref.watch(appLinksProvider); + 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 00000000..0512b887 --- /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 369bbe6b..f0448fa3 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/pubspec.lock b/pubspec.lock index b9e568dc..0a2b9ab7 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 12222123..4845424e 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: From d3606f7be9a7822dc26fd2a443423bb51597cc1c Mon Sep 17 00:00:00 2001 From: Ilya Zverev Date: Tue, 3 Dec 2024 23:48:16 +0200 Subject: [PATCH 2/2] Should work --- lib/main.dart | 20 ---------------- lib/providers/app_links_provider.dart | 34 ++++++++++++++++++++++++--- lib/screens/loading.dart | 2 ++ 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 7b8d619c..823f072d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,10 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'package:every_door/helpers/log_store.dart'; -import 'package:every_door/helpers/navigation_helper.dart'; -import 'package:every_door/providers/app_links_provider.dart'; import 'package:every_door/providers/language.dart'; -import 'package:every_door/providers/navigation_provider.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:every_door/constants.dart'; @@ -47,25 +44,8 @@ class EveryDoorApp extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final navigatorKey = ref.read(navigatonKeyProvider); - final uriAsyncValue = ref.watch(uriLinkStreamProvider); - - WidgetsBinding.instance.addPostFrameCallback((_) { - uriAsyncValue.whenData((uri) { - if (uri != null) { - final screen = NavigationHelper.navigateByUri(uri); - if (context.mounted) { - navigatorKey.currentState?.push( - MaterialPageRoute(builder: (context) => screen), - ); - } - } - }); - }); - return Portal( child: MaterialApp( - navigatorKey: navigatorKey, title: kAppTitle, theme: ThemeData( primarySwatch: Colors.blue, diff --git a/lib/providers/app_links_provider.dart b/lib/providers/app_links_provider.dart index 0ca62490..90814233 100644 --- a/lib/providers/app_links_provider.dart +++ b/lib/providers/app_links_provider.dart @@ -5,11 +5,39 @@ import 'package:app_links/app_links.dart'; import 'package:latlong2/latlong.dart'; import 'package:logging/logging.dart'; -final appLinksProvider = Provider((ref) => AppLinks()); +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* { - final appLinks = ref.watch(appLinksProvider); - await for (final uri in appLinks.uriLinkStream) { + await for (final uri in AppLinks().uriLinkStream) { if (uri.scheme == 'geo' && uri.path.isNotEmpty) { _handleGeoIntent(uri, ref); } diff --git a/lib/screens/loading.dart b/lib/screens/loading.dart index cd7c8bc7..84ea6195 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 =