From d8b9b1a0b7743dbba321e5cb138b82cc55525d1d Mon Sep 17 00:00:00 2001 From: Geoffrey Bonneville Date: Mon, 16 Dec 2024 13:04:25 +0100 Subject: [PATCH] Add initial magnet & app link sharing for android & Linux --- app/android/app/src/main/AndroidManifest.xml | 19 +++++ app/lib/dialogs/share_torrent.dart | 69 ++++++++++++++++ app/lib/engine/torrent.dart | 4 +- .../engine/transmission/models/torrent.dart | 10 ++- app/lib/engine/transmission/transmission.dart | 34 ++++---- app/lib/navigation/app_shell_route.dart | 3 + .../screens/torrents/torrent_list_tile.dart | 41 +++++++--- app/lib/screens/torrents/torrents.dart | 12 ++- app/lib/utils/app_links.dart | 10 +++ .../flutter/generated_plugin_registrant.cc | 4 + app/linux/flutter/generated_plugins.cmake | 1 + app/linux/my_application.cc | 71 +++++++++------- .../Flutter/GeneratedPluginRegistrant.swift | 2 + app/pubspec.lock | 80 +++++++++++++++++++ app/pubspec.yaml | 1 + .../flutter/generated_plugin_registrant.cc | 6 ++ app/windows/flutter/generated_plugins.cmake | 2 + 17 files changed, 309 insertions(+), 60 deletions(-) create mode 100644 app/lib/dialogs/share_torrent.dart create mode 100644 app/lib/utils/app_links.dart diff --git a/app/android/app/src/main/AndroidManifest.xml b/app/android/app/src/main/AndroidManifest.xml index 4482f2c..2ec5921 100644 --- a/app/android/app/src/main/AndroidManifest.xml +++ b/app/android/app/src/main/AndroidManifest.xml @@ -55,21 +55,40 @@ + + + + + + + + + + + + + + + diff --git a/app/lib/dialogs/share_torrent.dart b/app/lib/dialogs/share_torrent.dart new file mode 100644 index 0000000..a12cad1 --- /dev/null +++ b/app/lib/dialogs/share_torrent.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:pikatorrent/engine/torrent.dart'; +import 'package:pikatorrent/utils/app_links.dart'; +import 'package:pikatorrent/utils/device.dart'; +import 'package:share_plus/share_plus.dart'; + +class ShareTorrentDialog extends StatelessWidget { + final Torrent torrent; + + const ShareTorrentDialog({ + super.key, + required this.torrent, + }); + + void shareMagnetLink(BuildContext context) async { + shareLink(context, torrent.magnetLink!); + } + + void shareAppLink(BuildContext context) async { + shareLink(context, createAppLink(torrent.magnetLink!)); + } + + void shareLink(BuildContext context, String link) async { + if (isMobile()) { + await Share.share(link); + } else { + Clipboard.setData(ClipboardData(text: link)); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text('Link copied'), + backgroundColor: Colors.lightGreen, + )); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Share Torrent'), + content: torrent.isPrivate! + ? const Text('This torrent is private to you and can\'t be shared.') + : null, + actions: torrent.isPrivate! + ? [ + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text('Cancel')) + ] + : [ + TextButton( + child: const Text('Magnet link'), + onPressed: () { + shareMagnetLink(context); + Navigator.pop(context); + }, + ), + TextButton( + child: const Text('PikaTorrent link'), + onPressed: () { + shareAppLink(context); + Navigator.pop(context); + }, + ), + ], + ); + } +} diff --git a/app/lib/engine/torrent.dart b/app/lib/engine/torrent.dart index 0e20d1b..499cb8b 100644 --- a/app/lib/engine/torrent.dart +++ b/app/lib/engine/torrent.dart @@ -39,6 +39,7 @@ abstract class Torrent extends TorrentBase { final String? comment; final List? files; final int? peersConnected; + final String? magnetLink; Torrent( {required super.id, @@ -61,7 +62,8 @@ abstract class Torrent extends TorrentBase { this.comment, this.creator, this.files, - this.peersConnected}); + this.peersConnected, + this.magnetLink}); // Start the torrent start(); diff --git a/app/lib/engine/transmission/models/torrent.dart b/app/lib/engine/transmission/models/torrent.dart index 22eb568..948b0fc 100644 --- a/app/lib/engine/transmission/models/torrent.dart +++ b/app/lib/engine/transmission/models/torrent.dart @@ -22,7 +22,8 @@ enum TorrentField { files, fileStats, labels, - peersConnected + peersConnected, + magnetLink } class TransmissionTorrentFile { @@ -70,6 +71,7 @@ class TransmissionTorrent { final List? fileStats; final List? labels; final int? peersConnected; + final String? magnetLink; const TransmissionTorrent( this.id, @@ -93,7 +95,8 @@ class TransmissionTorrent { this.files, this.labels, this.peersConnected, - this.fileStats); + this.fileStats, + this.magnetLink); TransmissionTorrent.fromJson(Map json) : id = json['id'], @@ -128,5 +131,6 @@ class TransmissionTorrent { .toList(), labels = json['labels'] != null ? List.from(json['labels']) : null, - peersConnected = json['peersConnected']; + peersConnected = json['peersConnected'], + magnetLink = json['magnetLink']; } diff --git a/app/lib/engine/transmission/transmission.dart b/app/lib/engine/transmission/transmission.dart index 197c30d..971702a 100644 --- a/app/lib/engine/transmission/transmission.dart +++ b/app/lib/engine/transmission/transmission.dart @@ -51,7 +51,8 @@ class TransmissionTorrent extends Torrent { super.comment, super.files, super.labels, - super.peersConnected}); + super.peersConnected, + super.magnetLink}); @override start() { @@ -173,6 +174,8 @@ class TransmissionEngine implements Engine { TorrentField.labels, TorrentField.addedDate, TorrentField.errorString, + TorrentField.magnetLink, + TorrentField.isPrivate ])); String res = await flutter_libtransmission .requestAsync(jsonEncode(torrentGetRequest)); @@ -182,17 +185,18 @@ class TransmissionEngine implements Engine { return decodedRes.arguments.torrents .map((torrent) => TransmissionTorrent( - id: torrent.id, - name: torrent.name, - progress: torrent.percentDone, - status: torrent.status, - size: torrent.totalSize, - rateDownload: torrent.rateDownload, - rateUpload: torrent.rateUpload, - labels: torrent.labels, - addedDate: torrent.addedDate, - errorString: torrent.errorString, - )) + id: torrent.id, + name: torrent.name, + progress: torrent.percentDone, + status: torrent.status, + size: torrent.totalSize, + rateDownload: torrent.rateDownload, + rateUpload: torrent.rateUpload, + labels: torrent.labels, + addedDate: torrent.addedDate, + errorString: torrent.errorString, + magnetLink: torrent.magnetLink, + isPrivate: torrent.isPrivate)) .toList(); } @@ -223,7 +227,8 @@ class TransmissionEngine implements Engine { TorrentField.files, TorrentField.fileStats, TorrentField.labels, - TorrentField.peersConnected + TorrentField.peersConnected, + TorrentField.magnetLink ])); String res = await flutter_libtransmission @@ -262,7 +267,8 @@ class TransmissionEngine implements Engine { wanted: torrent.fileStats![entry.key].wanted)) .toList(), labels: torrent.labels, - peersConnected: torrent.peersConnected)) + peersConnected: torrent.peersConnected, + magnetLink: torrent.magnetLink)) .toList() .first; } diff --git a/app/lib/navigation/app_shell_route.dart b/app/lib/navigation/app_shell_route.dart index 9782ba2..c045fb5 100644 --- a/app/lib/navigation/app_shell_route.dart +++ b/app/lib/navigation/app_shell_route.dart @@ -4,6 +4,7 @@ import 'package:pikatorrent/dialogs/add_torrent.dart'; import 'package:pikatorrent/dialogs/terms_of_use.dart'; import 'package:pikatorrent/models/app.dart'; import 'package:pikatorrent/navigation/navigation.dart'; +import 'package:pikatorrent/utils/app_links.dart'; import 'package:provider/provider.dart'; class AppShellRoute extends StatefulWidget { @@ -36,6 +37,8 @@ class _AppShellRouteState extends State { } else if (uriString.startsWith('content://') || uriString.startsWith('file://')) { _openAddTorrentDialog(null, uriString); + } else if (uriString.startsWith(appUri)) { + _openAddTorrentDialog(getTorrentLink(uriString), null); } }); } diff --git a/app/lib/screens/torrents/torrent_list_tile.dart b/app/lib/screens/torrents/torrent_list_tile.dart index cc664a0..d91b0c5 100644 --- a/app/lib/screens/torrents/torrent_list_tile.dart +++ b/app/lib/screens/torrents/torrent_list_tile.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:pikatorrent/dialogs/remove_torrent.dart'; +import 'package:pikatorrent/dialogs/share_torrent.dart'; import 'package:pikatorrent/engine/torrent.dart'; import 'package:pikatorrent/models/torrents.dart'; import 'package:pikatorrent/screens/torrents/sheets/torrent_details/torrent_details.dart'; @@ -26,8 +27,9 @@ class TorrentListTile extends StatelessWidget { Widget build(BuildContext context) { return Consumer(builder: (context, torrentsModel, child) { return ListTile( - contentPadding: - !isMobileSize(context) ? const EdgeInsets.only(left: 16, right: 16) : null, + contentPadding: !isMobileSize(context) + ? const EdgeInsets.only(left: 16, right: 16) + : null, onTap: () { showDeviceSheet(context, torrent.name ?? 'Torrent details', TorrentDetailsModalSheet(id: torrent.id)); @@ -74,16 +76,31 @@ class TorrentListTile extends StatelessWidget { ], ), trailing: (!isMobileSize(context)) - ? IconButton( - tooltip: 'Remove', - onPressed: () => showDialog( - context: context, - builder: (BuildContext context) { - return RemoveTorrentDialog(torrent: torrent); - }), - icon: const Icon( - Icons.delete_outline, - )) + ? Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + tooltip: 'Share', + onPressed: () => showDialog( + context: context, + builder: (BuildContext context) { + return ShareTorrentDialog(torrent: torrent); + }), + icon: const Icon( + Icons.share, + )), + IconButton( + tooltip: 'Remove', + onPressed: () => showDialog( + context: context, + builder: (BuildContext context) { + return RemoveTorrentDialog(torrent: torrent); + }), + icon: const Icon( + Icons.delete_outline, + )), + ], + ) : null, subtitle: Row(children: [ diff --git a/app/lib/screens/torrents/torrents.dart b/app/lib/screens/torrents/torrents.dart index d3915d7..0d0822c 100644 --- a/app/lib/screens/torrents/torrents.dart +++ b/app/lib/screens/torrents/torrents.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:pikatorrent/dialogs/remove_torrent.dart'; +import 'package:pikatorrent/dialogs/share_torrent.dart'; import 'package:pikatorrent/engine/torrent.dart'; import 'package:pikatorrent/models/torrents.dart'; import 'package:pikatorrent/screens/torrents/filter_labels_button.dart'; @@ -78,8 +79,17 @@ class _TorrentScreen extends State key: Key(index.toString()), endActionPane: ActionPane( motion: const ScrollMotion(), - extentRatio: 0.2, + extentRatio: 0.4, children: [ + SlidableAction( + backgroundColor: Colors.blue, + onPressed: (_) => showDialog( + context: context, + builder: (BuildContext context) { + return ShareTorrentDialog(torrent: torrent); + }), + icon: Icons.share, + ), SlidableAction( backgroundColor: Colors.red, onPressed: (_) => showDialog( diff --git a/app/lib/utils/app_links.dart b/app/lib/utils/app_links.dart new file mode 100644 index 0000000..601aeb5 --- /dev/null +++ b/app/lib/utils/app_links.dart @@ -0,0 +1,10 @@ +const appUri = 'https://www.pikatorrent.com/'; + +createAppLink(String link) { + return Uri.encodeFull('$appUri#$link'); +} + +/// get torrent link from an app link created with createAppLink +getTorrentLink(String appLink) { + return appLink.replaceFirst('$appUri#', ''); +} \ No newline at end of file diff --git a/app/linux/flutter/generated_plugin_registrant.cc b/app/linux/flutter/generated_plugin_registrant.cc index 3e43b2e..508b61c 100644 --- a/app/linux/flutter/generated_plugin_registrant.cc +++ b/app/linux/flutter/generated_plugin_registrant.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -26,6 +27,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin"); screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar); + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); g_autoptr(FlPluginRegistrar) window_manager_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); window_manager_plugin_register_with_registrar(window_manager_registrar); diff --git a/app/linux/flutter/generated_plugins.cmake b/app/linux/flutter/generated_plugins.cmake index 9351869..d0eb06d 100644 --- a/app/linux/flutter/generated_plugins.cmake +++ b/app/linux/flutter/generated_plugins.cmake @@ -7,6 +7,7 @@ list(APPEND FLUTTER_PLUGIN_LIST handy_window open_file_linux screen_retriever_linux + url_launcher_linux window_manager yaru_window_linux ) diff --git a/app/linux/my_application.cc b/app/linux/my_application.cc index c4cde33..5df2494 100644 --- a/app/linux/my_application.cc +++ b/app/linux/my_application.cc @@ -1,6 +1,7 @@ #include "my_application.h" #include + #ifdef GDK_WINDOWING_X11 #include #endif @@ -8,17 +9,25 @@ #include "flutter/generated_plugin_registrant.h" struct _MyApplication { - GtkApplication parent_instance; - char** dart_entrypoint_arguments; + GtkApplication parent_instance; + char **dart_entrypoint_arguments; }; -G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION +) // Implements GApplication::activate. -static void my_application_activate(GApplication* application) { - MyApplication* self = MY_APPLICATION(application); - GtkWindow* window = - GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); +static void my_application_activate(GApplication *application) { + MyApplication * self = MY_APPLICATION(application); + + GList *windows = gtk_application_get_windows(GTK_APPLICATION(application)); + if (windows) { + gtk_window_present(GTK_WINDOW(windows->data)); + return; + } + + GtkWindow *window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); // Use a header bar when running in GNOME as this is the common style used // by applications and is the setup most users will be using (e.g. Ubuntu @@ -38,7 +47,7 @@ static void my_application_activate(GApplication* application) { } #endif if (use_header_bar) { - GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); gtk_widget_show(GTK_WIDGET(header_bar)); gtk_header_bar_set_title(header_bar, "PikaTorrent"); gtk_header_bar_set_show_close_button(header_bar, TRUE); @@ -47,17 +56,18 @@ static void my_application_activate(GApplication* application) { gtk_window_set_title(window, "PikaTorrent"); } - GdkGeometry geometry_min; - geometry_min.min_width = 360; - geometry_min.min_height = 360; - gtk_window_set_geometry_hints(window, nullptr, &geometry_min, GDK_HINT_MIN_SIZE); + GdkGeometry geometry_min; + geometry_min.min_width = 360; + geometry_min.min_height = 360; + gtk_window_set_geometry_hints(window, nullptr, &geometry_min, GDK_HINT_MIN_SIZE); gtk_window_set_default_size(window, 1280, 720); // Set minimum window size - g_autoptr(FlDartProject) project = fl_dart_project_new(); + g_autoptr(FlDartProject) + project = fl_dart_project_new(); fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); - FlView* view = fl_view_new(project); + FlView *view = fl_view_new(project); // gtk_widget_show(GTK_WIDGET(view)); gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); @@ -69,26 +79,28 @@ static void my_application_activate(GApplication* application) { } // Implements GApplication::local_command_line. -static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { - MyApplication* self = MY_APPLICATION(application); +static gboolean +my_application_local_command_line(GApplication *application, gchar ***arguments, int *exit_status) { + MyApplication * self = MY_APPLICATION(application); // Strip out the first argument as it is the binary name. self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); - g_autoptr(GError) error = nullptr; + g_autoptr(GError) + error = nullptr; if (!g_application_register(application, nullptr, &error)) { - g_warning("Failed to register: %s", error->message); - *exit_status = 1; - return TRUE; + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; } g_application_activate(application); *exit_status = 0; - return TRUE; + return FALSE; } // Implements GApplication::startup. -static void my_application_startup(GApplication* application) { +static void my_application_startup(GApplication *application) { //MyApplication* self = MY_APPLICATION(object); // Perform any actions required at application startup. @@ -97,7 +109,7 @@ static void my_application_startup(GApplication* application) { } // Implements GApplication::shutdown. -static void my_application_shutdown(GApplication* application) { +static void my_application_shutdown(GApplication *application) { //MyApplication* self = MY_APPLICATION(object); // Perform any actions required at application shutdown. @@ -106,13 +118,13 @@ static void my_application_shutdown(GApplication* application) { } // Implements GObject::dispose. -static void my_application_dispose(GObject* object) { - MyApplication* self = MY_APPLICATION(object); +static void my_application_dispose(GObject *object) { + MyApplication * self = MY_APPLICATION(object); g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); G_OBJECT_CLASS(my_application_parent_class)->dispose(object); } -static void my_application_class_init(MyApplicationClass* klass) { +static void my_application_class_init(MyApplicationClass *klass) { G_APPLICATION_CLASS(klass)->activate = my_application_activate; G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; G_APPLICATION_CLASS(klass)->startup = my_application_startup; @@ -120,11 +132,12 @@ static void my_application_class_init(MyApplicationClass* klass) { G_OBJECT_CLASS(klass)->dispose = my_application_dispose; } -static void my_application_init(MyApplication* self) {} +static void my_application_init(MyApplication * self) {} -MyApplication* my_application_new() { +MyApplication *my_application_new() { return MY_APPLICATION(g_object_new(my_application_get_type(), "application-id", APPLICATION_ID, - "flags", G_APPLICATION_NON_UNIQUE, + "flags", G_APPLICATION_HANDLES_COMMAND_LINE | + G_APPLICATION_HANDLES_OPEN, nullptr)); } diff --git a/app/macos/Flutter/GeneratedPluginRegistrant.swift b/app/macos/Flutter/GeneratedPluginRegistrant.swift index bf60b19..5ed701c 100644 --- a/app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -11,6 +11,7 @@ import flutter_local_notifications import open_file_mac import path_provider_foundation import screen_retriever_macos +import share_plus import shared_preferences_foundation import window_manager @@ -21,6 +22,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { OpenFilePlugin.register(with: registry.registrar(forPlugin: "OpenFilePlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin")) + SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) } diff --git a/app/pubspec.lock b/app/pubspec.lock index 0d0b17a..d1d373f 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -225,6 +225,14 @@ packages: url: "https://pub.dev" source: hosted version: "8.1.3" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -457,6 +465,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.15.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" nested: dependency: transitive description: @@ -729,6 +745,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.0" + share_plus: + dependency: "direct main" + description: + name: share_plus + sha256: "6327c3f233729374d0abaafd61f6846115b2a481b4feddd8534211dc10659400" + url: "https://pub.dev" + source: hosted + version: "10.1.3" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b + url: "https://pub.dev" + source: hosted + version: "5.0.2" shared_preferences: dependency: "direct main" description: @@ -798,6 +830,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" stack_trace: dependency: transitive description: @@ -854,6 +894,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" + url: "https://pub.dev" + source: hosted + version: "2.3.3" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "44cf3aabcedde30f2dba119a9dea3b0f2672fbe6fa96e85536251d678216b3c4" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" vector_graphics: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 0afc274..0a4ea81 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -60,6 +60,7 @@ dependencies: external_path: ^2.0.1 device_info_plus: ^11.1.1 collection: ^1.18.0 + share_plus: ^10.1.3 dev_dependencies: flutter_test: diff --git a/app/windows/flutter/generated_plugin_registrant.cc b/app/windows/flutter/generated_plugin_registrant.cc index 1154c0d..4762b7d 100644 --- a/app/windows/flutter/generated_plugin_registrant.cc +++ b/app/windows/flutter/generated_plugin_registrant.cc @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { @@ -18,6 +20,10 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi")); + SharePlusWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); WindowManagerPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("WindowManagerPlugin")); } diff --git a/app/windows/flutter/generated_plugins.cmake b/app/windows/flutter/generated_plugins.cmake index 33d5e97..166be3b 100644 --- a/app/windows/flutter/generated_plugins.cmake +++ b/app/windows/flutter/generated_plugins.cmake @@ -6,6 +6,8 @@ list(APPEND FLUTTER_PLUGIN_LIST app_links permission_handler_windows screen_retriever_windows + share_plus + url_launcher_windows window_manager )