Browse Source

Merge pull request #422 from alonalbert/deluge-rss

Deluge Remote RSS
pull/424/head
Eric Kok 6 years ago committed by GitHub
parent
commit
48d2b1a63b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java
  2. 138
      app/src/main/java/org/transdroid/core/gui/remoterss/RemoteRssActivity.java
  3. 89
      app/src/main/java/org/transdroid/core/gui/remoterss/RemoteRssFragment.java
  4. 9
      app/src/main/java/org/transdroid/core/gui/remoterss/data/RemoteRssSupplier.java
  5. 2
      app/src/main/java/org/transdroid/daemon/Daemon.java
  6. 8
      app/src/main/java/org/transdroid/daemon/Deluge/DelugeCommon.java
  7. 85
      app/src/main/java/org/transdroid/daemon/Deluge/DelugeRemoteRssChannel.java
  8. 47
      app/src/main/java/org/transdroid/daemon/Deluge/DelugeRemoteRssItem.java
  9. 123
      app/src/main/java/org/transdroid/daemon/Deluge/DelugeRpcAdapter.java
  10. 14
      app/src/main/java/org/transdroid/daemon/Utorrent/UtorrentAdapter.java
  11. 23
      app/src/main/res/layout/fragment_remoterss.xml
  12. 1
      app/src/main/res/values/strings.xml

17
app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java

@ -84,8 +84,6 @@ import org.transdroid.core.gui.navigation.NavigationHelper;
import org.transdroid.core.gui.navigation.RefreshableActivity; import org.transdroid.core.gui.navigation.RefreshableActivity;
import org.transdroid.core.gui.navigation.StatusType; import org.transdroid.core.gui.navigation.StatusType;
import org.transdroid.core.gui.remoterss.RemoteRssActivity_; import org.transdroid.core.gui.remoterss.RemoteRssActivity_;
import org.transdroid.core.gui.remoterss.data.RemoteRssChannel;
import org.transdroid.core.gui.remoterss.data.RemoteRssSupplier;
import org.transdroid.core.gui.rss.RssfeedsActivity_; import org.transdroid.core.gui.rss.RssfeedsActivity_;
import org.transdroid.core.gui.search.BarcodeHelper; import org.transdroid.core.gui.search.BarcodeHelper;
import org.transdroid.core.gui.search.FilePickerHelper; import org.transdroid.core.gui.search.FilePickerHelper;
@ -854,18 +852,11 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
} }
@OptionsItem(R.id.action_remoterss) @OptionsItem(R.id.action_remoterss)
@Background
protected void openRemoteRss() { protected void openRemoteRss() {
ArrayList<RemoteRssChannel> rssFeedItems = ((RemoteRssSupplier) (currentConnection)).getRemoteRssChannels(); // Passing the items over as a feed can overload the Intent size limit and crash without a stack trace.
// Loading the items can take a while so we don't want to load them here and again in the RemoteRssActivity.
if (rssFeedItems.size() == 0) { RemoteRssActivity_.intent(this).start();
return;
}
// Passing the items over as a feed can overload the Intent size limit and crash without a stack trace!
RemoteRssActivity_.intent(this)
// .feeds(rssFeedItems)
.start()
;
} }
@OptionsItem(R.id.action_help) @OptionsItem(R.id.action_help)

138
app/src/main/java/org/transdroid/core/gui/remoterss/RemoteRssActivity.java

@ -26,23 +26,34 @@ import android.support.v7.widget.Toolbar;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ListView; import android.widget.ListView;
import com.nispok.snackbar.Snackbar;
import com.nispok.snackbar.SnackbarManager;
import com.nispok.snackbar.enums.SnackbarType;
import org.androidannotations.annotations.AfterViews; import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Background;
import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EActivity; import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.FragmentById; import org.androidannotations.annotations.FragmentById;
import org.androidannotations.annotations.InstanceState; import org.androidannotations.annotations.InstanceState;
import org.androidannotations.annotations.ItemClick; import org.androidannotations.annotations.ItemClick;
import org.androidannotations.annotations.NonConfigurationInstance;
import org.androidannotations.annotations.OptionsItem; import org.androidannotations.annotations.OptionsItem;
import org.androidannotations.annotations.UiThread;
import org.androidannotations.annotations.ViewById; import org.androidannotations.annotations.ViewById;
import org.transdroid.R; import org.transdroid.R;
import org.transdroid.core.app.settings.ApplicationSettings; import org.transdroid.core.app.settings.ApplicationSettings;
import org.transdroid.core.app.settings.ServerSetting; import org.transdroid.core.app.settings.ServerSetting;
import org.transdroid.core.app.settings.SystemSettings_; import org.transdroid.core.app.settings.SystemSettings_;
import org.transdroid.core.gui.lists.LocalTorrent;
import org.transdroid.core.gui.lists.SimpleListItemAdapter; import org.transdroid.core.gui.lists.SimpleListItemAdapter;
import org.transdroid.core.gui.log.Log;
import org.transdroid.core.gui.navigation.RefreshableActivity;
import org.transdroid.core.gui.remoterss.data.RemoteRssChannel; import org.transdroid.core.gui.remoterss.data.RemoteRssChannel;
import org.transdroid.core.gui.remoterss.data.RemoteRssItem; import org.transdroid.core.gui.remoterss.data.RemoteRssItem;
import org.transdroid.core.gui.remoterss.data.RemoteRssSupplier; import org.transdroid.core.gui.remoterss.data.RemoteRssSupplier;
import org.transdroid.core.service.ConnectivityHelper; import org.transdroid.core.service.ConnectivityHelper;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.IDaemonAdapter; import org.transdroid.daemon.IDaemonAdapter;
import java.util.ArrayList; import java.util.ArrayList;
@ -61,18 +72,22 @@ import java.util.List;
* @author Twig Nguyen * @author Twig Nguyen
*/ */
@EActivity(R.layout.activity_remoterss) @EActivity(R.layout.activity_remoterss)
public class RemoteRssActivity extends AppCompatActivity { public class RemoteRssActivity extends AppCompatActivity implements RefreshableActivity {
// @Extra @NonConfigurationInstance
@InstanceState
protected ArrayList<RemoteRssChannel> feeds; protected ArrayList<RemoteRssChannel> feeds;
@InstanceState @InstanceState
protected int selectedFilter;
@NonConfigurationInstance
protected ArrayList<RemoteRssItem> recentItems; protected ArrayList<RemoteRssItem> recentItems;
// Server connection // Server connection
@Bean @Bean
protected ApplicationSettings applicationSettings; protected ApplicationSettings applicationSettings;
@Bean @Bean
protected Log log;
@Bean
protected ConnectivityHelper connectivityHelper; protected ConnectivityHelper connectivityHelper;
private IDaemonAdapter currentConnection; private IDaemonAdapter currentConnection;
@ -110,13 +125,67 @@ public class RemoteRssActivity extends AppCompatActivity {
// Connect to the last used server // Connect to the last used server
ServerSetting lastUsed = applicationSettings.getLastUsedServer(); ServerSetting lastUsed = applicationSettings.getLastUsedServer();
currentConnection = lastUsed.createServerAdapter(connectivityHelper.getConnectedNetworkName(), this); currentConnection = lastUsed.createServerAdapter(connectivityHelper.getConnectedNetworkName(), this);
feeds = ((RemoteRssSupplier) (currentConnection)).getRemoteRssChannels();
// Fill in the filter list if (feeds != null) {
// Called from a configuration change. No need to load anything from server
showChannelFilters();
fragmentRemoteRss.updateRemoteItems(
selectedFilter == 0 ? recentItems : feeds.get(selectedFilter - 1).getItems(),
false /* allow android to restore scroll position */ );
} else {
loadFeeds();
}
}
@Background
protected void loadFeeds() {
try {
fragmentRemoteRss.setRefreshing(true);
feeds = ((RemoteRssSupplier) (currentConnection)).getRemoteRssChannels(log);
fragmentRemoteRss.setRefreshing(false);
} catch (DaemonException e) {
onCommunicationError(e);
}
recentItems = new ArrayList<>();
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MONTH, -1);
Date oneMonthAgo = calendar.getTime();
for (RemoteRssChannel feed : feeds) {
for (RemoteRssItem item : feed.getItems()) {
if (item.getTimestamp().after(oneMonthAgo)) {
recentItems.add(item);
}
}
}
// Sort by -newest
Collections.sort(recentItems, new Comparator<RemoteRssItem>() {
@Override
public int compare(RemoteRssItem lhs, RemoteRssItem rhs) {
return rhs.getTimestamp().compareTo(lhs.getTimestamp());
}
});
afterLoadFeeds();
}
@UiThread
protected void afterLoadFeeds() {
// We need to wrap these calls in a @UiThread rather than make them each a @UiThread themselves
// because they need to run sequentially in a configuration change scenario in order for Android
// to maintain scroll position on the fragment adapter.
showChannelFilters(); showChannelFilters();
onFeedSelected(selectedFilter);
}
// Show all items @UiThread
showRecentItems(); protected void onCommunicationError(DaemonException daemonException) {
//noinspection ThrowableResultOfMethodCallIgnored
log.i(this, daemonException.toString());
String error = getString(LocalTorrent.getResourceForDaemonException(daemonException));
SnackbarManager.show(Snackbar.with(this).text(error).colorResource(R.color.red).type(SnackbarType.MULTI_LINE));
} }
@ -139,36 +208,7 @@ public class RemoteRssActivity extends AppCompatActivity {
} }
} }
protected void showRecentItems() { private void showChannelFilters() {
if (recentItems == null) {
recentItems = new ArrayList<>();
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MONTH, -1);
Date oneMonthAgo = calendar.getTime();
for (RemoteRssChannel feed : feeds) {
for (RemoteRssItem item : feed.getItems()) {
if (item.getTimestamp().after(oneMonthAgo)) {
recentItems.add(item);
}
}
}
// Sort by -newest
Collections.sort(recentItems, new Comparator<RemoteRssItem>() {
@Override
public int compare(RemoteRssItem lhs, RemoteRssItem rhs) {
return rhs.getTimestamp().compareTo(lhs.getTimestamp());
}
});
}
fragmentRemoteRss.updateRemoteItems(recentItems);
RemoteRssChannel channel = (RemoteRssChannel) drawerList.getAdapter().getItem(0);
getSupportActionBar().setSubtitle(channel.getName());
}
protected void showChannelFilters() {
List<RemoteRssChannel> feedLabels = new ArrayList<>(feeds.size() +1); List<RemoteRssChannel> feedLabels = new ArrayList<>(feeds.size() +1);
feedLabels.add(new RemoteRssChannel() { feedLabels.add(new RemoteRssChannel() {
@Override @Override
@ -187,12 +227,8 @@ public class RemoteRssActivity extends AppCompatActivity {
@ItemClick(R.id.drawer_list) @ItemClick(R.id.drawer_list)
protected void onFeedSelected(int position) { protected void onFeedSelected(int position) {
if (position == 0) { selectedFilter = position;
showRecentItems(); fragmentRemoteRss.updateRemoteItems(position == 0 ? recentItems : feeds.get(position - 1).getItems(), true);
}
else {
fragmentRemoteRss.updateRemoteItems(feeds.get(position -1).getItems());
}
RemoteRssChannel channel = (RemoteRssChannel) drawerList.getAdapter().getItem(position); RemoteRssChannel channel = (RemoteRssChannel) drawerList.getAdapter().getItem(position);
getSupportActionBar().setSubtitle(channel.getName()); getSupportActionBar().setSubtitle(channel.getName());
@ -203,4 +239,18 @@ public class RemoteRssActivity extends AppCompatActivity {
public IDaemonAdapter getCurrentConnection() { public IDaemonAdapter getCurrentConnection() {
return currentConnection; return currentConnection;
} }
}
public RemoteRssChannel getChannel(String name) {
for (RemoteRssChannel feed : feeds) {
if (feed.getName().equals(name)) {
return feed;
}
}
return null;
}
@Override
public void refreshScreen() {
loadFeeds();
}
}

89
app/src/main/java/org/transdroid/core/gui/remoterss/RemoteRssFragment.java

@ -32,20 +32,16 @@ import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Background; import org.androidannotations.annotations.Background;
import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EFragment; import org.androidannotations.annotations.EFragment;
import org.androidannotations.annotations.InstanceState;
import org.androidannotations.annotations.ItemClick; import org.androidannotations.annotations.ItemClick;
import org.androidannotations.annotations.UiThread; import org.androidannotations.annotations.UiThread;
import org.androidannotations.annotations.ViewById; import org.androidannotations.annotations.ViewById;
import org.transdroid.R; import org.transdroid.R;
import org.transdroid.core.gui.lists.LocalTorrent; import org.transdroid.core.gui.lists.LocalTorrent;
import org.transdroid.core.gui.log.Log; import org.transdroid.core.gui.log.Log;
import org.transdroid.core.gui.navigation.RefreshableActivity;
import org.transdroid.core.gui.remoterss.data.RemoteRssItem; import org.transdroid.core.gui.remoterss.data.RemoteRssItem;
import org.transdroid.daemon.Daemon; import org.transdroid.core.gui.remoterss.data.RemoteRssSupplier;
import org.transdroid.daemon.IDaemonAdapter; import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.task.AddByMagnetUrlTask;
import org.transdroid.daemon.task.AddByUrlTask;
import org.transdroid.daemon.task.DaemonTaskFailureResult;
import org.transdroid.daemon.task.DaemonTaskResult;
import org.transdroid.daemon.task.DaemonTaskSuccessResult; import org.transdroid.daemon.task.DaemonTaskSuccessResult;
import java.util.ArrayList; import java.util.ArrayList;
@ -62,7 +58,6 @@ public class RemoteRssFragment extends Fragment {
protected Log log; protected Log log;
// Local data // Local data
@InstanceState
protected ArrayList<RemoteRssItem> remoteRssItems; protected ArrayList<RemoteRssItem> remoteRssItems;
// Views // Views
@ -75,7 +70,7 @@ public class RemoteRssFragment extends Fragment {
@ViewById @ViewById
protected ListView torrentsList; protected ListView torrentsList;
@ViewById @ViewById
protected TextView remoterssNoFilesMessage; protected TextView remoterssStatusMessage;
protected RemoteRssItemsAdapter adapter; protected RemoteRssItemsAdapter adapter;
@ -100,27 +95,45 @@ public class RemoteRssFragment extends Fragment {
torrentsList.setAdapter(adapter); torrentsList.setAdapter(adapter);
torrentsList.setFastScrollEnabled(true); torrentsList.setFastScrollEnabled(true);
// Restore the fragment state (on orientation changes et al.) // Allow pulls on the list view to refresh the torrents
if (remoteRssItems != null) { if (getActivity() != null && getActivity() instanceof RefreshableActivity) {
updateRemoteItems(remoteRssItems); swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
((RefreshableActivity) getActivity()).refreshScreen();
}
});
} }
} }
/** /**
* Updates the UI with a new list of RSS items. * Updates the UI with a new list of RSS items.
*/ */
public void updateRemoteItems(List<RemoteRssItem> remoteItems) { public void updateRemoteItems(List<RemoteRssItem> remoteItems, boolean scrollToTop) {
remoteRssItems = new ArrayList<>(remoteItems); remoteRssItems = new ArrayList<>(remoteItems);
adapter.updateItems(remoteRssItems); adapter.updateItems(remoteRssItems);
torrentsList.smoothScrollToPosition(0); if (scrollToTop) {
torrentsList.smoothScrollToPosition(0);
}
// Show/hide a nice message if there are no items to show // Show/hide a nice message if there are no items to show
remoterssNoFilesMessage.setVisibility(remoteRssItems.size() > 0 ? View.GONE : View.VISIBLE); if (remoteRssItems.size() > 0) {
remoterssStatusMessage.setVisibility(View.GONE);
}
else {
remoterssStatusMessage.setVisibility(View.VISIBLE);
remoterssStatusMessage.setText(R.string.remoterss_no_files);
}
swipeRefreshLayout.setRefreshing(false);
}
@UiThread
public void setRefreshing(boolean refreshing) {
swipeRefreshLayout.setRefreshing(refreshing);
} }
/** /**
* When the user clicks on an item, prepare to download it. * When the user clicks on an item, prepare to download it.
*/ */
@ItemClick(resName = "torrents_list") @ItemClick(resName = "torrents_list")
protected void detailsListClicked(int position) { protected void detailsListClicked(int position) {
RemoteRssItem item = (RemoteRssItem) adapter.getItem(position); RemoteRssItem item = (RemoteRssItem) adapter.getItem(position);
@ -129,33 +142,17 @@ public class RemoteRssFragment extends Fragment {
/** /**
* Download the item in a background thread and display success/fail accordingly. * Download the item in a background thread and display success/fail accordingly.
*/ */
@Background @Background
protected void downloadRemoteRssItem(RemoteRssItem item) { protected void downloadRemoteRssItem(RemoteRssItem item) {
RemoteRssActivity activity = (RemoteRssActivity) getActivity(); final RemoteRssActivity activity = (RemoteRssActivity) getActivity();
IDaemonAdapter currentConnection = activity.getCurrentConnection(); final RemoteRssSupplier supplier = (RemoteRssSupplier) activity.getCurrentConnection();
DaemonTaskResult result;
try {
if (item.isMagnetLink()) { supplier.downloadRemoteRssItem(log, item, activity.getChannel(item.getSourceName()));
// Check if it's supported onTaskSucceeded(null, getString(R.string.result_added, item.getTitle()));
if (!Daemon.supportsAddByMagnetUrl(currentConnection.getType())) { } catch (DaemonException e) {
onTaskFailed(getString(R.string.error_magnet_links_unsupported)); onTaskFailed(getString(LocalTorrent.getResourceForDaemonException(e)));
return;
}
AddByMagnetUrlTask addByMagnetUrlTask = AddByMagnetUrlTask.create(currentConnection, item.getLink());
result = addByMagnetUrlTask.execute(log);
}
else {
result = AddByUrlTask.create(currentConnection, item.getLink(), item.getTitle()).execute(log);
}
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_added, item.getTitle()));
} else if (result instanceof DaemonTaskFailureResult){
DaemonTaskFailureResult failure = ((DaemonTaskFailureResult) result);
String message = getString(LocalTorrent.getResourceForDaemonException(failure.getException()));
onTaskFailed(message);
} }
} }
@ -167,9 +164,9 @@ public class RemoteRssFragment extends Fragment {
@UiThread @UiThread
protected void onTaskFailed(String message) { protected void onTaskFailed(String message) {
SnackbarManager.show(Snackbar.with(getActivity()) SnackbarManager.show(Snackbar.with(getActivity())
.text(message) .text(message)
.colorResource(R.color.red) .colorResource(R.color.red)
.type(SnackbarType.MULTI_LINE) .type(SnackbarType.MULTI_LINE)
); );
} }
} }

9
app/src/main/java/org/transdroid/core/gui/remoterss/data/RemoteRssSupplier.java

@ -1,5 +1,8 @@
package org.transdroid.core.gui.remoterss.data; package org.transdroid.core.gui.remoterss.data;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.DaemonException;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
@ -8,5 +11,7 @@ import java.util.ArrayList;
* @author Twig * @author Twig
*/ */
public interface RemoteRssSupplier { public interface RemoteRssSupplier {
ArrayList<RemoteRssChannel> getRemoteRssChannels(); ArrayList<RemoteRssChannel> getRemoteRssChannels(Log log) throws DaemonException;
}
void downloadRemoteRssItem(Log log, RemoteRssItem rssItem, RemoteRssChannel rssChannel) throws DaemonException;
}

2
app/src/main/java/org/transdroid/daemon/Daemon.java

@ -401,6 +401,6 @@ public enum Daemon {
} }
public static boolean supportsRemoteRssManagement(Daemon type) { public static boolean supportsRemoteRssManagement(Daemon type) {
return type == uTorrent; return type == uTorrent || type == DelugeRpc;
} }
} }

8
app/src/main/java/org/transdroid/daemon/Deluge/DelugeCommon.java

@ -29,6 +29,7 @@ import org.transdroid.daemon.TorrentStatus;
class DelugeCommon { class DelugeCommon {
static final String RPC_DETAILS = "files"; static final String RPC_DETAILS = "files";
static final String RPC_DOWNLOAD_LOCATION = "download_location";
static final String RPC_DOWNLOADEDEVER = "total_done"; static final String RPC_DOWNLOADEDEVER = "total_done";
static final String RPC_ETA = "eta"; static final String RPC_ETA = "eta";
static final String RPC_FILE = "file"; static final String RPC_FILE = "file";
@ -37,6 +38,7 @@ class DelugeCommon {
static final String RPC_HASH = "hash"; static final String RPC_HASH = "hash";
static final String RPC_INDEX = "index"; static final String RPC_INDEX = "index";
static final String RPC_LABEL = "label"; static final String RPC_LABEL = "label";
static final String RPC_KEY = "key";
static final String RPC_MAXDOWNLOAD = "max_download_speed"; static final String RPC_MAXDOWNLOAD = "max_download_speed";
static final String RPC_MAXUPLOAD = "max_upload_speed"; static final String RPC_MAXUPLOAD = "max_upload_speed";
static final String RPC_MESSAGE = "message"; static final String RPC_MESSAGE = "message";
@ -64,6 +66,9 @@ class DelugeCommon {
static final String RPC_METHOD_SETTRACKERS = "core.set_torrent_trackers"; static final String RPC_METHOD_SETTRACKERS = "core.set_torrent_trackers";
static final String RPC_METHOD_SET_TORRENT_OPTIONS = "core.set_torrent_options"; static final String RPC_METHOD_SET_TORRENT_OPTIONS = "core.set_torrent_options";
static final String RPC_METHOD_STATUS = "core.get_torrent_status"; static final String RPC_METHOD_STATUS = "core.get_torrent_status";
static final String RPC_METHOD_GET_RSS_CONFIG = "yarss2.get_config";
static final String RPC_MOVE_COMPLETED = "move_completed";
static final String RPC_MOVE_COMPLETED_PATH = "move_completed_path";
static final String RPC_NAME = "name"; static final String RPC_NAME = "name";
static final String RPC_NUMPEERS = "num_peers"; static final String RPC_NUMPEERS = "num_peers";
static final String RPC_NUMSEEDS = "num_seeds"; static final String RPC_NUMSEEDS = "num_seeds";
@ -73,10 +78,13 @@ class DelugeCommon {
static final String RPC_RATEDOWNLOAD = "download_payload_rate"; static final String RPC_RATEDOWNLOAD = "download_payload_rate";
static final String RPC_RATEUPLOAD = "upload_payload_rate"; static final String RPC_RATEUPLOAD = "upload_payload_rate";
static final String RPC_RESULT = "result"; static final String RPC_RESULT = "result";
static final String RPC_RSSFEED_KEY = "rssfeed_key";
static final String RPC_RSSFEEDS = "rssfeeds";
static final String RPC_SAVEPATH = "save_path"; static final String RPC_SAVEPATH = "save_path";
static final String RPC_SESSION_ID = "_session_id"; static final String RPC_SESSION_ID = "_session_id";
static final String RPC_SIZE = "size"; static final String RPC_SIZE = "size";
static final String RPC_STATUS = "state"; static final String RPC_STATUS = "state";
static final String RPC_SUBSCRIPTIONS = "subscriptions";
static final String RPC_TIMEADDED = "time_added"; static final String RPC_TIMEADDED = "time_added";
static final String RPC_TORRENTS = "torrents"; static final String RPC_TORRENTS = "torrents";
static final String RPC_TOTALPEERS = "total_peers"; static final String RPC_TOTALPEERS = "total_peers";

85
app/src/main/java/org/transdroid/daemon/Deluge/DelugeRemoteRssChannel.java

@ -0,0 +1,85 @@
package org.transdroid.daemon.Deluge;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.List;
import org.transdroid.core.gui.remoterss.data.RemoteRssChannel;
import org.transdroid.core.gui.remoterss.data.RemoteRssItem;
/**
* Deluge implementation of RemoteRssChannel.
*
* @author alonalbert
*/
class DelugeRemoteRssChannel extends RemoteRssChannel {
private final String label;
private final String downloadLocation;
private final String moveCompleted;
DelugeRemoteRssChannel(
int id,
String name,
String link,
long lastUpdated,
String label, String downloadLocation, String moveCompleted, List<RemoteRssItem> items) {
this.label = label;
this.downloadLocation = downloadLocation;
this.moveCompleted = moveCompleted;
this.id = id;
this.name = name;
this.link = link;
this.lastUpdated = lastUpdated;
this.items = items;
}
private DelugeRemoteRssChannel(Parcel in) {
id = in.readInt();
name = in.readString();
link = in.readString();
lastUpdated = in.readLong();
label = in.readString();
downloadLocation = in.readString();
moveCompleted = in.readString();
items = new ArrayList<>();
in.readList(items, DelugeRemoteRssItem.class.getClassLoader());
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
dest.writeString(link);
dest.writeLong(lastUpdated);
dest.writeString(label);
dest.writeString(downloadLocation);
dest.writeString(moveCompleted);
dest.writeList(items);
}
public String getLabel() {
return label;
}
public String getDownloadLocation() {
return downloadLocation;
}
public String getMoveCompleted() {
return moveCompleted;
}
public static final Parcelable.Creator<DelugeRemoteRssChannel> CREATOR = new Parcelable.Creator<DelugeRemoteRssChannel>() {
public DelugeRemoteRssChannel createFromParcel(Parcel in) {
return new DelugeRemoteRssChannel(in);
}
public DelugeRemoteRssChannel[] newArray(int size) {
return new DelugeRemoteRssChannel[size];
}
};
}

47
app/src/main/java/org/transdroid/daemon/Deluge/DelugeRemoteRssItem.java

@ -0,0 +1,47 @@
package org.transdroid.daemon.Deluge;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Date;
import org.transdroid.core.gui.remoterss.data.RemoteRssItem;
/**
* Deluge implementation of RemoteRssItem.
*
* @author alonalbert
*/
class DelugeRemoteRssItem extends RemoteRssItem {
DelugeRemoteRssItem(String title, String link, String sourceName, Date timestamp) {
this.title = title;
this.link = link;
this.sourceName = sourceName;
this.timestamp = timestamp;
}
private DelugeRemoteRssItem(Parcel in) {
title = in.readString();
link = in.readString();
sourceName = in.readString();
timestamp = (Date) in.readSerializable();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(title);
dest.writeString(link);
dest.writeString(sourceName);
dest.writeSerializable(timestamp);
}
public static final Parcelable.Creator<DelugeRemoteRssItem> CREATOR = new Parcelable.Creator<DelugeRemoteRssItem>() {
public DelugeRemoteRssItem createFromParcel(Parcel in) {
return new DelugeRemoteRssItem(in);
}
public DelugeRemoteRssItem[] newArray(int size) {
return new DelugeRemoteRssItem[size];
}
};
}

123
app/src/main/java/org/transdroid/daemon/Deluge/DelugeRpcAdapter.java

@ -21,6 +21,12 @@ import android.support.annotation.NonNull;
import org.base64.android.Base64; import org.base64.android.Base64;
import org.transdroid.core.gui.log.Log; import org.transdroid.core.gui.log.Log;
import org.transdroid.core.gui.remoterss.data.RemoteRssChannel;
import org.transdroid.core.gui.remoterss.data.RemoteRssItem;
import org.transdroid.core.gui.remoterss.data.RemoteRssSupplier;
import org.transdroid.core.rssparser.Channel;
import org.transdroid.core.rssparser.Item;
import org.transdroid.core.rssparser.RssParser;
import org.transdroid.daemon.Daemon; import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException; import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType; import org.transdroid.daemon.DaemonException.ExceptionType;
@ -51,6 +57,7 @@ import org.transdroid.daemon.task.SetFilePriorityTask;
import org.transdroid.daemon.task.SetLabelTask; import org.transdroid.daemon.task.SetLabelTask;
import org.transdroid.daemon.task.SetTrackersTask; import org.transdroid.daemon.task.SetTrackersTask;
import org.transdroid.daemon.task.SetTransferRatesTask; import org.transdroid.daemon.task.SetTransferRatesTask;
import org.xml.sax.SAXException;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -70,9 +77,12 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_DETAILS; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_DETAILS;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_DETAILS_FIELDS_ARRAY; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_DETAILS_FIELDS_ARRAY;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_DOWNLOADEDEVER; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_DOWNLOADEDEVER;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_DOWNLOAD_LOCATION;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_ETA; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_ETA;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_FIELDS_ARRAY; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_FIELDS_ARRAY;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_FILEPRIORITIES; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_FILEPRIORITIES;
@ -80,6 +90,7 @@ import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_FILEPROGRESS;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_FILE_FIELDS_ARRAY; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_FILE_FIELDS_ARRAY;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_HASH; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_HASH;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_INDEX; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_INDEX;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_KEY;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_LABEL; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_LABEL;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_MAXDOWNLOAD; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_MAXDOWNLOAD;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_MAXUPLOAD; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_MAXUPLOAD;
@ -90,6 +101,7 @@ import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_ADD_MAGNET;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_FORCERECHECK; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_FORCERECHECK;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_GET_LABELS; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_GET_LABELS;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_GET_METHOD_LIST; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_GET_METHOD_LIST;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_GET_RSS_CONFIG;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_GET_TORRENTS_STATUS; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_GET_TORRENTS_STATUS;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_INFO; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_INFO;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_MOVESTORAGE; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_MOVESTORAGE;
@ -103,6 +115,8 @@ import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_SETLABEL;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_SETTRACKERS; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_SETTRACKERS;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_SET_TORRENT_OPTIONS; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_SET_TORRENT_OPTIONS;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_STATUS; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_METHOD_STATUS;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_MOVE_COMPLETED;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_MOVE_COMPLETED_PATH;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_NAME; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_NAME;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_NUMPEERS; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_NUMPEERS;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_NUMSEEDS; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_NUMSEEDS;
@ -110,9 +124,12 @@ import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_PARTDONE;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_PATH; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_PATH;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_RATEDOWNLOAD; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_RATEDOWNLOAD;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_RATEUPLOAD; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_RATEUPLOAD;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_RSSFEEDS;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_RSSFEED_KEY;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_SAVEPATH; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_SAVEPATH;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_SIZE; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_SIZE;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_STATUS; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_STATUS;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_SUBSCRIPTIONS;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_TIER; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_TIER;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_TIMEADDED; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_TIMEADDED;
import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_TOTALPEERS; import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_TOTALPEERS;
@ -127,7 +144,7 @@ import static org.transdroid.daemon.Deluge.DelugeCommon.RPC_URL;
* The daemon adapter from the Deluge torrent client using deluged API directly. * The daemon adapter from the Deluge torrent client using deluged API directly.
* @author alon.albert * @author alon.albert
*/ */
public class DelugeRpcAdapter implements IDaemonAdapter { public class DelugeRpcAdapter implements IDaemonAdapter, RemoteRssSupplier {
public static final int DEFAULT_PORT = 58846; public static final int DEFAULT_PORT = 58846;
@ -200,6 +217,93 @@ public class DelugeRpcAdapter implements IDaemonAdapter {
return settings; return settings;
} }
@Override
public ArrayList<RemoteRssChannel> getRemoteRssChannels(Log log) throws DaemonException {
final long now = System.currentTimeMillis();
final DelugeRpcClient client = new DelugeRpcClient();
try {
client.connect(settings);
if (!hasMethod(client, RPC_METHOD_GET_RSS_CONFIG)) {
throw new DaemonException(ExceptionType.MethodUnsupported, "YaRRS2 plugin not installed");
}
//noinspection unchecked
final Map<String, Object> rssConfig = (Map<String, Object>) client.sendRequest(RPC_METHOD_GET_RSS_CONFIG);
//noinspection unchecked
final Map<String, Map<String, Object>> rssFeeds = (Map<String, Map<String, Object>>) rssConfig.get(RPC_RSSFEEDS);
final Map<Object, String> feedUrlMap = new HashMap<>();
final Map<Object, List<Item>> feedItemMap = new HashMap<>();
for (Map<String, Object> feed : rssFeeds.values()) {
final String feedUrl = (String) feed.get(RPC_URL);
final Object key = feed.get(RPC_KEY);
feedUrlMap.put(key, feedUrl);
final List<Item> items = getRssFeedItems(feedUrl, log);
feedItemMap.put(key, items);
}
//noinspection unchecked
final Map<String, Map<String, Object>> subscriptions = (Map<String, Map<String, Object>>) rssConfig.get(RPC_SUBSCRIPTIONS);
final ArrayList<RemoteRssChannel> channels = new ArrayList<>();
for (Map<String, Object> subscription : subscriptions.values()) {
final Integer key = Integer.valueOf(subscription.get(RPC_KEY).toString());
final String name = (String) subscription.get(RPC_NAME);
final String label = (String) subscription.get(RPC_LABEL);
final String downloadLocation = (String) subscription.get(RPC_DOWNLOAD_LOCATION);
final String moveCompleted = (String) subscription.get(RPC_MOVE_COMPLETED);
final Object feedKey = subscription.get(RPC_RSSFEED_KEY);
final String feedUrl = feedUrlMap.get(feedKey);
final List<RemoteRssItem> items = new ArrayList<>();
for (Item item : feedItemMap.get(feedKey)) {
items.add(new DelugeRemoteRssItem(item.getTitle(), item.getLink(), name, item.getPubdate()));
}
channels.add(new DelugeRemoteRssChannel(key, name, feedUrl, now, label, downloadLocation, moveCompleted, items));
}
return channels;
} finally {
client.close();
android.util.Log.i("Alon", String.format("getRemoteRssChannels: %dms", System.currentTimeMillis() - now));
}
}
@Override
public void downloadRemoteRssItem(Log log, RemoteRssItem rssItem, RemoteRssChannel rssChannel) throws DaemonException {
final DelugeRemoteRssItem item = (DelugeRemoteRssItem) rssItem;
final DelugeRemoteRssChannel channel = (DelugeRemoteRssChannel) rssChannel;
final Map<String, Object> options = new HashMap<>();
final String label;
if (channel != null) {
final String downloadLocation = channel.getDownloadLocation();
if (downloadLocation != null) {
options.put(RPC_DOWNLOAD_LOCATION, downloadLocation);
}
final String moveCompleted = channel.getMoveCompleted();
if (moveCompleted != null) {
options.put(RPC_MOVE_COMPLETED, true);
options.put(RPC_MOVE_COMPLETED_PATH, moveCompleted);
}
label = channel.getLabel();
} else {
label = null;
}
final DelugeRpcClient client = new DelugeRpcClient();
try {
client.connect(settings);
final String torrentId = (String) client
.sendRequest(item.isMagnetLink() ? RPC_METHOD_ADD_MAGNET : RPC_METHOD_ADD, item.getLink(), options);
if (label != null && hasMethod(client, RPC_METHOD_SETLABEL)) {
client.sendRequest(RPC_METHOD_SETLABEL, torrentId, label);
}
} finally {
client.close();
}
}
@NonNull @NonNull
private RetrieveTaskSuccessResult doRetrieve(DelugeRpcClient client, RetrieveTask task) throws DaemonException { private RetrieveTaskSuccessResult doRetrieve(DelugeRpcClient client, RetrieveTask task) throws DaemonException {
// Get torrents // Get torrents
@ -498,6 +602,23 @@ public class DelugeRpcAdapter implements IDaemonAdapter {
return new String[]{task.getTargetTorrent().getUniqueID()}; return new String[]{task.getTargetTorrent().getUniqueID()};
} }
@NonNull
private List<Item> getRssFeedItems(String feedUrl, Log log) {
final RssParser rssParser = new RssParser(feedUrl, null, null);
try {
rssParser.parse();
final Channel channel = rssParser.getChannel();
return channel.getItems();
} catch (ParserConfigurationException e) {
log.e(DelugeRpcAdapter.this, "Failed to parse RSS feed.");
} catch (SAXException e) {
log.e(DelugeRpcAdapter.this, "Failed to parse RSS feed.");
} catch (IOException e) {
log.e(DelugeRpcAdapter.this, "Failed to load RSS feed.");
}
return new ArrayList<>();
}
private boolean hasMethod(DelugeRpcClient client, String method) throws DaemonException { private boolean hasMethod(DelugeRpcClient client, String method) throws DaemonException {
//noinspection unchecked //noinspection unchecked
final List<String> methods = (List<String>) client.sendRequest(RPC_METHOD_GET_METHOD_LIST); final List<String> methods = (List<String>) client.sendRequest(RPC_METHOD_GET_METHOD_LIST);

14
app/src/main/java/org/transdroid/daemon/Utorrent/UtorrentAdapter.java

@ -30,6 +30,7 @@ import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.transdroid.core.gui.log.Log; import org.transdroid.core.gui.log.Log;
import org.transdroid.core.gui.remoterss.data.RemoteRssChannel; import org.transdroid.core.gui.remoterss.data.RemoteRssChannel;
import org.transdroid.core.gui.remoterss.data.RemoteRssItem;
import org.transdroid.core.gui.remoterss.data.RemoteRssSupplier; import org.transdroid.core.gui.remoterss.data.RemoteRssSupplier;
import org.transdroid.daemon.Daemon; import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException; import org.transdroid.daemon.DaemonException;
@ -657,7 +658,18 @@ public class UtorrentAdapter implements IDaemonAdapter, RemoteRssSupplier {
return this.settings; return this.settings;
} }
public ArrayList<RemoteRssChannel> getRemoteRssChannels() { public ArrayList<RemoteRssChannel> getRemoteRssChannels(Log log) {
return remoteRssChannels; return remoteRssChannels;
} }
@Override
public void downloadRemoteRssItem(Log log, RemoteRssItem rssItem, RemoteRssChannel rssChannel) throws DaemonException {
final String link = rssItem.getLink();
try {
makeUtorrentRequest(log, "&action=add-url&s=" + URLEncoder.encode(link, "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new DaemonException(ExceptionType.ParsingFailed, "Invalid URL: " + link);
}
}
} }

23
app/src/main/res/layout/fragment_remoterss.xml

@ -23,17 +23,20 @@
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:text="@string/remoterss_no_files" android:text="@string/remoterss_loading"
android:gravity="center" android:gravity="center"
android:id="@+id/remoterss_no_files_message"/> android:id="@+id/remoterss_status_message"/>
<ListView <android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/torrents_list" android:id="@+id/swipe_refresh_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:choiceMode="multipleChoiceModal" <ListView
android:clipToPadding="false" android:id="@+id/torrents_list"
tools:listitem="@layout/list_item_remoterssitem" android:layout_width="match_parent"
tools:visibility="visible"/> android:layout_height="match_parent"
android:clipToPadding="false"
tools:listitem="@layout/list_item_remoterssitem"
tools:visibility="visible"/>
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout> </FrameLayout>

1
app/src/main/res/values/strings.xml

@ -214,6 +214,7 @@
<string name="remoterss_filter_allrecent">(All recent)</string> <string name="remoterss_filter_allrecent">(All recent)</string>
<string name="remoterss_no_files">No torrent files found.\n\nAre your RSS feeds configured correctly?</string> <string name="remoterss_no_files">No torrent files found.\n\nAre your RSS feeds configured correctly?</string>
<string name="remoterss_loading">Loading RSS feeds…</string>
<string name="widget_loading">Loading&#8230;</string> <string name="widget_loading">Loading&#8230;</string>
<string name="widget_opentransdroid">Open Transdroid</string> <string name="widget_opentransdroid">Open Transdroid</string>

Loading…
Cancel
Save