Browse Source

Add Remote RSS Support to Deluge RPC Adapter

Also:
* Make Remote RSS activity support "Swipe To Refresh"
* Preserve scroll position on orientation changes.
pull/422/head
Alon Albert 7 years ago
parent
commit
e2e70d05c3
  1. 141
      app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java
  2. 124
      app/src/main/java/org/transdroid/core/gui/remoterss/RemoteRssActivity.java
  3. 75
      app/src/main/java/org/transdroid/core/gui/remoterss/RemoteRssFragment.java
  4. 7
      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. 40
      app/src/main/java/org/transdroid/daemon/Utorrent/UtorrentAdapter.java
  11. 11
      app/src/main/res/layout/fragment_remoterss.xml
  12. 1
      app/src/main/res/values/strings.xml

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

@ -16,31 +16,17 @@
*/ */
package org.transdroid.core.gui; package org.transdroid.core.gui;
import android.annotation.TargetApi; import java.io.File;
import android.app.SearchManager; import java.io.FileNotFoundException;
import android.content.ContentResolver; import java.io.FileOutputStream;
import android.content.Intent; import java.io.IOException;
import android.net.Uri; import java.io.InputStream;
import android.os.AsyncTask; import java.io.UnsupportedEncodingException;
import android.os.Build; import java.net.URLDecoder;
import android.os.Bundle; import java.util.ArrayList;
import android.support.annotation.NonNull; import java.util.List;
import android.support.v4.view.MenuItemCompat; import java.util.Map;
import android.support.v4.widget.DrawerLayout; import java.util.Map.Entry;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.ActionMenuView;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import com.getbase.floatingactionbutton.FloatingActionButton; import com.getbase.floatingactionbutton.FloatingActionButton;
import com.getbase.floatingactionbutton.FloatingActionsMenu; import com.getbase.floatingactionbutton.FloatingActionsMenu;
@ -84,8 +70,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;
@ -130,17 +114,31 @@ import org.transdroid.daemon.task.StartTask;
import org.transdroid.daemon.task.StopTask; import org.transdroid.daemon.task.StopTask;
import org.transdroid.daemon.util.HttpHelper; import org.transdroid.daemon.util.HttpHelper;
import java.io.File; import android.annotation.TargetApi;
import java.io.FileNotFoundException; import android.app.SearchManager;
import java.io.FileOutputStream; import android.content.ContentResolver;
import java.io.IOException; import android.content.Intent;
import java.io.InputStream; import android.net.Uri;
import java.io.UnsupportedEncodingException; import android.os.AsyncTask;
import java.net.URLDecoder; import android.os.Build;
import java.util.ArrayList; import android.os.Bundle;
import java.util.List; import android.support.annotation.NonNull;
import java.util.Map; import android.support.v4.view.MenuItemCompat;
import java.util.Map.Entry; import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.ActionMenuView;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
/** /**
* Main activity that holds the fragment that shows the torrents list, presents a way to filter the list (via an action bar spinner or list side list) * Main activity that holds the fragment that shows the torrents list, presents a way to filter the list (via an action bar spinner or list side list)
@ -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)
@ -964,7 +955,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
if (result instanceof RetrieveTaskSuccessResult) { if (result instanceof RetrieveTaskSuccessResult) {
onTorrentsRetrieved(((RetrieveTaskSuccessResult) result).getTorrents(), ((RetrieveTaskSuccessResult) result).getLabels()); onTorrentsRetrieved(((RetrieveTaskSuccessResult) result).getTorrents(), ((RetrieveTaskSuccessResult) result).getLabels());
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, true); onCommunicationError(((DaemonTaskFailureResult) result).getException(), true);
} }
} }
@ -982,7 +973,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
if (result instanceof GetTorrentDetailsTaskSuccessResult) { if (result instanceof GetTorrentDetailsTaskSuccessResult) {
onTorrentDetailsRetrieved(torrent, ((GetTorrentDetailsTaskSuccessResult) result).getTorrentDetails()); onTorrentDetailsRetrieved(torrent, ((GetTorrentDetailsTaskSuccessResult) result).getTorrentDetails());
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1000,7 +991,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
if (result instanceof GetFileListTaskSuccessResult) { if (result instanceof GetFileListTaskSuccessResult) {
onTorrentFilesRetrieved(torrent, ((GetFileListTaskSuccessResult) result).getFiles()); onTorrentFilesRetrieved(torrent, ((GetFileListTaskSuccessResult) result).getFiles());
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1015,7 +1006,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
if (result instanceof GetStatsTaskSuccessResult) { if (result instanceof GetStatsTaskSuccessResult) {
onTurtleModeRetrieved(((GetStatsTaskSuccessResult) result).isAlternativeModeEnabled()); onTurtleModeRetrieved(((GetStatsTaskSuccessResult) result).isAlternativeModeEnabled());
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1031,7 +1022,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
// Success; no need to retrieve it again - just update the visual indicator // Success; no need to retrieve it again - just update the visual indicator
onTurtleModeRetrieved(enable); onTurtleModeRetrieved(enable);
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1042,7 +1033,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_added, title)); onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_added, title));
refreshTorrents(); refreshTorrents();
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1056,16 +1047,16 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
// Ignore: UTF-8 is always available on Android devices // Ignore: UTF-8 is always available on Android devices
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// Illegal character or escape sequence; fail task to show error // Illegal character or escape sequence; fail task to show error
onCommunicationError(new DaemonTaskFailureResult(AddByMagnetUrlTask.create(currentConnection, url), onCommunicationError(new DaemonException(DaemonException.ExceptionType.FileAccessError,
new DaemonException(DaemonException.ExceptionType.FileAccessError, "Invalid characters in magnet link")), false); "Invalid characters in magnet link"), false);
return; return;
} }
AddByMagnetUrlTask addByMagnetUrlTask = AddByMagnetUrlTask.create(currentConnection, url); AddByMagnetUrlTask addByMagnetUrlTask = AddByMagnetUrlTask.create(currentConnection, url);
if (!Daemon.supportsAddByMagnetUrl(currentConnection.getType())) { if (!Daemon.supportsAddByMagnetUrl(currentConnection.getType())) {
// No support for magnet links: forcefully let the task fail to report the error // No support for magnet links: forcefully let the task fail to report the error
onCommunicationError(new DaemonTaskFailureResult(addByMagnetUrlTask, new DaemonException(DaemonException.ExceptionType.MethodUnsupported, onCommunicationError(new DaemonException(DaemonException.ExceptionType.MethodUnsupported,
currentConnection.getType().name() + " does not support magnet links")), false); currentConnection.getType().name() + " does not support magnet links"), false);
return; return;
} }
@ -1074,7 +1065,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_added, title)); onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_added, title));
refreshTorrents(); refreshTorrents();
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1092,7 +1083,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_added, title)); onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_added, title));
refreshTorrents(); refreshTorrents();
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1201,7 +1192,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
if (result instanceof DaemonTaskSuccessResult) { if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_resumed, torrent.getName())); onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_resumed, torrent.getName()));
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1213,7 +1204,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
if (result instanceof DaemonTaskSuccessResult) { if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_paused, torrent.getName())); onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_paused, torrent.getName()));
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1225,7 +1216,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
if (result instanceof DaemonTaskSuccessResult) { if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_started, torrent.getName())); onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_started, torrent.getName()));
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1237,7 +1228,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
if (result instanceof DaemonTaskSuccessResult) { if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_stopped, torrent.getName())); onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_stopped, torrent.getName()));
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1249,7 +1240,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
onTaskSucceeded((DaemonTaskSuccessResult) result, onTaskSucceeded((DaemonTaskSuccessResult) result,
getString(withData ? R.string.result_removed_with_data : R.string.result_removed, torrent.getName())); getString(withData ? R.string.result_removed_with_data : R.string.result_removed, torrent.getName()));
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1262,7 +1253,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
onTaskSucceeded((DaemonTaskSuccessResult) result, onTaskSucceeded((DaemonTaskSuccessResult) result,
newLabel == null ? getString(R.string.result_labelremoved) : getString(R.string.result_labelset, newLabel)); newLabel == null ? getString(R.string.result_labelremoved) : getString(R.string.result_labelset, newLabel));
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1274,7 +1265,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
if (result instanceof DaemonTaskSuccessResult) { if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_recheckedstarted, torrent.getName())); onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_recheckedstarted, torrent.getName()));
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1285,7 +1276,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
if (result instanceof DaemonTaskSuccessResult) { if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_trackersupdated)); onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_trackersupdated));
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1296,7 +1287,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
if (result instanceof DaemonTaskSuccessResult) { if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_locationset, newLocation)); onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_locationset, newLocation));
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1307,7 +1298,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
if (result instanceof DaemonTaskSuccessResult) { if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_priotitiesset)); onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_priotitiesset));
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1317,7 +1308,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
if (result instanceof DaemonTaskSuccessResult) { if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_maxspeedsset)); onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_maxspeedsset));
} else { } else {
onCommunicationError((DaemonTaskFailureResult) result, false); onCommunicationError(((DaemonTaskFailureResult) result).getException(), false);
} }
} }
@ -1329,10 +1320,10 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE
} }
@UiThread @UiThread
protected void onCommunicationError(DaemonTaskFailureResult result, boolean isCritical) { protected void onCommunicationError(DaemonException daemonException, boolean isCritical) {
//noinspection ThrowableResultOfMethodCallIgnored //noinspection ThrowableResultOfMethodCallIgnored
log.i(this, result.getException().toString()); log.i(this, daemonException.toString());
String error = getString(LocalTorrent.getResourceForDaemonException(result.getException())); String error = getString(LocalTorrent.getResourceForDaemonException(daemonException));
SnackbarManager.show(Snackbar.with(this).text(error).colorResource(R.color.red).type(SnackbarType.MULTI_LINE)); SnackbarManager.show(Snackbar.with(this).text(error).colorResource(R.color.red).type(SnackbarType.MULTI_LINE));
fragmentTorrents.updateIsLoading(false); fragmentTorrents.updateIsLoading(false);
if (isCritical) { if (isCritical) {

124
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,37 +125,29 @@ 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(); showChannelFilters();
fragmentRemoteRss.updateRemoteItems(
// Show all items selectedFilter == 0 ? recentItems : feeds.get(selectedFilter - 1).getItems(),
showRecentItems(); false /* allow android to restore scroll position */ );
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@OptionsItem(android.R.id.home)
protected void navigateUp() {
if (drawerLayout.isDrawerOpen(drawerContainer)) {
drawerLayout.closeDrawers();
} else { } else {
drawerLayout.openDrawer(drawerContainer); loadFeeds();
}
} }
@Override
public void onBackPressed() {
if (drawerLayout.isDrawerOpen(drawerContainer)) {
drawerLayout.closeDrawers();
} else {
finish();
} }
@Background
protected void loadFeeds() {
try {
fragmentRemoteRss.setRefreshing(true);
feeds = ((RemoteRssSupplier) (currentConnection)).getRemoteRssChannels(log);
fragmentRemoteRss.setRefreshing(false);
} catch (DaemonException e) {
onCommunicationError(e);
} }
protected void showRecentItems() {
if (recentItems == null) {
recentItems = new ArrayList<>(); recentItems = new ArrayList<>();
Calendar calendar = Calendar.getInstance(); Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MONTH, -1); calendar.add(Calendar.MONTH, -1);
@ -153,7 +160,6 @@ public class RemoteRssActivity extends AppCompatActivity {
} }
} }
} }
// Sort by -newest // Sort by -newest
Collections.sort(recentItems, new Comparator<RemoteRssItem>() { Collections.sort(recentItems, new Comparator<RemoteRssItem>() {
@Override @Override
@ -161,14 +167,48 @@ public class RemoteRssActivity extends AppCompatActivity {
return rhs.getTimestamp().compareTo(lhs.getTimestamp()); return rhs.getTimestamp().compareTo(lhs.getTimestamp());
} }
}); });
afterLoadFeeds();
} }
fragmentRemoteRss.updateRemoteItems(recentItems); @UiThread
RemoteRssChannel channel = (RemoteRssChannel) drawerList.getAdapter().getItem(0); protected void afterLoadFeeds() {
getSupportActionBar().setSubtitle(channel.getName()); // 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();
onFeedSelected(selectedFilter);
}
@UiThread
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));
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@OptionsItem(android.R.id.home)
protected void navigateUp() {
if (drawerLayout.isDrawerOpen(drawerContainer)) {
drawerLayout.closeDrawers();
} else {
drawerLayout.openDrawer(drawerContainer);
}
} }
protected void showChannelFilters() { @Override
public void onBackPressed() {
if (drawerLayout.isDrawerOpen(drawerContainer)) {
drawerLayout.closeDrawers();
} else {
finish();
}
}
private 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();
}
} }

75
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,22 +95,40 @@ 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);
if (scrollToTop) {
torrentsList.smoothScrollToPosition(0); 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);
} }
/** /**
@ -132,30 +145,14 @@ public class RemoteRssFragment extends Fragment {
*/ */
@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);
} }
} }

7
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);

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

@ -17,6 +17,19 @@
*/ */
package org.transdroid.daemon.Utorrent; package org.transdroid.daemon.Utorrent;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import com.android.internalcopy.http.multipart.FilePart; import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity; import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part; import com.android.internalcopy.http.multipart.Part;
@ -30,6 +43,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;
@ -64,19 +78,6 @@ import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.task.StartTask; import org.transdroid.daemon.task.StartTask;
import org.transdroid.daemon.util.HttpHelper; import org.transdroid.daemon.util.HttpHelper;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
/** /**
* An adapter that allows for easy access to uTorrent torrent data. Communication is handled via authenticated JSON-RPC * An adapter that allows for easy access to uTorrent torrent data. Communication is handled via authenticated JSON-RPC
* HTTP GET requests and responses. * HTTP GET requests and responses.
@ -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);
}
}
} }

11
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"/>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView <ListView
android:id="@+id/torrents_list" android:id="@+id/torrents_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:choiceMode="multipleChoiceModal"
android:clipToPadding="false" android:clipToPadding="false"
tools:listitem="@layout/list_item_remoterssitem" tools:listitem="@layout/list_item_remoterssitem"
tools:visibility="visible"/> 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