From e2e70d05c34112cd4beb3305a0cd27de919b62f3 Mon Sep 17 00:00:00 2001 From: Alon Albert Date: Tue, 20 Feb 2018 11:17:07 +0200 Subject: [PATCH 1/3] Add Remote RSS Support to Deluge RPC Adapter Also: * Make Remote RSS activity support "Swipe To Refresh" * Preserve scroll position on orientation changes. --- .../transdroid/core/gui/TorrentsActivity.java | 143 ++++++++---------- .../core/gui/remoterss/RemoteRssActivity.java | 138 +++++++++++------ .../core/gui/remoterss/RemoteRssFragment.java | 89 ++++++----- .../gui/remoterss/data/RemoteRssSupplier.java | 9 +- .../java/org/transdroid/daemon/Daemon.java | 2 +- .../daemon/Deluge/DelugeCommon.java | 8 + .../daemon/Deluge/DelugeRemoteRssChannel.java | 85 +++++++++++ .../daemon/Deluge/DelugeRemoteRssItem.java | 47 ++++++ .../daemon/Deluge/DelugeRpcAdapter.java | 123 ++++++++++++++- .../daemon/Utorrent/UtorrentAdapter.java | 40 +++-- .../main/res/layout/fragment_remoterss.xml | 23 +-- app/src/main/res/values/strings.xml | 1 + 12 files changed, 514 insertions(+), 194 deletions(-) create mode 100644 app/src/main/java/org/transdroid/daemon/Deluge/DelugeRemoteRssChannel.java create mode 100644 app/src/main/java/org/transdroid/daemon/Deluge/DelugeRemoteRssItem.java diff --git a/app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java b/app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java index 1ec83c81..8486daf1 100644 --- a/app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java +++ b/app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java @@ -16,31 +16,17 @@ */ package org.transdroid.core.gui; -import android.annotation.TargetApi; -import android.app.SearchManager; -import android.content.ContentResolver; -import android.content.Intent; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.view.MenuItemCompat; -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; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import com.getbase.floatingactionbutton.FloatingActionButton; 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.StatusType; 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.search.BarcodeHelper; 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.util.HttpHelper; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; +import android.annotation.TargetApi; +import android.app.SearchManager; +import android.content.ContentResolver; +import android.content.Intent; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.view.MenuItemCompat; +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) @@ -854,18 +852,11 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE } @OptionsItem(R.id.action_remoterss) + @Background protected void openRemoteRss() { - ArrayList rssFeedItems = ((RemoteRssSupplier) (currentConnection)).getRemoteRssChannels(); - - if (rssFeedItems.size() == 0) { - 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() - ; + // 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. + RemoteRssActivity_.intent(this).start(); } @OptionsItem(R.id.action_help) @@ -964,7 +955,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE if (result instanceof RetrieveTaskSuccessResult) { onTorrentsRetrieved(((RetrieveTaskSuccessResult) result).getTorrents(), ((RetrieveTaskSuccessResult) result).getLabels()); } 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) { onTorrentDetailsRetrieved(torrent, ((GetTorrentDetailsTaskSuccessResult) result).getTorrentDetails()); } 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) { onTorrentFilesRetrieved(torrent, ((GetFileListTaskSuccessResult) result).getFiles()); } 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) { onTurtleModeRetrieved(((GetStatsTaskSuccessResult) result).isAlternativeModeEnabled()); } 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 onTurtleModeRetrieved(enable); } 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)); refreshTorrents(); } 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 } catch (IllegalArgumentException e) { // Illegal character or escape sequence; fail task to show error - onCommunicationError(new DaemonTaskFailureResult(AddByMagnetUrlTask.create(currentConnection, url), - new DaemonException(DaemonException.ExceptionType.FileAccessError, "Invalid characters in magnet link")), false); + onCommunicationError(new DaemonException(DaemonException.ExceptionType.FileAccessError, + "Invalid characters in magnet link"), false); return; } AddByMagnetUrlTask addByMagnetUrlTask = AddByMagnetUrlTask.create(currentConnection, url); if (!Daemon.supportsAddByMagnetUrl(currentConnection.getType())) { // No support for magnet links: forcefully let the task fail to report the error - onCommunicationError(new DaemonTaskFailureResult(addByMagnetUrlTask, new DaemonException(DaemonException.ExceptionType.MethodUnsupported, - currentConnection.getType().name() + " does not support magnet links")), false); + onCommunicationError(new DaemonException(DaemonException.ExceptionType.MethodUnsupported, + currentConnection.getType().name() + " does not support magnet links"), false); return; } @@ -1074,7 +1065,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_added, title)); refreshTorrents(); } 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)); refreshTorrents(); } 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) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_resumed, torrent.getName())); } 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) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_paused, torrent.getName())); } 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) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_started, torrent.getName())); } 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) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_stopped, torrent.getName())); } else { - onCommunicationError((DaemonTaskFailureResult) result, false); + onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); } } @@ -1249,7 +1240,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE onTaskSucceeded((DaemonTaskSuccessResult) result, getString(withData ? R.string.result_removed_with_data : R.string.result_removed, torrent.getName())); } else { - onCommunicationError((DaemonTaskFailureResult) result, false); + onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); } } @@ -1262,7 +1253,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE onTaskSucceeded((DaemonTaskSuccessResult) result, newLabel == null ? getString(R.string.result_labelremoved) : getString(R.string.result_labelset, newLabel)); } 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) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_recheckedstarted, torrent.getName())); } 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) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_trackersupdated)); } 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) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_locationset, newLocation)); } 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) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_priotitiesset)); } 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) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_maxspeedsset)); } else { - onCommunicationError((DaemonTaskFailureResult) result, false); + onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); } } @@ -1329,10 +1320,10 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE } @UiThread - protected void onCommunicationError(DaemonTaskFailureResult result, boolean isCritical) { + protected void onCommunicationError(DaemonException daemonException, boolean isCritical) { //noinspection ThrowableResultOfMethodCallIgnored - log.i(this, result.getException().toString()); - String error = getString(LocalTorrent.getResourceForDaemonException(result.getException())); + 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)); fragmentTorrents.updateIsLoading(false); if (isCritical) { @@ -1407,4 +1398,4 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE invalidateOptionsMenu(); } -} +} \ No newline at end of file diff --git a/app/src/main/java/org/transdroid/core/gui/remoterss/RemoteRssActivity.java b/app/src/main/java/org/transdroid/core/gui/remoterss/RemoteRssActivity.java index a67f138d..7eaaad7b 100644 --- a/app/src/main/java/org/transdroid/core/gui/remoterss/RemoteRssActivity.java +++ b/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.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.Background; import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EActivity; import org.androidannotations.annotations.FragmentById; import org.androidannotations.annotations.InstanceState; import org.androidannotations.annotations.ItemClick; +import org.androidannotations.annotations.NonConfigurationInstance; import org.androidannotations.annotations.OptionsItem; +import org.androidannotations.annotations.UiThread; import org.androidannotations.annotations.ViewById; import org.transdroid.R; import org.transdroid.core.app.settings.ApplicationSettings; import org.transdroid.core.app.settings.ServerSetting; 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.log.Log; +import org.transdroid.core.gui.navigation.RefreshableActivity; 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.service.ConnectivityHelper; +import org.transdroid.daemon.DaemonException; import org.transdroid.daemon.IDaemonAdapter; import java.util.ArrayList; @@ -61,18 +72,22 @@ import java.util.List; * @author Twig Nguyen */ @EActivity(R.layout.activity_remoterss) -public class RemoteRssActivity extends AppCompatActivity { -// @Extra - @InstanceState +public class RemoteRssActivity extends AppCompatActivity implements RefreshableActivity { + @NonConfigurationInstance protected ArrayList feeds; @InstanceState + protected int selectedFilter; + + @NonConfigurationInstance protected ArrayList recentItems; // Server connection @Bean protected ApplicationSettings applicationSettings; @Bean + protected Log log; + @Bean protected ConnectivityHelper connectivityHelper; private IDaemonAdapter currentConnection; @@ -110,13 +125,67 @@ public class RemoteRssActivity extends AppCompatActivity { // Connect to the last used server ServerSetting lastUsed = applicationSettings.getLastUsedServer(); 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() { + @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(); + onFeedSelected(selectedFilter); + } - // Show all items - showRecentItems(); + @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)); } @@ -139,36 +208,7 @@ public class RemoteRssActivity extends AppCompatActivity { } } - protected void showRecentItems() { - 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() { - @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() { + private void showChannelFilters() { List feedLabels = new ArrayList<>(feeds.size() +1); feedLabels.add(new RemoteRssChannel() { @Override @@ -187,12 +227,8 @@ public class RemoteRssActivity extends AppCompatActivity { @ItemClick(R.id.drawer_list) protected void onFeedSelected(int position) { - if (position == 0) { - showRecentItems(); - } - else { - fragmentRemoteRss.updateRemoteItems(feeds.get(position -1).getItems()); - } + selectedFilter = position; + fragmentRemoteRss.updateRemoteItems(position == 0 ? recentItems : feeds.get(position - 1).getItems(), true); RemoteRssChannel channel = (RemoteRssChannel) drawerList.getAdapter().getItem(position); getSupportActionBar().setSubtitle(channel.getName()); @@ -203,4 +239,18 @@ public class RemoteRssActivity extends AppCompatActivity { public IDaemonAdapter getCurrentConnection() { 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(); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/transdroid/core/gui/remoterss/RemoteRssFragment.java b/app/src/main/java/org/transdroid/core/gui/remoterss/RemoteRssFragment.java index 7f4e0378..c86a9f08 100644 --- a/app/src/main/java/org/transdroid/core/gui/remoterss/RemoteRssFragment.java +++ b/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.Bean; import org.androidannotations.annotations.EFragment; -import org.androidannotations.annotations.InstanceState; import org.androidannotations.annotations.ItemClick; import org.androidannotations.annotations.UiThread; import org.androidannotations.annotations.ViewById; import org.transdroid.R; import org.transdroid.core.gui.lists.LocalTorrent; 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.daemon.Daemon; -import org.transdroid.daemon.IDaemonAdapter; -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.core.gui.remoterss.data.RemoteRssSupplier; +import org.transdroid.daemon.DaemonException; import org.transdroid.daemon.task.DaemonTaskSuccessResult; import java.util.ArrayList; @@ -62,7 +58,6 @@ public class RemoteRssFragment extends Fragment { protected Log log; // Local data - @InstanceState protected ArrayList remoteRssItems; // Views @@ -75,7 +70,7 @@ public class RemoteRssFragment extends Fragment { @ViewById protected ListView torrentsList; @ViewById - protected TextView remoterssNoFilesMessage; + protected TextView remoterssStatusMessage; protected RemoteRssItemsAdapter adapter; @@ -100,27 +95,45 @@ public class RemoteRssFragment extends Fragment { torrentsList.setAdapter(adapter); torrentsList.setFastScrollEnabled(true); - // Restore the fragment state (on orientation changes et al.) - if (remoteRssItems != null) { - updateRemoteItems(remoteRssItems); + // Allow pulls on the list view to refresh the torrents + if (getActivity() != null && getActivity() instanceof RefreshableActivity) { + swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + ((RefreshableActivity) getActivity()).refreshScreen(); + } + }); } } /** * Updates the UI with a new list of RSS items. */ - public void updateRemoteItems(List remoteItems) { + public void updateRemoteItems(List remoteItems, boolean scrollToTop) { remoteRssItems = new ArrayList<>(remoteItems); adapter.updateItems(remoteRssItems); - torrentsList.smoothScrollToPosition(0); - + if (scrollToTop) { + torrentsList.smoothScrollToPosition(0); + } // 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. - */ + */ @ItemClick(resName = "torrents_list") protected void detailsListClicked(int 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. - */ + */ @Background protected void downloadRemoteRssItem(RemoteRssItem item) { - RemoteRssActivity activity = (RemoteRssActivity) getActivity(); - IDaemonAdapter currentConnection = activity.getCurrentConnection(); - DaemonTaskResult result; - - if (item.isMagnetLink()) { - // Check if it's supported - if (!Daemon.supportsAddByMagnetUrl(currentConnection.getType())) { - onTaskFailed(getString(R.string.error_magnet_links_unsupported)); - 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); + final RemoteRssActivity activity = (RemoteRssActivity) getActivity(); + final RemoteRssSupplier supplier = (RemoteRssSupplier) activity.getCurrentConnection(); + + try { + supplier.downloadRemoteRssItem(log, item, activity.getChannel(item.getSourceName())); + onTaskSucceeded(null, getString(R.string.result_added, item.getTitle())); + } catch (DaemonException e) { + onTaskFailed(getString(LocalTorrent.getResourceForDaemonException(e))); } } @@ -167,9 +164,9 @@ public class RemoteRssFragment extends Fragment { @UiThread protected void onTaskFailed(String message) { SnackbarManager.show(Snackbar.with(getActivity()) - .text(message) - .colorResource(R.color.red) - .type(SnackbarType.MULTI_LINE) + .text(message) + .colorResource(R.color.red) + .type(SnackbarType.MULTI_LINE) ); } -} +} \ No newline at end of file diff --git a/app/src/main/java/org/transdroid/core/gui/remoterss/data/RemoteRssSupplier.java b/app/src/main/java/org/transdroid/core/gui/remoterss/data/RemoteRssSupplier.java index 8e74c318..93b1d329 100644 --- a/app/src/main/java/org/transdroid/core/gui/remoterss/data/RemoteRssSupplier.java +++ b/app/src/main/java/org/transdroid/core/gui/remoterss/data/RemoteRssSupplier.java @@ -1,5 +1,8 @@ package org.transdroid.core.gui.remoterss.data; +import org.transdroid.core.gui.log.Log; +import org.transdroid.daemon.DaemonException; + import java.util.ArrayList; /** @@ -8,5 +11,7 @@ import java.util.ArrayList; * @author Twig */ public interface RemoteRssSupplier { - ArrayList getRemoteRssChannels(); -} + ArrayList getRemoteRssChannels(Log log) throws DaemonException; + + void downloadRemoteRssItem(Log log, RemoteRssItem rssItem, RemoteRssChannel rssChannel) throws DaemonException; +} \ No newline at end of file diff --git a/app/src/main/java/org/transdroid/daemon/Daemon.java b/app/src/main/java/org/transdroid/daemon/Daemon.java index 70e538bc..8c98d2a0 100644 --- a/app/src/main/java/org/transdroid/daemon/Daemon.java +++ b/app/src/main/java/org/transdroid/daemon/Daemon.java @@ -401,6 +401,6 @@ public enum Daemon { } public static boolean supportsRemoteRssManagement(Daemon type) { - return type == uTorrent; + return type == uTorrent || type == DelugeRpc; } } diff --git a/app/src/main/java/org/transdroid/daemon/Deluge/DelugeCommon.java b/app/src/main/java/org/transdroid/daemon/Deluge/DelugeCommon.java index e5ab61a8..dba9a719 100644 --- a/app/src/main/java/org/transdroid/daemon/Deluge/DelugeCommon.java +++ b/app/src/main/java/org/transdroid/daemon/Deluge/DelugeCommon.java @@ -29,6 +29,7 @@ import org.transdroid.daemon.TorrentStatus; class DelugeCommon { 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_ETA = "eta"; static final String RPC_FILE = "file"; @@ -37,6 +38,7 @@ class DelugeCommon { static final String RPC_HASH = "hash"; static final String RPC_INDEX = "index"; static final String RPC_LABEL = "label"; + static final String RPC_KEY = "key"; static final String RPC_MAXDOWNLOAD = "max_download_speed"; static final String RPC_MAXUPLOAD = "max_upload_speed"; 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_SET_TORRENT_OPTIONS = "core.set_torrent_options"; 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_NUMPEERS = "num_peers"; 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_RATEUPLOAD = "upload_payload_rate"; 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_SESSION_ID = "_session_id"; static final String RPC_SIZE = "size"; static final String RPC_STATUS = "state"; + static final String RPC_SUBSCRIPTIONS = "subscriptions"; static final String RPC_TIMEADDED = "time_added"; static final String RPC_TORRENTS = "torrents"; static final String RPC_TOTALPEERS = "total_peers"; diff --git a/app/src/main/java/org/transdroid/daemon/Deluge/DelugeRemoteRssChannel.java b/app/src/main/java/org/transdroid/daemon/Deluge/DelugeRemoteRssChannel.java new file mode 100644 index 00000000..d072dfff --- /dev/null +++ b/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 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 CREATOR = new Parcelable.Creator() { + public DelugeRemoteRssChannel createFromParcel(Parcel in) { + return new DelugeRemoteRssChannel(in); + } + public DelugeRemoteRssChannel[] newArray(int size) { + return new DelugeRemoteRssChannel[size]; + } + }; +} \ No newline at end of file diff --git a/app/src/main/java/org/transdroid/daemon/Deluge/DelugeRemoteRssItem.java b/app/src/main/java/org/transdroid/daemon/Deluge/DelugeRemoteRssItem.java new file mode 100644 index 00000000..2e30d8e2 --- /dev/null +++ b/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 CREATOR = new Parcelable.Creator() { + public DelugeRemoteRssItem createFromParcel(Parcel in) { + return new DelugeRemoteRssItem(in); + } + + public DelugeRemoteRssItem[] newArray(int size) { + return new DelugeRemoteRssItem[size]; + } + }; +} \ No newline at end of file diff --git a/app/src/main/java/org/transdroid/daemon/Deluge/DelugeRpcAdapter.java b/app/src/main/java/org/transdroid/daemon/Deluge/DelugeRpcAdapter.java index 79a22f32..3005ee3f 100644 --- a/app/src/main/java/org/transdroid/daemon/Deluge/DelugeRpcAdapter.java +++ b/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.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.DaemonException; 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.SetTrackersTask; import org.transdroid.daemon.task.SetTransferRatesTask; +import org.xml.sax.SAXException; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; @@ -70,9 +77,12 @@ import java.util.Map; import java.util.Map.Entry; 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_FIELDS_ARRAY; 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_FIELDS_ARRAY; 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_HASH; 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_MAXDOWNLOAD; 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_GET_LABELS; 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_INFO; 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_SET_TORRENT_OPTIONS; 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_NUMPEERS; 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_RATEDOWNLOAD; 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_SIZE; 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_TIMEADDED; 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. * @author alon.albert */ -public class DelugeRpcAdapter implements IDaemonAdapter { +public class DelugeRpcAdapter implements IDaemonAdapter, RemoteRssSupplier { public static final int DEFAULT_PORT = 58846; @@ -200,6 +217,93 @@ public class DelugeRpcAdapter implements IDaemonAdapter { return settings; } + @Override + public ArrayList 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 rssConfig = (Map) client.sendRequest(RPC_METHOD_GET_RSS_CONFIG); + + //noinspection unchecked + final Map> rssFeeds = (Map>) rssConfig.get(RPC_RSSFEEDS); + + final Map feedUrlMap = new HashMap<>(); + final Map> feedItemMap = new HashMap<>(); + for (Map feed : rssFeeds.values()) { + final String feedUrl = (String) feed.get(RPC_URL); + final Object key = feed.get(RPC_KEY); + feedUrlMap.put(key, feedUrl); + final List items = getRssFeedItems(feedUrl, log); + feedItemMap.put(key, items); + } + + //noinspection unchecked + final Map> subscriptions = (Map>) rssConfig.get(RPC_SUBSCRIPTIONS); + final ArrayList channels = new ArrayList<>(); + for (Map 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 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 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 private RetrieveTaskSuccessResult doRetrieve(DelugeRpcClient client, RetrieveTask task) throws DaemonException { // Get torrents @@ -498,6 +602,23 @@ public class DelugeRpcAdapter implements IDaemonAdapter { return new String[]{task.getTargetTorrent().getUniqueID()}; } + @NonNull + private List 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 { //noinspection unchecked final List methods = (List) client.sendRequest(RPC_METHOD_GET_METHOD_LIST); diff --git a/app/src/main/java/org/transdroid/daemon/Utorrent/UtorrentAdapter.java b/app/src/main/java/org/transdroid/daemon/Utorrent/UtorrentAdapter.java index 171e50d4..82edd426 100644 --- a/app/src/main/java/org/transdroid/daemon/Utorrent/UtorrentAdapter.java +++ b/app/src/main/java/org/transdroid/daemon/Utorrent/UtorrentAdapter.java @@ -17,6 +17,19 @@ */ 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.MultipartEntity; import com.android.internalcopy.http.multipart.Part; @@ -30,6 +43,7 @@ import org.json.JSONException; import org.json.JSONObject; 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.daemon.Daemon; 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.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 * HTTP GET requests and responses. @@ -657,7 +658,18 @@ public class UtorrentAdapter implements IDaemonAdapter, RemoteRssSupplier { return this.settings; } - public ArrayList getRemoteRssChannels() { + public ArrayList getRemoteRssChannels(Log log) { 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); + } + } + } diff --git a/app/src/main/res/layout/fragment_remoterss.xml b/app/src/main/res/layout/fragment_remoterss.xml index b786a4f5..3a5c9c3b 100644 --- a/app/src/main/res/layout/fragment_remoterss.xml +++ b/app/src/main/res/layout/fragment_remoterss.xml @@ -23,17 +23,20 @@ + android:id="@+id/remoterss_status_message"/> - - + android:layout_height="match_parent"> + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 795b7bbf..262fb594 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -214,6 +214,7 @@ (All recent) No torrent files found.\n\nAre your RSS feeds configured correctly? + Loading RSS feeds… Loading… Open Transdroid From 4a64c32113b5886e04150956b36677a6d912dc33 Mon Sep 17 00:00:00 2001 From: Alon Albert Date: Tue, 20 Feb 2018 11:25:50 +0200 Subject: [PATCH 2/3] Revert some unnecessary changes --- .../transdroid/core/gui/TorrentsActivity.java | 126 +++++++++--------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java b/app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java index 8486daf1..8f45b733 100644 --- a/app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java +++ b/app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java @@ -16,17 +16,31 @@ */ package org.transdroid.core.gui; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; +import android.annotation.TargetApi; +import android.app.SearchManager; +import android.content.ContentResolver; +import android.content.Intent; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.view.MenuItemCompat; +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; import com.getbase.floatingactionbutton.FloatingActionButton; import com.getbase.floatingactionbutton.FloatingActionsMenu; @@ -114,31 +128,17 @@ import org.transdroid.daemon.task.StartTask; import org.transdroid.daemon.task.StopTask; import org.transdroid.daemon.util.HttpHelper; -import android.annotation.TargetApi; -import android.app.SearchManager; -import android.content.ContentResolver; -import android.content.Intent; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.view.MenuItemCompat; -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; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; /** * 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) @@ -955,7 +955,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE if (result instanceof RetrieveTaskSuccessResult) { onTorrentsRetrieved(((RetrieveTaskSuccessResult) result).getTorrents(), ((RetrieveTaskSuccessResult) result).getLabels()); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), true); + onCommunicationError((DaemonTaskFailureResult) result, true); } } @@ -973,7 +973,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE if (result instanceof GetTorrentDetailsTaskSuccessResult) { onTorrentDetailsRetrieved(torrent, ((GetTorrentDetailsTaskSuccessResult) result).getTorrentDetails()); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -991,7 +991,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE if (result instanceof GetFileListTaskSuccessResult) { onTorrentFilesRetrieved(torrent, ((GetFileListTaskSuccessResult) result).getFiles()); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -1006,7 +1006,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE if (result instanceof GetStatsTaskSuccessResult) { onTurtleModeRetrieved(((GetStatsTaskSuccessResult) result).isAlternativeModeEnabled()); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -1022,7 +1022,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE // Success; no need to retrieve it again - just update the visual indicator onTurtleModeRetrieved(enable); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -1033,7 +1033,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_added, title)); refreshTorrents(); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -1047,16 +1047,16 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE // Ignore: UTF-8 is always available on Android devices } catch (IllegalArgumentException e) { // Illegal character or escape sequence; fail task to show error - onCommunicationError(new DaemonException(DaemonException.ExceptionType.FileAccessError, - "Invalid characters in magnet link"), false); + onCommunicationError(new DaemonTaskFailureResult(AddByMagnetUrlTask.create(currentConnection, url), + new DaemonException(DaemonException.ExceptionType.FileAccessError, "Invalid characters in magnet link")), false); return; } AddByMagnetUrlTask addByMagnetUrlTask = AddByMagnetUrlTask.create(currentConnection, url); if (!Daemon.supportsAddByMagnetUrl(currentConnection.getType())) { // No support for magnet links: forcefully let the task fail to report the error - onCommunicationError(new DaemonException(DaemonException.ExceptionType.MethodUnsupported, - currentConnection.getType().name() + " does not support magnet links"), false); + onCommunicationError(new DaemonTaskFailureResult(addByMagnetUrlTask, new DaemonException(DaemonException.ExceptionType.MethodUnsupported, + currentConnection.getType().name() + " does not support magnet links")), false); return; } @@ -1065,7 +1065,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_added, title)); refreshTorrents(); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -1083,7 +1083,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_added, title)); refreshTorrents(); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -1192,7 +1192,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE if (result instanceof DaemonTaskSuccessResult) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_resumed, torrent.getName())); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -1204,7 +1204,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE if (result instanceof DaemonTaskSuccessResult) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_paused, torrent.getName())); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -1216,7 +1216,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE if (result instanceof DaemonTaskSuccessResult) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_started, torrent.getName())); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -1228,7 +1228,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE if (result instanceof DaemonTaskSuccessResult) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_stopped, torrent.getName())); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -1240,7 +1240,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE onTaskSucceeded((DaemonTaskSuccessResult) result, getString(withData ? R.string.result_removed_with_data : R.string.result_removed, torrent.getName())); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -1253,7 +1253,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE onTaskSucceeded((DaemonTaskSuccessResult) result, newLabel == null ? getString(R.string.result_labelremoved) : getString(R.string.result_labelset, newLabel)); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -1265,7 +1265,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE if (result instanceof DaemonTaskSuccessResult) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_recheckedstarted, torrent.getName())); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -1276,7 +1276,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE if (result instanceof DaemonTaskSuccessResult) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_trackersupdated)); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -1287,7 +1287,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE if (result instanceof DaemonTaskSuccessResult) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_locationset, newLocation)); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -1298,7 +1298,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE if (result instanceof DaemonTaskSuccessResult) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_priotitiesset)); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -1308,7 +1308,7 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE if (result instanceof DaemonTaskSuccessResult) { onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_maxspeedsset)); } else { - onCommunicationError(((DaemonTaskFailureResult) result).getException(), false); + onCommunicationError((DaemonTaskFailureResult) result, false); } } @@ -1320,10 +1320,10 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE } @UiThread - protected void onCommunicationError(DaemonException daemonException, boolean isCritical) { + protected void onCommunicationError(DaemonTaskFailureResult result, boolean isCritical) { //noinspection ThrowableResultOfMethodCallIgnored - log.i(this, daemonException.toString()); - String error = getString(LocalTorrent.getResourceForDaemonException(daemonException)); + log.i(this, result.getException().toString()); + String error = getString(LocalTorrent.getResourceForDaemonException(result.getException())); SnackbarManager.show(Snackbar.with(this).text(error).colorResource(R.color.red).type(SnackbarType.MULTI_LINE)); fragmentTorrents.updateIsLoading(false); if (isCritical) { @@ -1398,4 +1398,4 @@ public class TorrentsActivity extends AppCompatActivity implements TorrentTasksE invalidateOptionsMenu(); } -} \ No newline at end of file +} From 1876fe89cf216bd76136a8be6dcb64181206ef96 Mon Sep 17 00:00:00 2001 From: Alon Albert Date: Tue, 20 Feb 2018 11:29:40 +0200 Subject: [PATCH 3/3] Revert some unnecessary changes --- .../daemon/Utorrent/UtorrentAdapter.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/transdroid/daemon/Utorrent/UtorrentAdapter.java b/app/src/main/java/org/transdroid/daemon/Utorrent/UtorrentAdapter.java index 82edd426..5f2e7b7e 100644 --- a/app/src/main/java/org/transdroid/daemon/Utorrent/UtorrentAdapter.java +++ b/app/src/main/java/org/transdroid/daemon/Utorrent/UtorrentAdapter.java @@ -17,19 +17,6 @@ */ 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.MultipartEntity; import com.android.internalcopy.http.multipart.Part; @@ -78,6 +65,19 @@ import org.transdroid.daemon.task.SetTransferRatesTask; import org.transdroid.daemon.task.StartTask; 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 * HTTP GET requests and responses.