diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 3aa394f..d23cce2 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -28,6 +28,23 @@ + + + + + + + + + + + + + + + diff --git a/lib/utils/url_launcher.dart b/lib/utils/url_launcher.dart new file mode 100644 index 0000000..81555bd --- /dev/null +++ b/lib/utils/url_launcher.dart @@ -0,0 +1,55 @@ +import 'package:famedlysdk/famedlysdk.dart'; +import 'package:fluffychat/components/matrix.dart'; +import 'package:fluffychat/utils/app_route.dart'; +import 'package:fluffychat/views/chat.dart'; +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class UrlLauncher { + final String url; + final BuildContext context; + const UrlLauncher(this.context, this.url); + + void launchUrl() { + print("Open url: $url"); + if (url.startsWith("https://matrix.to/#/")) { + return openMatrixToUrl(); + } + launch(url); + } + + void openMatrixToUrl() async { + final matrix = Matrix.of(context); + final String identifier = url.replaceAll("https://matrix.to/#/", ""); + if (identifier.substring(0, 1) == "#") { + print("Join room ${Uri.encodeFull(identifier)}"); + final response = await matrix.tryRequestWithLoadingDialog( + matrix.client.joinRoomById( + Uri.encodeComponent(identifier), + ), + ); + if (response == false) return; + await Navigator.pushAndRemoveUntil( + context, + AppRoute.defaultRoute(context, Chat(response["room_id"])), + (r) => r.isFirst, + ); + } else if (identifier.substring(0, 1) == "@") { + print("Start chat with user $identifier"); + final User user = User( + identifier, + room: Room(id: "", client: matrix.client), + ); + final String roomID = + await matrix.tryRequestWithLoadingDialog(user.startDirectChat()); + Navigator.of(context).pop(); + + if (roomID != null) { + await Navigator.push( + context, + MaterialPageRoute(builder: (context) => Chat(roomID)), + ); + } + } + } +} diff --git a/lib/views/chat_details.dart b/lib/views/chat_details.dart index de87889..5cdc381 100644 --- a/lib/views/chat_details.dart +++ b/lib/views/chat_details.dart @@ -13,9 +13,9 @@ import 'package:fluffychat/utils/room_state_enums_extensions.dart'; import 'package:fluffychat/views/chat_list.dart'; import 'package:fluffychat/views/invitation_selection.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:image_picker/image_picker.dart'; import 'package:link_text/link_text.dart'; -import 'package:share/share.dart'; import 'package:toast/toast.dart'; class ChatDetails extends StatefulWidget { @@ -157,8 +157,13 @@ class _ChatDetailsState extends State { if (widget.room.canonicalAlias?.isNotEmpty ?? false) IconButton( icon: Icon(Icons.share), - onPressed: () => Share.share( - "https://matrix.to/#/${widget.room.canonicalAlias}"), + onPressed: () { + Clipboard.setData( + ClipboardData(text: widget.room.canonicalAlias), + ); + Toast.show("Invitation link copied to clipboard", context, + duration: 5); + }, ), ChatSettingsPopupMenu(widget.room, false) ], @@ -209,6 +214,7 @@ class _ChatDetailsState extends State { text: widget.room.topic?.isEmpty ?? true ? "Add a group description" : widget.room.topic, + linkStyle: TextStyle(color: Colors.blueAccent), textStyle: TextStyle( fontSize: 14, color: Colors.black, diff --git a/lib/views/chat_list.dart b/lib/views/chat_list.dart index 26f85ef..5b90464 100644 --- a/lib/views/chat_list.dart +++ b/lib/views/chat_list.dart @@ -7,10 +7,12 @@ import 'package:fluffychat/components/dialogs/new_private_chat_dialog.dart'; import 'package:fluffychat/components/list_items/chat_list_item.dart'; import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/utils/app_route.dart'; +import 'package:fluffychat/utils/url_launcher.dart'; import 'package:fluffychat/views/archive.dart'; import 'package:fluffychat/views/settings.dart'; import 'package:flutter/material.dart'; import 'package:flutter_speed_dial/flutter_speed_dial.dart'; +import 'package:receive_sharing_intent/receive_sharing_intent.dart'; enum SelectMode { normal, multi_select, share } @@ -57,15 +59,40 @@ class _ChatListState extends State { searchController.addListener( () => setState(() => null), ); + getSharedData(); super.initState(); } + StreamSubscription _intentDataStreamSubscription; + + void processSharedText(String text) { + if (text.startsWith("https://matrix.to/#/")) { + UrlLauncher(context, text).openMatrixToUrl(); + } else { + setState(() => Matrix.of(context).shareContent = { + "msgtype": "m.text", + "body": text, + }); + } + } + + void getSharedData() { + // For sharing or opening urls/text coming from outside the app while the app is in the memory + _intentDataStreamSubscription = ReceiveSharingIntent.getTextStream() + .listen(processSharedText, onError: (err) { + print("getLinkStream error: $err"); + }); + // For sharing or opening urls/text coming from outside the app while the app is closed + ReceiveSharingIntent.getInitialText().then(processSharedText); + } + @override void dispose() { sub?.cancel(); searchController.removeListener( () => setState(() => null), ); + _intentDataStreamSubscription?.cancel(); super.dispose(); } diff --git a/pubspec.lock b/pubspec.lock index 3915206..e582c26 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -263,6 +263,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.5" + receive_sharing_intent: + dependency: "direct main" + description: + name: receive_sharing_intent + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.2" share: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 2e1d835..d81f6cf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,6 +45,7 @@ dependencies: path_provider: ^1.5.1 webview_flutter: ^0.3.19+4 share: ^0.6.3+5 + receive_sharing_intent: ^1.3.2 dev_dependencies: flutter_test: