Skip to content

Commit

Permalink
Add initial magnet & app link sharing for android & Linux
Browse files Browse the repository at this point in the history
  • Loading branch information
G-Ray committed Dec 16, 2024
1 parent d5e9aa4 commit d8b9b1a
Show file tree
Hide file tree
Showing 17 changed files with 309 additions and 60 deletions.
19 changes: 19 additions & 0 deletions app/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,40 @@
<!-- Handle magnet: links -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="magnet" />
</intent-filter>

<!-- Handle .torrent files -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="content" />
<data android:scheme="file" />
<data android:mimeType="application/x-bittorrent" />
<data android:pathPattern=".*\\.torrent" />
</intent-filter>

<!-- App Links -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="www.pikatorrent.com"
android:scheme="http" />
<data
android:host="www.pikatorrent.com"
android:scheme="https" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
Expand Down
69 changes: 69 additions & 0 deletions app/lib/dialogs/share_torrent.dart
Original file line number Diff line number Diff line change
@@ -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);
},
),
],
);
}
}
4 changes: 3 additions & 1 deletion app/lib/engine/torrent.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ abstract class Torrent extends TorrentBase {
final String? comment;
final List<File>? files;
final int? peersConnected;
final String? magnetLink;

Torrent(
{required super.id,
Expand All @@ -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();
Expand Down
10 changes: 7 additions & 3 deletions app/lib/engine/transmission/models/torrent.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ enum TorrentField {
files,
fileStats,
labels,
peersConnected
peersConnected,
magnetLink
}

class TransmissionTorrentFile {
Expand Down Expand Up @@ -70,6 +71,7 @@ class TransmissionTorrent {
final List<TransmissionTorrentFileStats>? fileStats;
final List<String>? labels;
final int? peersConnected;
final String? magnetLink;

const TransmissionTorrent(
this.id,
Expand All @@ -93,7 +95,8 @@ class TransmissionTorrent {
this.files,
this.labels,
this.peersConnected,
this.fileStats);
this.fileStats,
this.magnetLink);

TransmissionTorrent.fromJson(Map<String, dynamic> json)
: id = json['id'],
Expand Down Expand Up @@ -128,5 +131,6 @@ class TransmissionTorrent {
.toList(),
labels =
json['labels'] != null ? List<String>.from(json['labels']) : null,
peersConnected = json['peersConnected'];
peersConnected = json['peersConnected'],
magnetLink = json['magnetLink'];
}
34 changes: 20 additions & 14 deletions app/lib/engine/transmission/transmission.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ class TransmissionTorrent extends Torrent {
super.comment,
super.files,
super.labels,
super.peersConnected});
super.peersConnected,
super.magnetLink});

@override
start() {
Expand Down Expand Up @@ -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));
Expand All @@ -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();
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down
3 changes: 3 additions & 0 deletions app/lib/navigation/app_shell_route.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -36,6 +37,8 @@ class _AppShellRouteState extends State<AppShellRoute> {
} else if (uriString.startsWith('content://') ||
uriString.startsWith('file://')) {
_openAddTorrentDialog(null, uriString);
} else if (uriString.startsWith(appUri)) {
_openAddTorrentDialog(getTorrentLink(uriString), null);
}
});
}
Expand Down
41 changes: 29 additions & 12 deletions app/lib/screens/torrents/torrent_list_tile.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -26,8 +27,9 @@ class TorrentListTile extends StatelessWidget {
Widget build(BuildContext context) {
return Consumer<TorrentsModel>(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));
Expand Down Expand Up @@ -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: [
Expand Down
12 changes: 11 additions & 1 deletion app/lib/screens/torrents/torrents.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -78,8 +79,17 @@ class _TorrentScreen extends State<TorrentsScreen>
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(
Expand Down
10 changes: 10 additions & 0 deletions app/lib/utils/app_links.dart
Original file line number Diff line number Diff line change
@@ -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#', '');
}
4 changes: 4 additions & 0 deletions app/linux/flutter/generated_plugin_registrant.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <handy_window/handy_window_plugin.h>
#include <open_file_linux/open_file_linux_plugin.h>
#include <screen_retriever_linux/screen_retriever_linux_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
#include <window_manager/window_manager_plugin.h>
#include <yaru_window_linux/yaru_window_linux_plugin.h>

Expand All @@ -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);
Expand Down
1 change: 1 addition & 0 deletions app/linux/flutter/generated_plugins.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down
Loading

0 comments on commit d8b9b1a

Please sign in to comment.